xref: /minix/external/bsd/bind/dist/lib/dns/dispatch.c (revision 00b67f09)
1*00b67f09SDavid van Moolenbroek /*	$NetBSD: dispatch.c,v 1.10 2015/07/08 17:28:58 christos Exp $	*/
2*00b67f09SDavid van Moolenbroek 
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek  * Copyright (C) 2004-2009, 2011-2015  Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek  * Copyright (C) 1999-2003  Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek  *
7*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek  *
11*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek  * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek  */
19*00b67f09SDavid van Moolenbroek 
20*00b67f09SDavid van Moolenbroek /* Id: dispatch.c,v 1.175 2011/11/29 01:03:47 marka Exp  */
21*00b67f09SDavid van Moolenbroek 
22*00b67f09SDavid van Moolenbroek /*! \file */
23*00b67f09SDavid van Moolenbroek 
24*00b67f09SDavid van Moolenbroek #include <config.h>
25*00b67f09SDavid van Moolenbroek 
26*00b67f09SDavid van Moolenbroek #include <stdlib.h>
27*00b67f09SDavid van Moolenbroek #include <sys/types.h>
28*00b67f09SDavid van Moolenbroek #include <unistd.h>
29*00b67f09SDavid van Moolenbroek #include <stdlib.h>
30*00b67f09SDavid van Moolenbroek 
31*00b67f09SDavid van Moolenbroek #include <isc/entropy.h>
32*00b67f09SDavid van Moolenbroek #include <isc/mem.h>
33*00b67f09SDavid van Moolenbroek #include <isc/mutex.h>
34*00b67f09SDavid van Moolenbroek #include <isc/portset.h>
35*00b67f09SDavid van Moolenbroek #include <isc/print.h>
36*00b67f09SDavid van Moolenbroek #include <isc/random.h>
37*00b67f09SDavid van Moolenbroek #include <isc/socket.h>
38*00b67f09SDavid van Moolenbroek #include <isc/stats.h>
39*00b67f09SDavid van Moolenbroek #include <isc/string.h>
40*00b67f09SDavid van Moolenbroek #include <isc/task.h>
41*00b67f09SDavid van Moolenbroek #include <isc/time.h>
42*00b67f09SDavid van Moolenbroek #include <isc/util.h>
43*00b67f09SDavid van Moolenbroek 
44*00b67f09SDavid van Moolenbroek #include <dns/acl.h>
45*00b67f09SDavid van Moolenbroek #include <dns/dispatch.h>
46*00b67f09SDavid van Moolenbroek #include <dns/events.h>
47*00b67f09SDavid van Moolenbroek #include <dns/log.h>
48*00b67f09SDavid van Moolenbroek #include <dns/message.h>
49*00b67f09SDavid van Moolenbroek #include <dns/portlist.h>
50*00b67f09SDavid van Moolenbroek #include <dns/stats.h>
51*00b67f09SDavid van Moolenbroek #include <dns/tcpmsg.h>
52*00b67f09SDavid van Moolenbroek #include <dns/types.h>
53*00b67f09SDavid van Moolenbroek 
54*00b67f09SDavid van Moolenbroek typedef ISC_LIST(dns_dispentry_t)	dns_displist_t;
55*00b67f09SDavid van Moolenbroek 
56*00b67f09SDavid van Moolenbroek typedef struct dispsocket		dispsocket_t;
57*00b67f09SDavid van Moolenbroek typedef ISC_LIST(dispsocket_t)		dispsocketlist_t;
58*00b67f09SDavid van Moolenbroek 
59*00b67f09SDavid van Moolenbroek typedef struct dispportentry		dispportentry_t;
60*00b67f09SDavid van Moolenbroek typedef ISC_LIST(dispportentry_t)	dispportlist_t;
61*00b67f09SDavid van Moolenbroek 
62*00b67f09SDavid van Moolenbroek /* ARC4 Random generator state */
63*00b67f09SDavid van Moolenbroek typedef struct arc4ctx {
64*00b67f09SDavid van Moolenbroek 	isc_uint8_t	i;
65*00b67f09SDavid van Moolenbroek 	isc_uint8_t	j;
66*00b67f09SDavid van Moolenbroek 	isc_uint8_t	s[256];
67*00b67f09SDavid van Moolenbroek 	int		count;
68*00b67f09SDavid van Moolenbroek 	isc_entropy_t	*entropy;	/*%< entropy source for ARC4 */
69*00b67f09SDavid van Moolenbroek 	isc_mutex_t	*lock;
70*00b67f09SDavid van Moolenbroek } arc4ctx_t;
71*00b67f09SDavid van Moolenbroek 
72*00b67f09SDavid van Moolenbroek typedef struct dns_qid {
73*00b67f09SDavid van Moolenbroek 	unsigned int	magic;
74*00b67f09SDavid van Moolenbroek 	unsigned int	qid_nbuckets;	/*%< hash table size */
75*00b67f09SDavid van Moolenbroek 	unsigned int	qid_increment;	/*%< id increment on collision */
76*00b67f09SDavid van Moolenbroek 	isc_mutex_t	lock;
77*00b67f09SDavid van Moolenbroek 	dns_displist_t	*qid_table;	/*%< the table itself */
78*00b67f09SDavid van Moolenbroek 	dispsocketlist_t *sock_table;	/*%< socket table */
79*00b67f09SDavid van Moolenbroek } dns_qid_t;
80*00b67f09SDavid van Moolenbroek 
81*00b67f09SDavid van Moolenbroek struct dns_dispatchmgr {
82*00b67f09SDavid van Moolenbroek 	/* Unlocked. */
83*00b67f09SDavid van Moolenbroek 	unsigned int			magic;
84*00b67f09SDavid van Moolenbroek 	isc_mem_t		       *mctx;
85*00b67f09SDavid van Moolenbroek 	dns_acl_t		       *blackhole;
86*00b67f09SDavid van Moolenbroek 	dns_portlist_t		       *portlist;
87*00b67f09SDavid van Moolenbroek 	isc_stats_t		       *stats;
88*00b67f09SDavid van Moolenbroek 	isc_entropy_t		       *entropy; /*%< entropy source */
89*00b67f09SDavid van Moolenbroek 
90*00b67f09SDavid van Moolenbroek 	/* Locked by "lock". */
91*00b67f09SDavid van Moolenbroek 	isc_mutex_t			lock;
92*00b67f09SDavid van Moolenbroek 	unsigned int			state;
93*00b67f09SDavid van Moolenbroek 	ISC_LIST(dns_dispatch_t)	list;
94*00b67f09SDavid van Moolenbroek 
95*00b67f09SDavid van Moolenbroek 	/* Locked by arc4_lock. */
96*00b67f09SDavid van Moolenbroek 	isc_mutex_t			arc4_lock;
97*00b67f09SDavid van Moolenbroek 	arc4ctx_t			arc4ctx;    /*%< ARC4 context for QID */
98*00b67f09SDavid van Moolenbroek 
99*00b67f09SDavid van Moolenbroek 	/* locked by buffer lock */
100*00b67f09SDavid van Moolenbroek 	dns_qid_t			*qid;
101*00b67f09SDavid van Moolenbroek 	isc_mutex_t			buffer_lock;
102*00b67f09SDavid van Moolenbroek 	unsigned int			buffers;    /*%< allocated buffers */
103*00b67f09SDavid van Moolenbroek 	unsigned int			buffersize; /*%< size of each buffer */
104*00b67f09SDavid van Moolenbroek 	unsigned int			maxbuffers; /*%< max buffers */
105*00b67f09SDavid van Moolenbroek 
106*00b67f09SDavid van Moolenbroek 	/* Locked internally. */
107*00b67f09SDavid van Moolenbroek 	isc_mutex_t			depool_lock;
108*00b67f09SDavid van Moolenbroek 	isc_mempool_t		       *depool;	/*%< pool for dispatch events */
109*00b67f09SDavid van Moolenbroek 	isc_mutex_t			rpool_lock;
110*00b67f09SDavid van Moolenbroek 	isc_mempool_t		       *rpool;	/*%< pool for replies */
111*00b67f09SDavid van Moolenbroek 	isc_mutex_t			dpool_lock;
112*00b67f09SDavid van Moolenbroek 	isc_mempool_t		       *dpool;  /*%< dispatch allocations */
113*00b67f09SDavid van Moolenbroek 	isc_mutex_t			bpool_lock;
114*00b67f09SDavid van Moolenbroek 	isc_mempool_t		       *bpool;	/*%< pool for buffers */
115*00b67f09SDavid van Moolenbroek 	isc_mutex_t			spool_lock;
116*00b67f09SDavid van Moolenbroek 	isc_mempool_t		       *spool;	/*%< pool for dispsocks */
117*00b67f09SDavid van Moolenbroek 
118*00b67f09SDavid van Moolenbroek 	/*%
119*00b67f09SDavid van Moolenbroek 	 * Locked by qid->lock if qid exists; otherwise, can be used without
120*00b67f09SDavid van Moolenbroek 	 * being locked.
121*00b67f09SDavid van Moolenbroek 	 * Memory footprint considerations: this is a simple implementation of
122*00b67f09SDavid van Moolenbroek 	 * available ports, i.e., an ordered array of the actual port numbers.
123*00b67f09SDavid van Moolenbroek 	 * This will require about 256KB of memory in the worst case (128KB for
124*00b67f09SDavid van Moolenbroek 	 * each of IPv4 and IPv6).  We could reduce it by representing it as a
125*00b67f09SDavid van Moolenbroek 	 * more sophisticated way such as a list (or array) of ranges that are
126*00b67f09SDavid van Moolenbroek 	 * searched to identify a specific port.  Our decision here is the saved
127*00b67f09SDavid van Moolenbroek 	 * memory isn't worth the implementation complexity, considering the
128*00b67f09SDavid van Moolenbroek 	 * fact that the whole BIND9 process (which is mainly named) already
129*00b67f09SDavid van Moolenbroek 	 * requires a pretty large memory footprint.  We may, however, have to
130*00b67f09SDavid van Moolenbroek 	 * revisit the decision when we want to use it as a separate module for
131*00b67f09SDavid van Moolenbroek 	 * an environment where memory requirement is severer.
132*00b67f09SDavid van Moolenbroek 	 */
133*00b67f09SDavid van Moolenbroek 	in_port_t	*v4ports;	/*%< available ports for IPv4 */
134*00b67f09SDavid van Moolenbroek 	unsigned int	nv4ports;	/*%< # of available ports for IPv4 */
135*00b67f09SDavid van Moolenbroek 	in_port_t	*v6ports;	/*%< available ports for IPv4 */
136*00b67f09SDavid van Moolenbroek 	unsigned int	nv6ports;	/*%< # of available ports for IPv4 */
137*00b67f09SDavid van Moolenbroek };
138*00b67f09SDavid van Moolenbroek 
139*00b67f09SDavid van Moolenbroek #define MGR_SHUTTINGDOWN		0x00000001U
140*00b67f09SDavid van Moolenbroek #define MGR_IS_SHUTTINGDOWN(l)	(((l)->state & MGR_SHUTTINGDOWN) != 0)
141*00b67f09SDavid van Moolenbroek 
142*00b67f09SDavid van Moolenbroek #define IS_PRIVATE(d)	(((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0)
143*00b67f09SDavid van Moolenbroek 
144*00b67f09SDavid van Moolenbroek struct dns_dispentry {
145*00b67f09SDavid van Moolenbroek 	unsigned int			magic;
146*00b67f09SDavid van Moolenbroek 	dns_dispatch_t		       *disp;
147*00b67f09SDavid van Moolenbroek 	dns_messageid_t			id;
148*00b67f09SDavid van Moolenbroek 	in_port_t			port;
149*00b67f09SDavid van Moolenbroek 	unsigned int			bucket;
150*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t			host;
151*00b67f09SDavid van Moolenbroek 	isc_task_t		       *task;
152*00b67f09SDavid van Moolenbroek 	isc_taskaction_t		action;
153*00b67f09SDavid van Moolenbroek 	void			       *arg;
154*00b67f09SDavid van Moolenbroek 	isc_boolean_t			item_out;
155*00b67f09SDavid van Moolenbroek 	dispsocket_t			*dispsocket;
156*00b67f09SDavid van Moolenbroek 	ISC_LIST(dns_dispatchevent_t)	items;
157*00b67f09SDavid van Moolenbroek 	ISC_LINK(dns_dispentry_t)	link;
158*00b67f09SDavid van Moolenbroek };
159*00b67f09SDavid van Moolenbroek 
160*00b67f09SDavid van Moolenbroek /*%
161*00b67f09SDavid van Moolenbroek  * Maximum number of dispatch sockets that can be pooled for reuse.  The
162*00b67f09SDavid van Moolenbroek  * appropriate value may vary, but experiments have shown a busy caching server
163*00b67f09SDavid van Moolenbroek  * may need more than 1000 sockets concurrently opened.  The maximum allowable
164*00b67f09SDavid van Moolenbroek  * number of dispatch sockets (per manager) will be set to the double of this
165*00b67f09SDavid van Moolenbroek  * value.
166*00b67f09SDavid van Moolenbroek  */
167*00b67f09SDavid van Moolenbroek #ifndef DNS_DISPATCH_POOLSOCKS
168*00b67f09SDavid van Moolenbroek #define DNS_DISPATCH_POOLSOCKS			2048
169*00b67f09SDavid van Moolenbroek #endif
170*00b67f09SDavid van Moolenbroek 
171*00b67f09SDavid van Moolenbroek /*%
172*00b67f09SDavid van Moolenbroek  * Quota to control the number of dispatch sockets.  If a dispatch has more
173*00b67f09SDavid van Moolenbroek  * than the quota of sockets, new queries will purge oldest ones, so that
174*00b67f09SDavid van Moolenbroek  * a massive number of outstanding queries won't prevent subsequent queries
175*00b67f09SDavid van Moolenbroek  * (especially if the older ones take longer time and result in timeout).
176*00b67f09SDavid van Moolenbroek  */
177*00b67f09SDavid van Moolenbroek #ifndef DNS_DISPATCH_SOCKSQUOTA
178*00b67f09SDavid van Moolenbroek #define DNS_DISPATCH_SOCKSQUOTA			3072
179*00b67f09SDavid van Moolenbroek #endif
180*00b67f09SDavid van Moolenbroek 
181*00b67f09SDavid van Moolenbroek struct dispsocket {
182*00b67f09SDavid van Moolenbroek 	unsigned int			magic;
183*00b67f09SDavid van Moolenbroek 	isc_socket_t			*socket;
184*00b67f09SDavid van Moolenbroek 	dns_dispatch_t			*disp;
185*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t			host;
186*00b67f09SDavid van Moolenbroek 	in_port_t			localport; /* XXX: should be removed later */
187*00b67f09SDavid van Moolenbroek 	dispportentry_t			*portentry;
188*00b67f09SDavid van Moolenbroek 	dns_dispentry_t			*resp;
189*00b67f09SDavid van Moolenbroek 	isc_task_t			*task;
190*00b67f09SDavid van Moolenbroek 	ISC_LINK(dispsocket_t)		link;
191*00b67f09SDavid van Moolenbroek 	unsigned int			bucket;
192*00b67f09SDavid van Moolenbroek 	ISC_LINK(dispsocket_t)		blink;
193*00b67f09SDavid van Moolenbroek };
194*00b67f09SDavid van Moolenbroek 
195*00b67f09SDavid van Moolenbroek /*%
196*00b67f09SDavid van Moolenbroek  * A port table entry.  We remember every port we first open in a table with a
197*00b67f09SDavid van Moolenbroek  * reference counter so that we can 'reuse' the same port (with different
198*00b67f09SDavid van Moolenbroek  * destination addresses) using the SO_REUSEADDR socket option.
199*00b67f09SDavid van Moolenbroek  */
200*00b67f09SDavid van Moolenbroek struct dispportentry {
201*00b67f09SDavid van Moolenbroek 	in_port_t			port;
202*00b67f09SDavid van Moolenbroek 	unsigned int			refs;
203*00b67f09SDavid van Moolenbroek 	ISC_LINK(struct dispportentry)	link;
204*00b67f09SDavid van Moolenbroek };
205*00b67f09SDavid van Moolenbroek 
206*00b67f09SDavid van Moolenbroek #ifndef DNS_DISPATCH_PORTTABLESIZE
207*00b67f09SDavid van Moolenbroek #define DNS_DISPATCH_PORTTABLESIZE	1024
208*00b67f09SDavid van Moolenbroek #endif
209*00b67f09SDavid van Moolenbroek 
210*00b67f09SDavid van Moolenbroek #define INVALID_BUCKET		(0xffffdead)
211*00b67f09SDavid van Moolenbroek 
212*00b67f09SDavid van Moolenbroek /*%
213*00b67f09SDavid van Moolenbroek  * Number of tasks for each dispatch that use separate sockets for different
214*00b67f09SDavid van Moolenbroek  * transactions.  This must be a power of 2 as it will divide 32 bit numbers
215*00b67f09SDavid van Moolenbroek  * to get an uniformly random tasks selection.  See get_dispsocket().
216*00b67f09SDavid van Moolenbroek  */
217*00b67f09SDavid van Moolenbroek #define MAX_INTERNAL_TASKS	64
218*00b67f09SDavid van Moolenbroek 
219*00b67f09SDavid van Moolenbroek struct dns_dispatch {
220*00b67f09SDavid van Moolenbroek 	/* Unlocked. */
221*00b67f09SDavid van Moolenbroek 	unsigned int		magic;		/*%< magic */
222*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t      *mgr;		/*%< dispatch manager */
223*00b67f09SDavid van Moolenbroek 	int			ntasks;
224*00b67f09SDavid van Moolenbroek 	/*%
225*00b67f09SDavid van Moolenbroek 	 * internal task buckets.  We use multiple tasks to distribute various
226*00b67f09SDavid van Moolenbroek 	 * socket events well when using separate dispatch sockets.  We use the
227*00b67f09SDavid van Moolenbroek 	 * 1st task (task[0]) for internal control events.
228*00b67f09SDavid van Moolenbroek 	 */
229*00b67f09SDavid van Moolenbroek 	isc_task_t	       *task[MAX_INTERNAL_TASKS];
230*00b67f09SDavid van Moolenbroek 	isc_socket_t	       *socket;		/*%< isc socket attached to */
231*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t		local;		/*%< local address */
232*00b67f09SDavid van Moolenbroek 	in_port_t		localport;	/*%< local UDP port */
233*00b67f09SDavid van Moolenbroek 	isc_dscp_t		dscp;		/*%< "listen-on" DSCP value */
234*00b67f09SDavid van Moolenbroek 	unsigned int		maxrequests;	/*%< max requests */
235*00b67f09SDavid van Moolenbroek 	isc_event_t	       *ctlevent;
236*00b67f09SDavid van Moolenbroek 
237*00b67f09SDavid van Moolenbroek 	isc_mutex_t		sepool_lock;
238*00b67f09SDavid van Moolenbroek 	isc_mempool_t	       *sepool;		/*%< pool for socket events */
239*00b67f09SDavid van Moolenbroek 
240*00b67f09SDavid van Moolenbroek 	/*% Locked by mgr->lock. */
241*00b67f09SDavid van Moolenbroek 	ISC_LINK(dns_dispatch_t) link;
242*00b67f09SDavid van Moolenbroek 
243*00b67f09SDavid van Moolenbroek 	/* Locked by "lock". */
244*00b67f09SDavid van Moolenbroek 	isc_mutex_t		lock;		/*%< locks all below */
245*00b67f09SDavid van Moolenbroek 	isc_sockettype_t	socktype;
246*00b67f09SDavid van Moolenbroek 	unsigned int		attributes;
247*00b67f09SDavid van Moolenbroek 	unsigned int		refcount;	/*%< number of users */
248*00b67f09SDavid van Moolenbroek 	dns_dispatchevent_t    *failsafe_ev;	/*%< failsafe cancel event */
249*00b67f09SDavid van Moolenbroek 	unsigned int		shutting_down : 1,
250*00b67f09SDavid van Moolenbroek 				shutdown_out : 1,
251*00b67f09SDavid van Moolenbroek 				connected : 1,
252*00b67f09SDavid van Moolenbroek 				tcpmsg_valid : 1,
253*00b67f09SDavid van Moolenbroek 				recv_pending : 1; /*%< is a recv() pending? */
254*00b67f09SDavid van Moolenbroek 	isc_result_t		shutdown_why;
255*00b67f09SDavid van Moolenbroek 	ISC_LIST(dispsocket_t)	activesockets;
256*00b67f09SDavid van Moolenbroek 	ISC_LIST(dispsocket_t)	inactivesockets;
257*00b67f09SDavid van Moolenbroek 	unsigned int		nsockets;
258*00b67f09SDavid van Moolenbroek 	unsigned int		requests;	/*%< how many requests we have */
259*00b67f09SDavid van Moolenbroek 	unsigned int		tcpbuffers;	/*%< allocated buffers */
260*00b67f09SDavid van Moolenbroek 	dns_tcpmsg_t		tcpmsg;		/*%< for tcp streams */
261*00b67f09SDavid van Moolenbroek 	dns_qid_t		*qid;
262*00b67f09SDavid van Moolenbroek 	arc4ctx_t		arc4ctx;	/*%< for QID/UDP port num */
263*00b67f09SDavid van Moolenbroek 	dispportlist_t		*port_table;	/*%< hold ports 'owned' by us */
264*00b67f09SDavid van Moolenbroek 	isc_mempool_t		*portpool;	/*%< port table entries  */
265*00b67f09SDavid van Moolenbroek };
266*00b67f09SDavid van Moolenbroek 
267*00b67f09SDavid van Moolenbroek #define QID_MAGIC		ISC_MAGIC('Q', 'i', 'd', ' ')
268*00b67f09SDavid van Moolenbroek #define VALID_QID(e)		ISC_MAGIC_VALID((e), QID_MAGIC)
269*00b67f09SDavid van Moolenbroek 
270*00b67f09SDavid van Moolenbroek #define RESPONSE_MAGIC		ISC_MAGIC('D', 'r', 's', 'p')
271*00b67f09SDavid van Moolenbroek #define VALID_RESPONSE(e)	ISC_MAGIC_VALID((e), RESPONSE_MAGIC)
272*00b67f09SDavid van Moolenbroek 
273*00b67f09SDavid van Moolenbroek #define DISPSOCK_MAGIC		ISC_MAGIC('D', 's', 'o', 'c')
274*00b67f09SDavid van Moolenbroek #define VALID_DISPSOCK(e)	ISC_MAGIC_VALID((e), DISPSOCK_MAGIC)
275*00b67f09SDavid van Moolenbroek 
276*00b67f09SDavid van Moolenbroek #define DISPATCH_MAGIC		ISC_MAGIC('D', 'i', 's', 'p')
277*00b67f09SDavid van Moolenbroek #define VALID_DISPATCH(e)	ISC_MAGIC_VALID((e), DISPATCH_MAGIC)
278*00b67f09SDavid van Moolenbroek 
279*00b67f09SDavid van Moolenbroek #define DNS_DISPATCHMGR_MAGIC	ISC_MAGIC('D', 'M', 'g', 'r')
280*00b67f09SDavid van Moolenbroek #define VALID_DISPATCHMGR(e)	ISC_MAGIC_VALID((e), DNS_DISPATCHMGR_MAGIC)
281*00b67f09SDavid van Moolenbroek 
282*00b67f09SDavid van Moolenbroek #define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \
283*00b67f09SDavid van Moolenbroek 		       (disp)->qid : (disp)->mgr->qid
284*00b67f09SDavid van Moolenbroek #define DISP_ARC4CTX(disp) ((disp)->socktype == isc_sockettype_udp) ? \
285*00b67f09SDavid van Moolenbroek 			(&(disp)->arc4ctx) : (&(disp)->mgr->arc4ctx)
286*00b67f09SDavid van Moolenbroek 
287*00b67f09SDavid van Moolenbroek /*%
288*00b67f09SDavid van Moolenbroek  * Locking a query port buffer is a bit tricky.  We access the buffer without
289*00b67f09SDavid van Moolenbroek  * locking until qid is created.  Technically, there is a possibility of race
290*00b67f09SDavid van Moolenbroek  * between the creation of qid and access to the port buffer; in practice,
291*00b67f09SDavid van Moolenbroek  * however, this should be safe because qid isn't created until the first
292*00b67f09SDavid van Moolenbroek  * dispatch is created and there should be no contending situation until then.
293*00b67f09SDavid van Moolenbroek  */
294*00b67f09SDavid van Moolenbroek #define PORTBUFLOCK(mgr) if ((mgr)->qid != NULL) LOCK(&((mgr)->qid->lock))
295*00b67f09SDavid van Moolenbroek #define PORTBUFUNLOCK(mgr) if ((mgr)->qid != NULL) UNLOCK((&(mgr)->qid->lock))
296*00b67f09SDavid van Moolenbroek 
297*00b67f09SDavid van Moolenbroek /*
298*00b67f09SDavid van Moolenbroek  * Statics.
299*00b67f09SDavid van Moolenbroek  */
300*00b67f09SDavid van Moolenbroek static dns_dispentry_t *entry_search(dns_qid_t *, isc_sockaddr_t *,
301*00b67f09SDavid van Moolenbroek 				     dns_messageid_t, in_port_t, unsigned int);
302*00b67f09SDavid van Moolenbroek static isc_boolean_t destroy_disp_ok(dns_dispatch_t *);
303*00b67f09SDavid van Moolenbroek static void destroy_disp(isc_task_t *task, isc_event_t *event);
304*00b67f09SDavid van Moolenbroek static void destroy_dispsocket(dns_dispatch_t *, dispsocket_t **);
305*00b67f09SDavid van Moolenbroek static void deactivate_dispsocket(dns_dispatch_t *, dispsocket_t *);
306*00b67f09SDavid van Moolenbroek static void udp_exrecv(isc_task_t *, isc_event_t *);
307*00b67f09SDavid van Moolenbroek static void udp_shrecv(isc_task_t *, isc_event_t *);
308*00b67f09SDavid van Moolenbroek static void udp_recv(isc_event_t *, dns_dispatch_t *, dispsocket_t *);
309*00b67f09SDavid van Moolenbroek static void tcp_recv(isc_task_t *, isc_event_t *);
310*00b67f09SDavid van Moolenbroek static isc_result_t startrecv(dns_dispatch_t *, dispsocket_t *);
311*00b67f09SDavid van Moolenbroek static isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t,
312*00b67f09SDavid van Moolenbroek 			     in_port_t);
313*00b67f09SDavid van Moolenbroek static void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len);
314*00b67f09SDavid van Moolenbroek static void *allocate_udp_buffer(dns_dispatch_t *disp);
315*00b67f09SDavid van Moolenbroek static inline void free_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev);
316*00b67f09SDavid van Moolenbroek static inline dns_dispatchevent_t *allocate_devent(dns_dispatch_t *disp);
317*00b67f09SDavid van Moolenbroek static void do_cancel(dns_dispatch_t *disp);
318*00b67f09SDavid van Moolenbroek static dns_dispentry_t *linear_first(dns_qid_t *disp);
319*00b67f09SDavid van Moolenbroek static dns_dispentry_t *linear_next(dns_qid_t *disp,
320*00b67f09SDavid van Moolenbroek 				    dns_dispentry_t *resp);
321*00b67f09SDavid van Moolenbroek static void dispatch_free(dns_dispatch_t **dispp);
322*00b67f09SDavid van Moolenbroek static isc_result_t get_udpsocket(dns_dispatchmgr_t *mgr,
323*00b67f09SDavid van Moolenbroek 				  dns_dispatch_t *disp,
324*00b67f09SDavid van Moolenbroek 				  isc_socketmgr_t *sockmgr,
325*00b67f09SDavid van Moolenbroek 				  isc_sockaddr_t *localaddr,
326*00b67f09SDavid van Moolenbroek 				  isc_socket_t **sockp,
327*00b67f09SDavid van Moolenbroek 				  isc_socket_t *dup_socket);
328*00b67f09SDavid van Moolenbroek static isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr,
329*00b67f09SDavid van Moolenbroek 				       isc_socketmgr_t *sockmgr,
330*00b67f09SDavid van Moolenbroek 				       isc_taskmgr_t *taskmgr,
331*00b67f09SDavid van Moolenbroek 				       isc_sockaddr_t *localaddr,
332*00b67f09SDavid van Moolenbroek 				       unsigned int maxrequests,
333*00b67f09SDavid van Moolenbroek 				       unsigned int attributes,
334*00b67f09SDavid van Moolenbroek 				       dns_dispatch_t **dispp,
335*00b67f09SDavid van Moolenbroek 				       isc_socket_t *dup_socket);
336*00b67f09SDavid van Moolenbroek static isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr);
337*00b67f09SDavid van Moolenbroek static void destroy_mgr(dns_dispatchmgr_t **mgrp);
338*00b67f09SDavid van Moolenbroek static isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
339*00b67f09SDavid van Moolenbroek 				 unsigned int increment, dns_qid_t **qidp,
340*00b67f09SDavid van Moolenbroek 				 isc_boolean_t needaddrtable);
341*00b67f09SDavid van Moolenbroek static void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp);
342*00b67f09SDavid van Moolenbroek static isc_result_t open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local,
343*00b67f09SDavid van Moolenbroek 				unsigned int options, isc_socket_t **sockp,
344*00b67f09SDavid van Moolenbroek 				isc_socket_t *dup_socket);
345*00b67f09SDavid van Moolenbroek static isc_boolean_t portavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
346*00b67f09SDavid van Moolenbroek 				   isc_sockaddr_t *sockaddrp);
347*00b67f09SDavid van Moolenbroek 
348*00b67f09SDavid van Moolenbroek #define LVL(x) ISC_LOG_DEBUG(x)
349*00b67f09SDavid van Moolenbroek 
350*00b67f09SDavid van Moolenbroek static void
351*00b67f09SDavid van Moolenbroek mgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...)
352*00b67f09SDavid van Moolenbroek      ISC_FORMAT_PRINTF(3, 4);
353*00b67f09SDavid van Moolenbroek 
354*00b67f09SDavid van Moolenbroek static void
mgr_log(dns_dispatchmgr_t * mgr,int level,const char * fmt,...)355*00b67f09SDavid van Moolenbroek mgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) {
356*00b67f09SDavid van Moolenbroek 	char msgbuf[2048];
357*00b67f09SDavid van Moolenbroek 	va_list ap;
358*00b67f09SDavid van Moolenbroek 
359*00b67f09SDavid van Moolenbroek 	if (! isc_log_wouldlog(dns_lctx, level))
360*00b67f09SDavid van Moolenbroek 		return;
361*00b67f09SDavid van Moolenbroek 
362*00b67f09SDavid van Moolenbroek 	va_start(ap, fmt);
363*00b67f09SDavid van Moolenbroek 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
364*00b67f09SDavid van Moolenbroek 	va_end(ap);
365*00b67f09SDavid van Moolenbroek 
366*00b67f09SDavid van Moolenbroek 	isc_log_write(dns_lctx,
367*00b67f09SDavid van Moolenbroek 		      DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH,
368*00b67f09SDavid van Moolenbroek 		      level, "dispatchmgr %p: %s", mgr, msgbuf);
369*00b67f09SDavid van Moolenbroek }
370*00b67f09SDavid van Moolenbroek 
371*00b67f09SDavid van Moolenbroek static inline void
inc_stats(dns_dispatchmgr_t * mgr,isc_statscounter_t counter)372*00b67f09SDavid van Moolenbroek inc_stats(dns_dispatchmgr_t *mgr, isc_statscounter_t counter) {
373*00b67f09SDavid van Moolenbroek 	if (mgr->stats != NULL)
374*00b67f09SDavid van Moolenbroek 		isc_stats_increment(mgr->stats, counter);
375*00b67f09SDavid van Moolenbroek }
376*00b67f09SDavid van Moolenbroek 
377*00b67f09SDavid van Moolenbroek static inline void
dec_stats(dns_dispatchmgr_t * mgr,isc_statscounter_t counter)378*00b67f09SDavid van Moolenbroek dec_stats(dns_dispatchmgr_t *mgr, isc_statscounter_t counter) {
379*00b67f09SDavid van Moolenbroek 	if (mgr->stats != NULL)
380*00b67f09SDavid van Moolenbroek 		isc_stats_decrement(mgr->stats, counter);
381*00b67f09SDavid van Moolenbroek }
382*00b67f09SDavid van Moolenbroek 
383*00b67f09SDavid van Moolenbroek static void
384*00b67f09SDavid van Moolenbroek dispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...)
385*00b67f09SDavid van Moolenbroek      ISC_FORMAT_PRINTF(3, 4);
386*00b67f09SDavid van Moolenbroek 
387*00b67f09SDavid van Moolenbroek static void
dispatch_log(dns_dispatch_t * disp,int level,const char * fmt,...)388*00b67f09SDavid van Moolenbroek dispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) {
389*00b67f09SDavid van Moolenbroek 	char msgbuf[2048];
390*00b67f09SDavid van Moolenbroek 	va_list ap;
391*00b67f09SDavid van Moolenbroek 
392*00b67f09SDavid van Moolenbroek 	if (! isc_log_wouldlog(dns_lctx, level))
393*00b67f09SDavid van Moolenbroek 		return;
394*00b67f09SDavid van Moolenbroek 
395*00b67f09SDavid van Moolenbroek 	va_start(ap, fmt);
396*00b67f09SDavid van Moolenbroek 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
397*00b67f09SDavid van Moolenbroek 	va_end(ap);
398*00b67f09SDavid van Moolenbroek 
399*00b67f09SDavid van Moolenbroek 	isc_log_write(dns_lctx,
400*00b67f09SDavid van Moolenbroek 		      DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH,
401*00b67f09SDavid van Moolenbroek 		      level, "dispatch %p: %s", disp, msgbuf);
402*00b67f09SDavid van Moolenbroek }
403*00b67f09SDavid van Moolenbroek 
404*00b67f09SDavid van Moolenbroek static void
405*00b67f09SDavid van Moolenbroek request_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
406*00b67f09SDavid van Moolenbroek 	    int level, const char *fmt, ...)
407*00b67f09SDavid van Moolenbroek      ISC_FORMAT_PRINTF(4, 5);
408*00b67f09SDavid van Moolenbroek 
409*00b67f09SDavid van Moolenbroek static void
request_log(dns_dispatch_t * disp,dns_dispentry_t * resp,int level,const char * fmt,...)410*00b67f09SDavid van Moolenbroek request_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
411*00b67f09SDavid van Moolenbroek 	    int level, const char *fmt, ...)
412*00b67f09SDavid van Moolenbroek {
413*00b67f09SDavid van Moolenbroek 	char msgbuf[2048];
414*00b67f09SDavid van Moolenbroek 	char peerbuf[256];
415*00b67f09SDavid van Moolenbroek 	va_list ap;
416*00b67f09SDavid van Moolenbroek 
417*00b67f09SDavid van Moolenbroek 	if (! isc_log_wouldlog(dns_lctx, level))
418*00b67f09SDavid van Moolenbroek 		return;
419*00b67f09SDavid van Moolenbroek 
420*00b67f09SDavid van Moolenbroek 	va_start(ap, fmt);
421*00b67f09SDavid van Moolenbroek 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
422*00b67f09SDavid van Moolenbroek 	va_end(ap);
423*00b67f09SDavid van Moolenbroek 
424*00b67f09SDavid van Moolenbroek 	if (VALID_RESPONSE(resp)) {
425*00b67f09SDavid van Moolenbroek 		isc_sockaddr_format(&resp->host, peerbuf, sizeof(peerbuf));
426*00b67f09SDavid van Moolenbroek 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH,
427*00b67f09SDavid van Moolenbroek 			      DNS_LOGMODULE_DISPATCH, level,
428*00b67f09SDavid van Moolenbroek 			      "dispatch %p response %p %s: %s", disp, resp,
429*00b67f09SDavid van Moolenbroek 			      peerbuf, msgbuf);
430*00b67f09SDavid van Moolenbroek 	} else {
431*00b67f09SDavid van Moolenbroek 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH,
432*00b67f09SDavid van Moolenbroek 			      DNS_LOGMODULE_DISPATCH, level,
433*00b67f09SDavid van Moolenbroek 			      "dispatch %p req/resp %p: %s", disp, resp,
434*00b67f09SDavid van Moolenbroek 			      msgbuf);
435*00b67f09SDavid van Moolenbroek 	}
436*00b67f09SDavid van Moolenbroek }
437*00b67f09SDavid van Moolenbroek 
438*00b67f09SDavid van Moolenbroek /*%
439*00b67f09SDavid van Moolenbroek  * ARC4 random number generator derived from OpenBSD.
440*00b67f09SDavid van Moolenbroek  * Only dispatch_random() and dispatch_uniformrandom() are expected
441*00b67f09SDavid van Moolenbroek  * to be called from general dispatch routines; the rest of them are subroutines
442*00b67f09SDavid van Moolenbroek  * for these two.
443*00b67f09SDavid van Moolenbroek  *
444*00b67f09SDavid van Moolenbroek  * The original copyright follows:
445*00b67f09SDavid van Moolenbroek  * Copyright (c) 1996, David Mazieres <dm@uun.org>
446*00b67f09SDavid van Moolenbroek  * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
447*00b67f09SDavid van Moolenbroek  *
448*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and distribute this software for any
449*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
450*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
451*00b67f09SDavid van Moolenbroek  *
452*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
453*00b67f09SDavid van Moolenbroek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
454*00b67f09SDavid van Moolenbroek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
455*00b67f09SDavid van Moolenbroek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
456*00b67f09SDavid van Moolenbroek  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
457*00b67f09SDavid van Moolenbroek  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
458*00b67f09SDavid van Moolenbroek  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
459*00b67f09SDavid van Moolenbroek  */
460*00b67f09SDavid van Moolenbroek static void
dispatch_initrandom(arc4ctx_t * actx,isc_entropy_t * entropy,isc_mutex_t * lock)461*00b67f09SDavid van Moolenbroek dispatch_initrandom(arc4ctx_t *actx, isc_entropy_t *entropy,
462*00b67f09SDavid van Moolenbroek 		    isc_mutex_t *lock)
463*00b67f09SDavid van Moolenbroek {
464*00b67f09SDavid van Moolenbroek 	int n;
465*00b67f09SDavid van Moolenbroek 	for (n = 0; n < 256; n++)
466*00b67f09SDavid van Moolenbroek 		actx->s[n] = n;
467*00b67f09SDavid van Moolenbroek 	actx->i = 0;
468*00b67f09SDavid van Moolenbroek 	actx->j = 0;
469*00b67f09SDavid van Moolenbroek 	actx->count = 0;
470*00b67f09SDavid van Moolenbroek 	actx->entropy = entropy; /* don't have to attach */
471*00b67f09SDavid van Moolenbroek 	actx->lock = lock;
472*00b67f09SDavid van Moolenbroek }
473*00b67f09SDavid van Moolenbroek 
474*00b67f09SDavid van Moolenbroek static void
dispatch_arc4addrandom(arc4ctx_t * actx,unsigned char * dat,int datlen)475*00b67f09SDavid van Moolenbroek dispatch_arc4addrandom(arc4ctx_t *actx, unsigned char *dat, int datlen) {
476*00b67f09SDavid van Moolenbroek 	int n;
477*00b67f09SDavid van Moolenbroek 	isc_uint8_t si;
478*00b67f09SDavid van Moolenbroek 
479*00b67f09SDavid van Moolenbroek 	actx->i--;
480*00b67f09SDavid van Moolenbroek 	for (n = 0; n < 256; n++) {
481*00b67f09SDavid van Moolenbroek 		actx->i = (actx->i + 1);
482*00b67f09SDavid van Moolenbroek 		si = actx->s[actx->i];
483*00b67f09SDavid van Moolenbroek 		actx->j = (actx->j + si + dat[n % datlen]);
484*00b67f09SDavid van Moolenbroek 		actx->s[actx->i] = actx->s[actx->j];
485*00b67f09SDavid van Moolenbroek 		actx->s[actx->j] = si;
486*00b67f09SDavid van Moolenbroek 	}
487*00b67f09SDavid van Moolenbroek 	actx->j = actx->i;
488*00b67f09SDavid van Moolenbroek }
489*00b67f09SDavid van Moolenbroek 
490*00b67f09SDavid van Moolenbroek static inline isc_uint8_t
dispatch_arc4get8(arc4ctx_t * actx)491*00b67f09SDavid van Moolenbroek dispatch_arc4get8(arc4ctx_t *actx) {
492*00b67f09SDavid van Moolenbroek 	isc_uint8_t si, sj;
493*00b67f09SDavid van Moolenbroek 
494*00b67f09SDavid van Moolenbroek 	actx->i = (actx->i + 1);
495*00b67f09SDavid van Moolenbroek 	si = actx->s[actx->i];
496*00b67f09SDavid van Moolenbroek 	actx->j = (actx->j + si);
497*00b67f09SDavid van Moolenbroek 	sj = actx->s[actx->j];
498*00b67f09SDavid van Moolenbroek 	actx->s[actx->i] = sj;
499*00b67f09SDavid van Moolenbroek 	actx->s[actx->j] = si;
500*00b67f09SDavid van Moolenbroek 
501*00b67f09SDavid van Moolenbroek 	return (actx->s[(si + sj) & 0xff]);
502*00b67f09SDavid van Moolenbroek }
503*00b67f09SDavid van Moolenbroek 
504*00b67f09SDavid van Moolenbroek static inline isc_uint16_t
dispatch_arc4get16(arc4ctx_t * actx)505*00b67f09SDavid van Moolenbroek dispatch_arc4get16(arc4ctx_t *actx) {
506*00b67f09SDavid van Moolenbroek 	isc_uint16_t val;
507*00b67f09SDavid van Moolenbroek 
508*00b67f09SDavid van Moolenbroek 	val = dispatch_arc4get8(actx) << 8;
509*00b67f09SDavid van Moolenbroek 	val |= dispatch_arc4get8(actx);
510*00b67f09SDavid van Moolenbroek 
511*00b67f09SDavid van Moolenbroek 	return (val);
512*00b67f09SDavid van Moolenbroek }
513*00b67f09SDavid van Moolenbroek 
514*00b67f09SDavid van Moolenbroek static void
dispatch_arc4stir(arc4ctx_t * actx)515*00b67f09SDavid van Moolenbroek dispatch_arc4stir(arc4ctx_t *actx) {
516*00b67f09SDavid van Moolenbroek 	int i;
517*00b67f09SDavid van Moolenbroek 	union {
518*00b67f09SDavid van Moolenbroek 		unsigned char rnd[128];
519*00b67f09SDavid van Moolenbroek 		isc_uint32_t rnd32[32];
520*00b67f09SDavid van Moolenbroek 	} rnd;
521*00b67f09SDavid van Moolenbroek 	isc_result_t result;
522*00b67f09SDavid van Moolenbroek 
523*00b67f09SDavid van Moolenbroek 	if (actx->entropy != NULL) {
524*00b67f09SDavid van Moolenbroek 		/*
525*00b67f09SDavid van Moolenbroek 		 * We accept any quality of random data to avoid blocking.
526*00b67f09SDavid van Moolenbroek 		 */
527*00b67f09SDavid van Moolenbroek 		result = isc_entropy_getdata(actx->entropy, rnd.rnd,
528*00b67f09SDavid van Moolenbroek 					     sizeof(rnd), NULL, 0);
529*00b67f09SDavid van Moolenbroek 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
530*00b67f09SDavid van Moolenbroek 	} else {
531*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 32; i++)
532*00b67f09SDavid van Moolenbroek 			isc_random_get(&rnd.rnd32[i]);
533*00b67f09SDavid van Moolenbroek 	}
534*00b67f09SDavid van Moolenbroek 	dispatch_arc4addrandom(actx, rnd.rnd, sizeof(rnd.rnd));
535*00b67f09SDavid van Moolenbroek 
536*00b67f09SDavid van Moolenbroek 	/*
537*00b67f09SDavid van Moolenbroek 	 * Discard early keystream, as per recommendations in:
538*00b67f09SDavid van Moolenbroek 	 * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
539*00b67f09SDavid van Moolenbroek 	 */
540*00b67f09SDavid van Moolenbroek 	for (i = 0; i < 256; i++)
541*00b67f09SDavid van Moolenbroek 		(void)dispatch_arc4get8(actx);
542*00b67f09SDavid van Moolenbroek 
543*00b67f09SDavid van Moolenbroek 	/*
544*00b67f09SDavid van Moolenbroek 	 * Derived from OpenBSD's implementation.  The rationale is not clear,
545*00b67f09SDavid van Moolenbroek 	 * but should be conservative enough in safety, and reasonably large
546*00b67f09SDavid van Moolenbroek 	 * for efficiency.
547*00b67f09SDavid van Moolenbroek 	 */
548*00b67f09SDavid van Moolenbroek 	actx->count = 1600000;
549*00b67f09SDavid van Moolenbroek }
550*00b67f09SDavid van Moolenbroek 
551*00b67f09SDavid van Moolenbroek static isc_uint16_t
dispatch_random(arc4ctx_t * actx)552*00b67f09SDavid van Moolenbroek dispatch_random(arc4ctx_t *actx) {
553*00b67f09SDavid van Moolenbroek 	isc_uint16_t result;
554*00b67f09SDavid van Moolenbroek 
555*00b67f09SDavid van Moolenbroek 	if (actx->lock != NULL)
556*00b67f09SDavid van Moolenbroek 		LOCK(actx->lock);
557*00b67f09SDavid van Moolenbroek 
558*00b67f09SDavid van Moolenbroek 	actx->count -= sizeof(isc_uint16_t);
559*00b67f09SDavid van Moolenbroek 	if (actx->count <= 0)
560*00b67f09SDavid van Moolenbroek 		dispatch_arc4stir(actx);
561*00b67f09SDavid van Moolenbroek 	result = dispatch_arc4get16(actx);
562*00b67f09SDavid van Moolenbroek 
563*00b67f09SDavid van Moolenbroek 	if (actx->lock != NULL)
564*00b67f09SDavid van Moolenbroek 		UNLOCK(actx->lock);
565*00b67f09SDavid van Moolenbroek 
566*00b67f09SDavid van Moolenbroek 	return (result);
567*00b67f09SDavid van Moolenbroek }
568*00b67f09SDavid van Moolenbroek 
569*00b67f09SDavid van Moolenbroek static isc_uint16_t
dispatch_uniformrandom(arc4ctx_t * actx,isc_uint16_t upper_bound)570*00b67f09SDavid van Moolenbroek dispatch_uniformrandom(arc4ctx_t *actx, isc_uint16_t upper_bound) {
571*00b67f09SDavid van Moolenbroek 	isc_uint16_t min, r;
572*00b67f09SDavid van Moolenbroek 
573*00b67f09SDavid van Moolenbroek 	if (upper_bound < 2)
574*00b67f09SDavid van Moolenbroek 		return (0);
575*00b67f09SDavid van Moolenbroek 
576*00b67f09SDavid van Moolenbroek 	/*
577*00b67f09SDavid van Moolenbroek 	 * Ensure the range of random numbers [min, 0xffff] be a multiple of
578*00b67f09SDavid van Moolenbroek 	 * upper_bound and contain at least a half of the 16 bit range.
579*00b67f09SDavid van Moolenbroek 	 */
580*00b67f09SDavid van Moolenbroek 
581*00b67f09SDavid van Moolenbroek 	if (upper_bound > 0x8000)
582*00b67f09SDavid van Moolenbroek 		min = 1 + ~upper_bound; /* 0x8000 - upper_bound */
583*00b67f09SDavid van Moolenbroek 	else
584*00b67f09SDavid van Moolenbroek 		min = (isc_uint16_t)(0x10000 % (isc_uint32_t)upper_bound);
585*00b67f09SDavid van Moolenbroek 
586*00b67f09SDavid van Moolenbroek 	/*
587*00b67f09SDavid van Moolenbroek 	 * This could theoretically loop forever but each retry has
588*00b67f09SDavid van Moolenbroek 	 * p > 0.5 (worst case, usually far better) of selecting a
589*00b67f09SDavid van Moolenbroek 	 * number inside the range we need, so it should rarely need
590*00b67f09SDavid van Moolenbroek 	 * to re-roll.
591*00b67f09SDavid van Moolenbroek 	 */
592*00b67f09SDavid van Moolenbroek 	for (;;) {
593*00b67f09SDavid van Moolenbroek 		r = dispatch_random(actx);
594*00b67f09SDavid van Moolenbroek 		if (r >= min)
595*00b67f09SDavid van Moolenbroek 			break;
596*00b67f09SDavid van Moolenbroek 	}
597*00b67f09SDavid van Moolenbroek 
598*00b67f09SDavid van Moolenbroek 	return (r % upper_bound);
599*00b67f09SDavid van Moolenbroek }
600*00b67f09SDavid van Moolenbroek 
601*00b67f09SDavid van Moolenbroek /*
602*00b67f09SDavid van Moolenbroek  * Return a hash of the destination and message id.
603*00b67f09SDavid van Moolenbroek  */
604*00b67f09SDavid van Moolenbroek static isc_uint32_t
dns_hash(dns_qid_t * qid,isc_sockaddr_t * dest,dns_messageid_t id,in_port_t port)605*00b67f09SDavid van Moolenbroek dns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
606*00b67f09SDavid van Moolenbroek 	 in_port_t port)
607*00b67f09SDavid van Moolenbroek {
608*00b67f09SDavid van Moolenbroek 	unsigned int ret;
609*00b67f09SDavid van Moolenbroek 
610*00b67f09SDavid van Moolenbroek 	ret = isc_sockaddr_hash(dest, ISC_TRUE);
611*00b67f09SDavid van Moolenbroek 	ret ^= (id << 16) | port;
612*00b67f09SDavid van Moolenbroek 	ret %= qid->qid_nbuckets;
613*00b67f09SDavid van Moolenbroek 
614*00b67f09SDavid van Moolenbroek 	INSIST(ret < qid->qid_nbuckets);
615*00b67f09SDavid van Moolenbroek 
616*00b67f09SDavid van Moolenbroek 	return (ret);
617*00b67f09SDavid van Moolenbroek }
618*00b67f09SDavid van Moolenbroek 
619*00b67f09SDavid van Moolenbroek /*
620*00b67f09SDavid van Moolenbroek  * Find the first entry in 'qid'.  Returns NULL if there are no entries.
621*00b67f09SDavid van Moolenbroek  */
622*00b67f09SDavid van Moolenbroek static dns_dispentry_t *
linear_first(dns_qid_t * qid)623*00b67f09SDavid van Moolenbroek linear_first(dns_qid_t *qid) {
624*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *ret;
625*00b67f09SDavid van Moolenbroek 	unsigned int bucket;
626*00b67f09SDavid van Moolenbroek 
627*00b67f09SDavid van Moolenbroek 	bucket = 0;
628*00b67f09SDavid van Moolenbroek 
629*00b67f09SDavid van Moolenbroek 	while (bucket < qid->qid_nbuckets) {
630*00b67f09SDavid van Moolenbroek 		ret = ISC_LIST_HEAD(qid->qid_table[bucket]);
631*00b67f09SDavid van Moolenbroek 		if (ret != NULL)
632*00b67f09SDavid van Moolenbroek 			return (ret);
633*00b67f09SDavid van Moolenbroek 		bucket++;
634*00b67f09SDavid van Moolenbroek 	}
635*00b67f09SDavid van Moolenbroek 
636*00b67f09SDavid van Moolenbroek 	return (NULL);
637*00b67f09SDavid van Moolenbroek }
638*00b67f09SDavid van Moolenbroek 
639*00b67f09SDavid van Moolenbroek /*
640*00b67f09SDavid van Moolenbroek  * Find the next entry after 'resp' in 'qid'.  Return NULL if there are
641*00b67f09SDavid van Moolenbroek  * no more entries.
642*00b67f09SDavid van Moolenbroek  */
643*00b67f09SDavid van Moolenbroek static dns_dispentry_t *
linear_next(dns_qid_t * qid,dns_dispentry_t * resp)644*00b67f09SDavid van Moolenbroek linear_next(dns_qid_t *qid, dns_dispentry_t *resp) {
645*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *ret;
646*00b67f09SDavid van Moolenbroek 	unsigned int bucket;
647*00b67f09SDavid van Moolenbroek 
648*00b67f09SDavid van Moolenbroek 	ret = ISC_LIST_NEXT(resp, link);
649*00b67f09SDavid van Moolenbroek 	if (ret != NULL)
650*00b67f09SDavid van Moolenbroek 		return (ret);
651*00b67f09SDavid van Moolenbroek 
652*00b67f09SDavid van Moolenbroek 	bucket = resp->bucket;
653*00b67f09SDavid van Moolenbroek 	bucket++;
654*00b67f09SDavid van Moolenbroek 	while (bucket < qid->qid_nbuckets) {
655*00b67f09SDavid van Moolenbroek 		ret = ISC_LIST_HEAD(qid->qid_table[bucket]);
656*00b67f09SDavid van Moolenbroek 		if (ret != NULL)
657*00b67f09SDavid van Moolenbroek 			return (ret);
658*00b67f09SDavid van Moolenbroek 		bucket++;
659*00b67f09SDavid van Moolenbroek 	}
660*00b67f09SDavid van Moolenbroek 
661*00b67f09SDavid van Moolenbroek 	return (NULL);
662*00b67f09SDavid van Moolenbroek }
663*00b67f09SDavid van Moolenbroek 
664*00b67f09SDavid van Moolenbroek /*
665*00b67f09SDavid van Moolenbroek  * The dispatch must be locked.
666*00b67f09SDavid van Moolenbroek  */
667*00b67f09SDavid van Moolenbroek static isc_boolean_t
destroy_disp_ok(dns_dispatch_t * disp)668*00b67f09SDavid van Moolenbroek destroy_disp_ok(dns_dispatch_t *disp)
669*00b67f09SDavid van Moolenbroek {
670*00b67f09SDavid van Moolenbroek 	if (disp->refcount != 0)
671*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
672*00b67f09SDavid van Moolenbroek 
673*00b67f09SDavid van Moolenbroek 	if (disp->recv_pending != 0)
674*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
675*00b67f09SDavid van Moolenbroek 
676*00b67f09SDavid van Moolenbroek 	if (!ISC_LIST_EMPTY(disp->activesockets))
677*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
678*00b67f09SDavid van Moolenbroek 
679*00b67f09SDavid van Moolenbroek 	if (disp->shutting_down == 0)
680*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
681*00b67f09SDavid van Moolenbroek 
682*00b67f09SDavid van Moolenbroek 	return (ISC_TRUE);
683*00b67f09SDavid van Moolenbroek }
684*00b67f09SDavid van Moolenbroek 
685*00b67f09SDavid van Moolenbroek /*
686*00b67f09SDavid van Moolenbroek  * Called when refcount reaches 0 (and safe to destroy).
687*00b67f09SDavid van Moolenbroek  *
688*00b67f09SDavid van Moolenbroek  * The dispatcher must be locked.
689*00b67f09SDavid van Moolenbroek  * The manager must not be locked.
690*00b67f09SDavid van Moolenbroek  */
691*00b67f09SDavid van Moolenbroek static void
destroy_disp(isc_task_t * task,isc_event_t * event)692*00b67f09SDavid van Moolenbroek destroy_disp(isc_task_t *task, isc_event_t *event) {
693*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
694*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
695*00b67f09SDavid van Moolenbroek 	isc_boolean_t killmgr;
696*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsocket;
697*00b67f09SDavid van Moolenbroek 	int i;
698*00b67f09SDavid van Moolenbroek 
699*00b67f09SDavid van Moolenbroek 	INSIST(event->ev_type == DNS_EVENT_DISPATCHCONTROL);
700*00b67f09SDavid van Moolenbroek 
701*00b67f09SDavid van Moolenbroek 	UNUSED(task);
702*00b67f09SDavid van Moolenbroek 
703*00b67f09SDavid van Moolenbroek 	disp = event->ev_arg;
704*00b67f09SDavid van Moolenbroek 	mgr = disp->mgr;
705*00b67f09SDavid van Moolenbroek 
706*00b67f09SDavid van Moolenbroek 	LOCK(&mgr->lock);
707*00b67f09SDavid van Moolenbroek 	ISC_LIST_UNLINK(mgr->list, disp, link);
708*00b67f09SDavid van Moolenbroek 
709*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90),
710*00b67f09SDavid van Moolenbroek 		     "shutting down; detaching from sock %p, task %p",
711*00b67f09SDavid van Moolenbroek 		     disp->socket, disp->task[0]); /* XXXX */
712*00b67f09SDavid van Moolenbroek 
713*00b67f09SDavid van Moolenbroek 	if (disp->sepool != NULL) {
714*00b67f09SDavid van Moolenbroek 		isc_mempool_destroy(&disp->sepool);
715*00b67f09SDavid van Moolenbroek 		(void)isc_mutex_destroy(&disp->sepool_lock);
716*00b67f09SDavid van Moolenbroek 	}
717*00b67f09SDavid van Moolenbroek 
718*00b67f09SDavid van Moolenbroek 	if (disp->socket != NULL)
719*00b67f09SDavid van Moolenbroek 		isc_socket_detach(&disp->socket);
720*00b67f09SDavid van Moolenbroek 	while ((dispsocket = ISC_LIST_HEAD(disp->inactivesockets)) != NULL) {
721*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(disp->inactivesockets, dispsocket, link);
722*00b67f09SDavid van Moolenbroek 		destroy_dispsocket(disp, &dispsocket);
723*00b67f09SDavid van Moolenbroek 	}
724*00b67f09SDavid van Moolenbroek 	for (i = 0; i < disp->ntasks; i++)
725*00b67f09SDavid van Moolenbroek 		isc_task_detach(&disp->task[i]);
726*00b67f09SDavid van Moolenbroek 	isc_event_free(&event);
727*00b67f09SDavid van Moolenbroek 
728*00b67f09SDavid van Moolenbroek 	dispatch_free(&disp);
729*00b67f09SDavid van Moolenbroek 
730*00b67f09SDavid van Moolenbroek 	killmgr = destroy_mgr_ok(mgr);
731*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
732*00b67f09SDavid van Moolenbroek 	if (killmgr)
733*00b67f09SDavid van Moolenbroek 		destroy_mgr(&mgr);
734*00b67f09SDavid van Moolenbroek }
735*00b67f09SDavid van Moolenbroek 
736*00b67f09SDavid van Moolenbroek /*%
737*00b67f09SDavid van Moolenbroek  * Manipulate port table per dispatch: find an entry for a given port number,
738*00b67f09SDavid van Moolenbroek  * create a new entry, and decrement a given entry with possible clean-up.
739*00b67f09SDavid van Moolenbroek  */
740*00b67f09SDavid van Moolenbroek static dispportentry_t *
port_search(dns_dispatch_t * disp,in_port_t port)741*00b67f09SDavid van Moolenbroek port_search(dns_dispatch_t *disp, in_port_t port) {
742*00b67f09SDavid van Moolenbroek 	dispportentry_t *portentry;
743*00b67f09SDavid van Moolenbroek 
744*00b67f09SDavid van Moolenbroek 	REQUIRE(disp->port_table != NULL);
745*00b67f09SDavid van Moolenbroek 
746*00b67f09SDavid van Moolenbroek 	portentry = ISC_LIST_HEAD(disp->port_table[port %
747*00b67f09SDavid van Moolenbroek 						   DNS_DISPATCH_PORTTABLESIZE]);
748*00b67f09SDavid van Moolenbroek 	while (portentry != NULL) {
749*00b67f09SDavid van Moolenbroek 		if (portentry->port == port)
750*00b67f09SDavid van Moolenbroek 			return (portentry);
751*00b67f09SDavid van Moolenbroek 		portentry = ISC_LIST_NEXT(portentry, link);
752*00b67f09SDavid van Moolenbroek 	}
753*00b67f09SDavid van Moolenbroek 
754*00b67f09SDavid van Moolenbroek 	return (NULL);
755*00b67f09SDavid van Moolenbroek }
756*00b67f09SDavid van Moolenbroek 
757*00b67f09SDavid van Moolenbroek static dispportentry_t *
new_portentry(dns_dispatch_t * disp,in_port_t port)758*00b67f09SDavid van Moolenbroek new_portentry(dns_dispatch_t *disp, in_port_t port) {
759*00b67f09SDavid van Moolenbroek 	dispportentry_t *portentry;
760*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
761*00b67f09SDavid van Moolenbroek 
762*00b67f09SDavid van Moolenbroek 	REQUIRE(disp->port_table != NULL);
763*00b67f09SDavid van Moolenbroek 
764*00b67f09SDavid van Moolenbroek 	portentry = isc_mempool_get(disp->portpool);
765*00b67f09SDavid van Moolenbroek 	if (portentry == NULL)
766*00b67f09SDavid van Moolenbroek 		return (portentry);
767*00b67f09SDavid van Moolenbroek 
768*00b67f09SDavid van Moolenbroek 	portentry->port = port;
769*00b67f09SDavid van Moolenbroek 	portentry->refs = 1;
770*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(portentry, link);
771*00b67f09SDavid van Moolenbroek 	qid = DNS_QID(disp);
772*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
773*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(disp->port_table[port % DNS_DISPATCH_PORTTABLESIZE],
774*00b67f09SDavid van Moolenbroek 			portentry, link);
775*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
776*00b67f09SDavid van Moolenbroek 
777*00b67f09SDavid van Moolenbroek 	return (portentry);
778*00b67f09SDavid van Moolenbroek }
779*00b67f09SDavid van Moolenbroek 
780*00b67f09SDavid van Moolenbroek /*%
781*00b67f09SDavid van Moolenbroek  * The caller must not hold the qid->lock.
782*00b67f09SDavid van Moolenbroek  */
783*00b67f09SDavid van Moolenbroek static void
deref_portentry(dns_dispatch_t * disp,dispportentry_t ** portentryp)784*00b67f09SDavid van Moolenbroek deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
785*00b67f09SDavid van Moolenbroek 	dispportentry_t *portentry = *portentryp;
786*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
787*00b67f09SDavid van Moolenbroek 
788*00b67f09SDavid van Moolenbroek 	REQUIRE(disp->port_table != NULL);
789*00b67f09SDavid van Moolenbroek 	REQUIRE(portentry != NULL && portentry->refs > 0);
790*00b67f09SDavid van Moolenbroek 
791*00b67f09SDavid van Moolenbroek 	qid = DNS_QID(disp);
792*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
793*00b67f09SDavid van Moolenbroek 	portentry->refs--;
794*00b67f09SDavid van Moolenbroek 
795*00b67f09SDavid van Moolenbroek 	if (portentry->refs == 0) {
796*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(disp->port_table[portentry->port %
797*00b67f09SDavid van Moolenbroek 						 DNS_DISPATCH_PORTTABLESIZE],
798*00b67f09SDavid van Moolenbroek 				portentry, link);
799*00b67f09SDavid van Moolenbroek 		isc_mempool_put(disp->portpool, portentry);
800*00b67f09SDavid van Moolenbroek 	}
801*00b67f09SDavid van Moolenbroek 
802*00b67f09SDavid van Moolenbroek 	/*
803*00b67f09SDavid van Moolenbroek 	 * Set '*portentryp' to NULL inside the lock so that
804*00b67f09SDavid van Moolenbroek 	 * dispsock->portentry does not change in socket_search.
805*00b67f09SDavid van Moolenbroek 	 */
806*00b67f09SDavid van Moolenbroek 	*portentryp = NULL;
807*00b67f09SDavid van Moolenbroek 
808*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
809*00b67f09SDavid van Moolenbroek }
810*00b67f09SDavid van Moolenbroek 
811*00b67f09SDavid van Moolenbroek /*%
812*00b67f09SDavid van Moolenbroek  * Find a dispsocket for socket address 'dest', and port number 'port'.
813*00b67f09SDavid van Moolenbroek  * Return NULL if no such entry exists.  Requires qid->lock to be held.
814*00b67f09SDavid van Moolenbroek  */
815*00b67f09SDavid van Moolenbroek static dispsocket_t *
socket_search(dns_qid_t * qid,isc_sockaddr_t * dest,in_port_t port,unsigned int bucket)816*00b67f09SDavid van Moolenbroek socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port,
817*00b67f09SDavid van Moolenbroek 	      unsigned int bucket)
818*00b67f09SDavid van Moolenbroek {
819*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsock;
820*00b67f09SDavid van Moolenbroek 
821*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_QID(qid));
822*00b67f09SDavid van Moolenbroek 	REQUIRE(bucket < qid->qid_nbuckets);
823*00b67f09SDavid van Moolenbroek 
824*00b67f09SDavid van Moolenbroek 	dispsock = ISC_LIST_HEAD(qid->sock_table[bucket]);
825*00b67f09SDavid van Moolenbroek 
826*00b67f09SDavid van Moolenbroek 	while (dispsock != NULL) {
827*00b67f09SDavid van Moolenbroek 		if (dispsock->portentry != NULL &&
828*00b67f09SDavid van Moolenbroek 		    dispsock->portentry->port == port &&
829*00b67f09SDavid van Moolenbroek 		    isc_sockaddr_equal(dest, &dispsock->host))
830*00b67f09SDavid van Moolenbroek 			return (dispsock);
831*00b67f09SDavid van Moolenbroek 		dispsock = ISC_LIST_NEXT(dispsock, blink);
832*00b67f09SDavid van Moolenbroek 	}
833*00b67f09SDavid van Moolenbroek 
834*00b67f09SDavid van Moolenbroek 	return (NULL);
835*00b67f09SDavid van Moolenbroek }
836*00b67f09SDavid van Moolenbroek 
837*00b67f09SDavid van Moolenbroek /*%
838*00b67f09SDavid van Moolenbroek  * Make a new socket for a single dispatch with a random port number.
839*00b67f09SDavid van Moolenbroek  * The caller must hold the disp->lock
840*00b67f09SDavid van Moolenbroek  */
841*00b67f09SDavid van Moolenbroek static isc_result_t
get_dispsocket(dns_dispatch_t * disp,isc_sockaddr_t * dest,isc_socketmgr_t * sockmgr,dispsocket_t ** dispsockp,in_port_t * portp)842*00b67f09SDavid van Moolenbroek get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
843*00b67f09SDavid van Moolenbroek 	       isc_socketmgr_t *sockmgr, dispsocket_t **dispsockp,
844*00b67f09SDavid van Moolenbroek 	       in_port_t *portp)
845*00b67f09SDavid van Moolenbroek {
846*00b67f09SDavid van Moolenbroek 	int i;
847*00b67f09SDavid van Moolenbroek 	isc_uint32_t r;
848*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr = disp->mgr;
849*00b67f09SDavid van Moolenbroek 	isc_socket_t *sock = NULL;
850*00b67f09SDavid van Moolenbroek 	isc_result_t result = ISC_R_FAILURE;
851*00b67f09SDavid van Moolenbroek 	in_port_t port;
852*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t localaddr;
853*00b67f09SDavid van Moolenbroek 	unsigned int bucket = 0;
854*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsock;
855*00b67f09SDavid van Moolenbroek 	unsigned int nports;
856*00b67f09SDavid van Moolenbroek 	in_port_t *ports;
857*00b67f09SDavid van Moolenbroek 	unsigned int bindoptions;
858*00b67f09SDavid van Moolenbroek 	dispportentry_t *portentry = NULL;
859*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
860*00b67f09SDavid van Moolenbroek 
861*00b67f09SDavid van Moolenbroek 	if (isc_sockaddr_pf(&disp->local) == AF_INET) {
862*00b67f09SDavid van Moolenbroek 		nports = disp->mgr->nv4ports;
863*00b67f09SDavid van Moolenbroek 		ports = disp->mgr->v4ports;
864*00b67f09SDavid van Moolenbroek 	} else {
865*00b67f09SDavid van Moolenbroek 		nports = disp->mgr->nv6ports;
866*00b67f09SDavid van Moolenbroek 		ports = disp->mgr->v6ports;
867*00b67f09SDavid van Moolenbroek 	}
868*00b67f09SDavid van Moolenbroek 	if (nports == 0)
869*00b67f09SDavid van Moolenbroek 		return (ISC_R_ADDRNOTAVAIL);
870*00b67f09SDavid van Moolenbroek 
871*00b67f09SDavid van Moolenbroek 	dispsock = ISC_LIST_HEAD(disp->inactivesockets);
872*00b67f09SDavid van Moolenbroek 	if (dispsock != NULL) {
873*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(disp->inactivesockets, dispsock, link);
874*00b67f09SDavid van Moolenbroek 		sock = dispsock->socket;
875*00b67f09SDavid van Moolenbroek 		dispsock->socket = NULL;
876*00b67f09SDavid van Moolenbroek 	} else {
877*00b67f09SDavid van Moolenbroek 		dispsock = isc_mempool_get(mgr->spool);
878*00b67f09SDavid van Moolenbroek 		if (dispsock == NULL)
879*00b67f09SDavid van Moolenbroek 			return (ISC_R_NOMEMORY);
880*00b67f09SDavid van Moolenbroek 
881*00b67f09SDavid van Moolenbroek 		disp->nsockets++;
882*00b67f09SDavid van Moolenbroek 		dispsock->socket = NULL;
883*00b67f09SDavid van Moolenbroek 		dispsock->disp = disp;
884*00b67f09SDavid van Moolenbroek 		dispsock->resp = NULL;
885*00b67f09SDavid van Moolenbroek 		dispsock->portentry = NULL;
886*00b67f09SDavid van Moolenbroek 		isc_random_get(&r);
887*00b67f09SDavid van Moolenbroek 		dispsock->task = NULL;
888*00b67f09SDavid van Moolenbroek 		isc_task_attach(disp->task[r % disp->ntasks], &dispsock->task);
889*00b67f09SDavid van Moolenbroek 		ISC_LINK_INIT(dispsock, link);
890*00b67f09SDavid van Moolenbroek 		ISC_LINK_INIT(dispsock, blink);
891*00b67f09SDavid van Moolenbroek 		dispsock->magic = DISPSOCK_MAGIC;
892*00b67f09SDavid van Moolenbroek 	}
893*00b67f09SDavid van Moolenbroek 
894*00b67f09SDavid van Moolenbroek 	/*
895*00b67f09SDavid van Moolenbroek 	 * Pick up a random UDP port and open a new socket with it.  Avoid
896*00b67f09SDavid van Moolenbroek 	 * choosing ports that share the same destination because it will be
897*00b67f09SDavid van Moolenbroek 	 * very likely to fail in bind(2) or connect(2).
898*00b67f09SDavid van Moolenbroek 	 */
899*00b67f09SDavid van Moolenbroek 	localaddr = disp->local;
900*00b67f09SDavid van Moolenbroek 	qid = DNS_QID(disp);
901*00b67f09SDavid van Moolenbroek 
902*00b67f09SDavid van Moolenbroek 	for (i = 0; i < 64; i++) {
903*00b67f09SDavid van Moolenbroek 		port = ports[dispatch_uniformrandom(DISP_ARC4CTX(disp),
904*00b67f09SDavid van Moolenbroek 							nports)];
905*00b67f09SDavid van Moolenbroek 		isc_sockaddr_setport(&localaddr, port);
906*00b67f09SDavid van Moolenbroek 
907*00b67f09SDavid van Moolenbroek 		LOCK(&qid->lock);
908*00b67f09SDavid van Moolenbroek 		bucket = dns_hash(qid, dest, 0, port);
909*00b67f09SDavid van Moolenbroek 		if (socket_search(qid, dest, port, bucket) != NULL) {
910*00b67f09SDavid van Moolenbroek 			UNLOCK(&qid->lock);
911*00b67f09SDavid van Moolenbroek 			continue;
912*00b67f09SDavid van Moolenbroek 		}
913*00b67f09SDavid van Moolenbroek 		UNLOCK(&qid->lock);
914*00b67f09SDavid van Moolenbroek 		bindoptions = 0;
915*00b67f09SDavid van Moolenbroek 		portentry = port_search(disp, port);
916*00b67f09SDavid van Moolenbroek 
917*00b67f09SDavid van Moolenbroek 		if (portentry != NULL)
918*00b67f09SDavid van Moolenbroek 			bindoptions |= ISC_SOCKET_REUSEADDRESS;
919*00b67f09SDavid van Moolenbroek 		result = open_socket(sockmgr, &localaddr, bindoptions, &sock,
920*00b67f09SDavid van Moolenbroek 				     NULL);
921*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS) {
922*00b67f09SDavid van Moolenbroek 			if (portentry == NULL) {
923*00b67f09SDavid van Moolenbroek 				portentry = new_portentry(disp, port);
924*00b67f09SDavid van Moolenbroek 				if (portentry == NULL) {
925*00b67f09SDavid van Moolenbroek 					result = ISC_R_NOMEMORY;
926*00b67f09SDavid van Moolenbroek 					break;
927*00b67f09SDavid van Moolenbroek 				}
928*00b67f09SDavid van Moolenbroek 			} else {
929*00b67f09SDavid van Moolenbroek 				LOCK(&qid->lock);
930*00b67f09SDavid van Moolenbroek 				portentry->refs++;
931*00b67f09SDavid van Moolenbroek 				UNLOCK(&qid->lock);
932*00b67f09SDavid van Moolenbroek 			}
933*00b67f09SDavid van Moolenbroek 			break;
934*00b67f09SDavid van Moolenbroek 		} else if (result == ISC_R_NOPERM) {
935*00b67f09SDavid van Moolenbroek 			char buf[ISC_SOCKADDR_FORMATSIZE];
936*00b67f09SDavid van Moolenbroek 			isc_sockaddr_format(&localaddr, buf, sizeof(buf));
937*00b67f09SDavid van Moolenbroek 			dispatch_log(disp, ISC_LOG_WARNING,
938*00b67f09SDavid van Moolenbroek 				     "open_socket(%s) -> %s: continuing",
939*00b67f09SDavid van Moolenbroek 				     buf, isc_result_totext(result));
940*00b67f09SDavid van Moolenbroek 		} else if (result != ISC_R_ADDRINUSE)
941*00b67f09SDavid van Moolenbroek 			break;
942*00b67f09SDavid van Moolenbroek 	}
943*00b67f09SDavid van Moolenbroek 
944*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS) {
945*00b67f09SDavid van Moolenbroek 		dispsock->socket = sock;
946*00b67f09SDavid van Moolenbroek 		dispsock->host = *dest;
947*00b67f09SDavid van Moolenbroek 		dispsock->portentry = portentry;
948*00b67f09SDavid van Moolenbroek 		dispsock->bucket = bucket;
949*00b67f09SDavid van Moolenbroek 		LOCK(&qid->lock);
950*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink);
951*00b67f09SDavid van Moolenbroek 		UNLOCK(&qid->lock);
952*00b67f09SDavid van Moolenbroek 		*dispsockp = dispsock;
953*00b67f09SDavid van Moolenbroek 		*portp = port;
954*00b67f09SDavid van Moolenbroek 	} else {
955*00b67f09SDavid van Moolenbroek 		/*
956*00b67f09SDavid van Moolenbroek 		 * We could keep it in the inactive list, but since this should
957*00b67f09SDavid van Moolenbroek 		 * be an exceptional case and might be resource shortage, we'd
958*00b67f09SDavid van Moolenbroek 		 * rather destroy it.
959*00b67f09SDavid van Moolenbroek 		 */
960*00b67f09SDavid van Moolenbroek 		if (sock != NULL)
961*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&sock);
962*00b67f09SDavid van Moolenbroek 		destroy_dispsocket(disp, &dispsock);
963*00b67f09SDavid van Moolenbroek 	}
964*00b67f09SDavid van Moolenbroek 
965*00b67f09SDavid van Moolenbroek 	return (result);
966*00b67f09SDavid van Moolenbroek }
967*00b67f09SDavid van Moolenbroek 
968*00b67f09SDavid van Moolenbroek /*%
969*00b67f09SDavid van Moolenbroek  * Destroy a dedicated dispatch socket.
970*00b67f09SDavid van Moolenbroek  */
971*00b67f09SDavid van Moolenbroek static void
destroy_dispsocket(dns_dispatch_t * disp,dispsocket_t ** dispsockp)972*00b67f09SDavid van Moolenbroek destroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) {
973*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsock;
974*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
975*00b67f09SDavid van Moolenbroek 
976*00b67f09SDavid van Moolenbroek 	/*
977*00b67f09SDavid van Moolenbroek 	 * The dispatch must be locked.
978*00b67f09SDavid van Moolenbroek 	 */
979*00b67f09SDavid van Moolenbroek 
980*00b67f09SDavid van Moolenbroek 	REQUIRE(dispsockp != NULL && *dispsockp != NULL);
981*00b67f09SDavid van Moolenbroek 	dispsock = *dispsockp;
982*00b67f09SDavid van Moolenbroek 	REQUIRE(!ISC_LINK_LINKED(dispsock, link));
983*00b67f09SDavid van Moolenbroek 
984*00b67f09SDavid van Moolenbroek 	disp->nsockets--;
985*00b67f09SDavid van Moolenbroek 	dispsock->magic = 0;
986*00b67f09SDavid van Moolenbroek 	if (dispsock->portentry != NULL)
987*00b67f09SDavid van Moolenbroek 		deref_portentry(disp, &dispsock->portentry);
988*00b67f09SDavid van Moolenbroek 	if (dispsock->socket != NULL)
989*00b67f09SDavid van Moolenbroek 		isc_socket_detach(&dispsock->socket);
990*00b67f09SDavid van Moolenbroek 	if (ISC_LINK_LINKED(dispsock, blink)) {
991*00b67f09SDavid van Moolenbroek 		qid = DNS_QID(disp);
992*00b67f09SDavid van Moolenbroek 		LOCK(&qid->lock);
993*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock,
994*00b67f09SDavid van Moolenbroek 				blink);
995*00b67f09SDavid van Moolenbroek 		UNLOCK(&qid->lock);
996*00b67f09SDavid van Moolenbroek 	}
997*00b67f09SDavid van Moolenbroek 	if (dispsock->task != NULL)
998*00b67f09SDavid van Moolenbroek 		isc_task_detach(&dispsock->task);
999*00b67f09SDavid van Moolenbroek 	isc_mempool_put(disp->mgr->spool, dispsock);
1000*00b67f09SDavid van Moolenbroek 
1001*00b67f09SDavid van Moolenbroek 	*dispsockp = NULL;
1002*00b67f09SDavid van Moolenbroek }
1003*00b67f09SDavid van Moolenbroek 
1004*00b67f09SDavid van Moolenbroek /*%
1005*00b67f09SDavid van Moolenbroek  * Deactivate a dedicated dispatch socket.  Move it to the inactive list for
1006*00b67f09SDavid van Moolenbroek  * future reuse unless the total number of sockets are exceeding the maximum.
1007*00b67f09SDavid van Moolenbroek  */
1008*00b67f09SDavid van Moolenbroek static void
deactivate_dispsocket(dns_dispatch_t * disp,dispsocket_t * dispsock)1009*00b67f09SDavid van Moolenbroek deactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) {
1010*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1011*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
1012*00b67f09SDavid van Moolenbroek 
1013*00b67f09SDavid van Moolenbroek 	/*
1014*00b67f09SDavid van Moolenbroek 	 * The dispatch must be locked.
1015*00b67f09SDavid van Moolenbroek 	 */
1016*00b67f09SDavid van Moolenbroek 	ISC_LIST_UNLINK(disp->activesockets, dispsock, link);
1017*00b67f09SDavid van Moolenbroek 	if (dispsock->resp != NULL) {
1018*00b67f09SDavid van Moolenbroek 		INSIST(dispsock->resp->dispsocket == dispsock);
1019*00b67f09SDavid van Moolenbroek 		dispsock->resp->dispsocket = NULL;
1020*00b67f09SDavid van Moolenbroek 	}
1021*00b67f09SDavid van Moolenbroek 
1022*00b67f09SDavid van Moolenbroek 	INSIST(dispsock->portentry != NULL);
1023*00b67f09SDavid van Moolenbroek 	deref_portentry(disp, &dispsock->portentry);
1024*00b67f09SDavid van Moolenbroek 
1025*00b67f09SDavid van Moolenbroek 	if (disp->nsockets > DNS_DISPATCH_POOLSOCKS)
1026*00b67f09SDavid van Moolenbroek 		destroy_dispsocket(disp, &dispsock);
1027*00b67f09SDavid van Moolenbroek 	else {
1028*00b67f09SDavid van Moolenbroek 		result = isc_socket_close(dispsock->socket);
1029*00b67f09SDavid van Moolenbroek 
1030*00b67f09SDavid van Moolenbroek 		qid = DNS_QID(disp);
1031*00b67f09SDavid van Moolenbroek 		LOCK(&qid->lock);
1032*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock,
1033*00b67f09SDavid van Moolenbroek 				blink);
1034*00b67f09SDavid van Moolenbroek 		UNLOCK(&qid->lock);
1035*00b67f09SDavid van Moolenbroek 
1036*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
1037*00b67f09SDavid van Moolenbroek 			ISC_LIST_APPEND(disp->inactivesockets, dispsock, link);
1038*00b67f09SDavid van Moolenbroek 		else {
1039*00b67f09SDavid van Moolenbroek 			/*
1040*00b67f09SDavid van Moolenbroek 			 * If the underlying system does not allow this
1041*00b67f09SDavid van Moolenbroek 			 * optimization, destroy this temporary structure (and
1042*00b67f09SDavid van Moolenbroek 			 * create a new one for a new transaction).
1043*00b67f09SDavid van Moolenbroek 			 */
1044*00b67f09SDavid van Moolenbroek 			INSIST(result == ISC_R_NOTIMPLEMENTED);
1045*00b67f09SDavid van Moolenbroek 			destroy_dispsocket(disp, &dispsock);
1046*00b67f09SDavid van Moolenbroek 		}
1047*00b67f09SDavid van Moolenbroek 	}
1048*00b67f09SDavid van Moolenbroek }
1049*00b67f09SDavid van Moolenbroek 
1050*00b67f09SDavid van Moolenbroek /*
1051*00b67f09SDavid van Moolenbroek  * Find an entry for query ID 'id', socket address 'dest', and port number
1052*00b67f09SDavid van Moolenbroek  * 'port'.
1053*00b67f09SDavid van Moolenbroek  * Return NULL if no such entry exists.
1054*00b67f09SDavid van Moolenbroek  */
1055*00b67f09SDavid van Moolenbroek static dns_dispentry_t *
entry_search(dns_qid_t * qid,isc_sockaddr_t * dest,dns_messageid_t id,in_port_t port,unsigned int bucket)1056*00b67f09SDavid van Moolenbroek entry_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
1057*00b67f09SDavid van Moolenbroek 	     in_port_t port, unsigned int bucket)
1058*00b67f09SDavid van Moolenbroek {
1059*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *res;
1060*00b67f09SDavid van Moolenbroek 
1061*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_QID(qid));
1062*00b67f09SDavid van Moolenbroek 	REQUIRE(bucket < qid->qid_nbuckets);
1063*00b67f09SDavid van Moolenbroek 
1064*00b67f09SDavid van Moolenbroek 	res = ISC_LIST_HEAD(qid->qid_table[bucket]);
1065*00b67f09SDavid van Moolenbroek 
1066*00b67f09SDavid van Moolenbroek 	while (res != NULL) {
1067*00b67f09SDavid van Moolenbroek 		if (res->id == id && isc_sockaddr_equal(dest, &res->host) &&
1068*00b67f09SDavid van Moolenbroek 		    res->port == port) {
1069*00b67f09SDavid van Moolenbroek 			return (res);
1070*00b67f09SDavid van Moolenbroek 		}
1071*00b67f09SDavid van Moolenbroek 		res = ISC_LIST_NEXT(res, link);
1072*00b67f09SDavid van Moolenbroek 	}
1073*00b67f09SDavid van Moolenbroek 
1074*00b67f09SDavid van Moolenbroek 	return (NULL);
1075*00b67f09SDavid van Moolenbroek }
1076*00b67f09SDavid van Moolenbroek 
1077*00b67f09SDavid van Moolenbroek static void
free_buffer(dns_dispatch_t * disp,void * buf,unsigned int len)1078*00b67f09SDavid van Moolenbroek free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) {
1079*00b67f09SDavid van Moolenbroek 	isc_mempool_t *bpool;
1080*00b67f09SDavid van Moolenbroek 	INSIST(buf != NULL && len != 0);
1081*00b67f09SDavid van Moolenbroek 
1082*00b67f09SDavid van Moolenbroek 
1083*00b67f09SDavid van Moolenbroek 	switch (disp->socktype) {
1084*00b67f09SDavid van Moolenbroek 	case isc_sockettype_tcp:
1085*00b67f09SDavid van Moolenbroek 		INSIST(disp->tcpbuffers > 0);
1086*00b67f09SDavid van Moolenbroek 		disp->tcpbuffers--;
1087*00b67f09SDavid van Moolenbroek 		isc_mem_put(disp->mgr->mctx, buf, len);
1088*00b67f09SDavid van Moolenbroek 		break;
1089*00b67f09SDavid van Moolenbroek 	case isc_sockettype_udp:
1090*00b67f09SDavid van Moolenbroek 		LOCK(&disp->mgr->buffer_lock);
1091*00b67f09SDavid van Moolenbroek 		INSIST(disp->mgr->buffers > 0);
1092*00b67f09SDavid van Moolenbroek 		INSIST(len == disp->mgr->buffersize);
1093*00b67f09SDavid van Moolenbroek 		disp->mgr->buffers--;
1094*00b67f09SDavid van Moolenbroek 		bpool = disp->mgr->bpool;
1095*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->mgr->buffer_lock);
1096*00b67f09SDavid van Moolenbroek 		isc_mempool_put(bpool, buf);
1097*00b67f09SDavid van Moolenbroek 		break;
1098*00b67f09SDavid van Moolenbroek 	default:
1099*00b67f09SDavid van Moolenbroek 		INSIST(0);
1100*00b67f09SDavid van Moolenbroek 		break;
1101*00b67f09SDavid van Moolenbroek 	}
1102*00b67f09SDavid van Moolenbroek }
1103*00b67f09SDavid van Moolenbroek 
1104*00b67f09SDavid van Moolenbroek static void *
allocate_udp_buffer(dns_dispatch_t * disp)1105*00b67f09SDavid van Moolenbroek allocate_udp_buffer(dns_dispatch_t *disp) {
1106*00b67f09SDavid van Moolenbroek 	isc_mempool_t *bpool;
1107*00b67f09SDavid van Moolenbroek 	void *temp;
1108*00b67f09SDavid van Moolenbroek 
1109*00b67f09SDavid van Moolenbroek 	LOCK(&disp->mgr->buffer_lock);
1110*00b67f09SDavid van Moolenbroek 	bpool = disp->mgr->bpool;
1111*00b67f09SDavid van Moolenbroek 	disp->mgr->buffers++;
1112*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->mgr->buffer_lock);
1113*00b67f09SDavid van Moolenbroek 
1114*00b67f09SDavid van Moolenbroek 	temp = isc_mempool_get(bpool);
1115*00b67f09SDavid van Moolenbroek 
1116*00b67f09SDavid van Moolenbroek 	if (temp == NULL) {
1117*00b67f09SDavid van Moolenbroek 		LOCK(&disp->mgr->buffer_lock);
1118*00b67f09SDavid van Moolenbroek 		disp->mgr->buffers--;
1119*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->mgr->buffer_lock);
1120*00b67f09SDavid van Moolenbroek 	}
1121*00b67f09SDavid van Moolenbroek 
1122*00b67f09SDavid van Moolenbroek 	return (temp);
1123*00b67f09SDavid van Moolenbroek }
1124*00b67f09SDavid van Moolenbroek 
1125*00b67f09SDavid van Moolenbroek static inline void
free_sevent(isc_event_t * ev)1126*00b67f09SDavid van Moolenbroek free_sevent(isc_event_t *ev) {
1127*00b67f09SDavid van Moolenbroek 	isc_mempool_t *pool = ev->ev_destroy_arg;
1128*00b67f09SDavid van Moolenbroek 	isc_socketevent_t *sev = (isc_socketevent_t *) ev;
1129*00b67f09SDavid van Moolenbroek 	isc_mempool_put(pool, sev);
1130*00b67f09SDavid van Moolenbroek }
1131*00b67f09SDavid van Moolenbroek 
1132*00b67f09SDavid van Moolenbroek static inline isc_socketevent_t *
allocate_sevent(dns_dispatch_t * disp,isc_socket_t * socket,isc_eventtype_t type,isc_taskaction_t action,const void * arg)1133*00b67f09SDavid van Moolenbroek allocate_sevent(dns_dispatch_t *disp, isc_socket_t *socket,
1134*00b67f09SDavid van Moolenbroek 		isc_eventtype_t type, isc_taskaction_t action, const void *arg)
1135*00b67f09SDavid van Moolenbroek {
1136*00b67f09SDavid van Moolenbroek 	isc_socketevent_t *ev;
1137*00b67f09SDavid van Moolenbroek 	void *deconst_arg;
1138*00b67f09SDavid van Moolenbroek 
1139*00b67f09SDavid van Moolenbroek 	ev = isc_mempool_get(disp->sepool);
1140*00b67f09SDavid van Moolenbroek 	if (ev == NULL)
1141*00b67f09SDavid van Moolenbroek 		return (NULL);
1142*00b67f09SDavid van Moolenbroek 	DE_CONST(arg, deconst_arg);
1143*00b67f09SDavid van Moolenbroek 	ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, type,
1144*00b67f09SDavid van Moolenbroek 		       action, deconst_arg, socket,
1145*00b67f09SDavid van Moolenbroek 		       free_sevent, disp->sepool);
1146*00b67f09SDavid van Moolenbroek 	ev->result = ISC_R_UNSET;
1147*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(ev, ev_link);
1148*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(ev->bufferlist);
1149*00b67f09SDavid van Moolenbroek 	ev->region.base = NULL;
1150*00b67f09SDavid van Moolenbroek 	ev->n = 0;
1151*00b67f09SDavid van Moolenbroek 	ev->offset = 0;
1152*00b67f09SDavid van Moolenbroek 	ev->attributes = 0;
1153*00b67f09SDavid van Moolenbroek 
1154*00b67f09SDavid van Moolenbroek 	return (ev);
1155*00b67f09SDavid van Moolenbroek }
1156*00b67f09SDavid van Moolenbroek 
1157*00b67f09SDavid van Moolenbroek 
1158*00b67f09SDavid van Moolenbroek static inline void
free_devent(dns_dispatch_t * disp,dns_dispatchevent_t * ev)1159*00b67f09SDavid van Moolenbroek free_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev) {
1160*00b67f09SDavid van Moolenbroek 	if (disp->failsafe_ev == ev) {
1161*00b67f09SDavid van Moolenbroek 		INSIST(disp->shutdown_out == 1);
1162*00b67f09SDavid van Moolenbroek 		disp->shutdown_out = 0;
1163*00b67f09SDavid van Moolenbroek 
1164*00b67f09SDavid van Moolenbroek 		return;
1165*00b67f09SDavid van Moolenbroek 	}
1166*00b67f09SDavid van Moolenbroek 
1167*00b67f09SDavid van Moolenbroek 	isc_mempool_put(disp->mgr->depool, ev);
1168*00b67f09SDavid van Moolenbroek }
1169*00b67f09SDavid van Moolenbroek 
1170*00b67f09SDavid van Moolenbroek static inline dns_dispatchevent_t *
allocate_devent(dns_dispatch_t * disp)1171*00b67f09SDavid van Moolenbroek allocate_devent(dns_dispatch_t *disp) {
1172*00b67f09SDavid van Moolenbroek 	dns_dispatchevent_t *ev;
1173*00b67f09SDavid van Moolenbroek 
1174*00b67f09SDavid van Moolenbroek 	ev = isc_mempool_get(disp->mgr->depool);
1175*00b67f09SDavid van Moolenbroek 	if (ev == NULL)
1176*00b67f09SDavid van Moolenbroek 		return (NULL);
1177*00b67f09SDavid van Moolenbroek 	ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, 0,
1178*00b67f09SDavid van Moolenbroek 		       NULL, NULL, NULL, NULL, NULL);
1179*00b67f09SDavid van Moolenbroek 
1180*00b67f09SDavid van Moolenbroek 	return (ev);
1181*00b67f09SDavid van Moolenbroek }
1182*00b67f09SDavid van Moolenbroek 
1183*00b67f09SDavid van Moolenbroek static void
udp_exrecv(isc_task_t * task,isc_event_t * ev)1184*00b67f09SDavid van Moolenbroek udp_exrecv(isc_task_t *task, isc_event_t *ev) {
1185*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsock = ev->ev_arg;
1186*00b67f09SDavid van Moolenbroek 
1187*00b67f09SDavid van Moolenbroek 	UNUSED(task);
1188*00b67f09SDavid van Moolenbroek 
1189*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPSOCK(dispsock));
1190*00b67f09SDavid van Moolenbroek 	udp_recv(ev, dispsock->disp, dispsock);
1191*00b67f09SDavid van Moolenbroek }
1192*00b67f09SDavid van Moolenbroek 
1193*00b67f09SDavid van Moolenbroek static void
udp_shrecv(isc_task_t * task,isc_event_t * ev)1194*00b67f09SDavid van Moolenbroek udp_shrecv(isc_task_t *task, isc_event_t *ev) {
1195*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp = ev->ev_arg;
1196*00b67f09SDavid van Moolenbroek 
1197*00b67f09SDavid van Moolenbroek 	UNUSED(task);
1198*00b67f09SDavid van Moolenbroek 
1199*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
1200*00b67f09SDavid van Moolenbroek 	udp_recv(ev, disp, NULL);
1201*00b67f09SDavid van Moolenbroek }
1202*00b67f09SDavid van Moolenbroek 
1203*00b67f09SDavid van Moolenbroek /*
1204*00b67f09SDavid van Moolenbroek  * General flow:
1205*00b67f09SDavid van Moolenbroek  *
1206*00b67f09SDavid van Moolenbroek  * If I/O result == CANCELED or error, free the buffer.
1207*00b67f09SDavid van Moolenbroek  *
1208*00b67f09SDavid van Moolenbroek  * If query, free the buffer, restart.
1209*00b67f09SDavid van Moolenbroek  *
1210*00b67f09SDavid van Moolenbroek  * If response:
1211*00b67f09SDavid van Moolenbroek  *	Allocate event, fill in details.
1212*00b67f09SDavid van Moolenbroek  *		If cannot allocate, free buffer, restart.
1213*00b67f09SDavid van Moolenbroek  *	find target.  If not found, free buffer, restart.
1214*00b67f09SDavid van Moolenbroek  *	if event queue is not empty, queue.  else, send.
1215*00b67f09SDavid van Moolenbroek  *	restart.
1216*00b67f09SDavid van Moolenbroek  */
1217*00b67f09SDavid van Moolenbroek static void
udp_recv(isc_event_t * ev_in,dns_dispatch_t * disp,dispsocket_t * dispsock)1218*00b67f09SDavid van Moolenbroek udp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) {
1219*00b67f09SDavid van Moolenbroek 	isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
1220*00b67f09SDavid van Moolenbroek 	dns_messageid_t id;
1221*00b67f09SDavid van Moolenbroek 	isc_result_t dres;
1222*00b67f09SDavid van Moolenbroek 	isc_buffer_t source;
1223*00b67f09SDavid van Moolenbroek 	unsigned int flags;
1224*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *resp = NULL;
1225*00b67f09SDavid van Moolenbroek 	dns_dispatchevent_t *rev;
1226*00b67f09SDavid van Moolenbroek 	unsigned int bucket;
1227*00b67f09SDavid van Moolenbroek 	isc_boolean_t killit;
1228*00b67f09SDavid van Moolenbroek 	isc_boolean_t queue_response;
1229*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
1230*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
1231*00b67f09SDavid van Moolenbroek 	isc_netaddr_t netaddr;
1232*00b67f09SDavid van Moolenbroek 	int match;
1233*00b67f09SDavid van Moolenbroek 	int result;
1234*00b67f09SDavid van Moolenbroek 	isc_boolean_t qidlocked = ISC_FALSE;
1235*00b67f09SDavid van Moolenbroek 
1236*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
1237*00b67f09SDavid van Moolenbroek 
1238*00b67f09SDavid van Moolenbroek 	mgr = disp->mgr;
1239*00b67f09SDavid van Moolenbroek 	qid = mgr->qid;
1240*00b67f09SDavid van Moolenbroek 
1241*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90),
1242*00b67f09SDavid van Moolenbroek 		     "got packet: requests %d, buffers %d, recvs %d",
1243*00b67f09SDavid van Moolenbroek 		     disp->requests, disp->mgr->buffers, disp->recv_pending);
1244*00b67f09SDavid van Moolenbroek 
1245*00b67f09SDavid van Moolenbroek 	if (dispsock == NULL && ev->ev_type == ISC_SOCKEVENT_RECVDONE) {
1246*00b67f09SDavid van Moolenbroek 		/*
1247*00b67f09SDavid van Moolenbroek 		 * Unless the receive event was imported from a listening
1248*00b67f09SDavid van Moolenbroek 		 * interface, in which case the event type is
1249*00b67f09SDavid van Moolenbroek 		 * DNS_EVENT_IMPORTRECVDONE, receive operation must be pending.
1250*00b67f09SDavid van Moolenbroek 		 */
1251*00b67f09SDavid van Moolenbroek 		INSIST(disp->recv_pending != 0);
1252*00b67f09SDavid van Moolenbroek 		disp->recv_pending = 0;
1253*00b67f09SDavid van Moolenbroek 	}
1254*00b67f09SDavid van Moolenbroek 
1255*00b67f09SDavid van Moolenbroek 	if (dispsock != NULL &&
1256*00b67f09SDavid van Moolenbroek 	    (ev->result == ISC_R_CANCELED || dispsock->resp == NULL)) {
1257*00b67f09SDavid van Moolenbroek 		/*
1258*00b67f09SDavid van Moolenbroek 		 * dispsock->resp can be NULL if this transaction was canceled
1259*00b67f09SDavid van Moolenbroek 		 * just after receiving a response.  Since this socket is
1260*00b67f09SDavid van Moolenbroek 		 * exclusively used and there should be at most one receive
1261*00b67f09SDavid van Moolenbroek 		 * event the canceled event should have been no effect.  So
1262*00b67f09SDavid van Moolenbroek 		 * we can (and should) deactivate the socket right now.
1263*00b67f09SDavid van Moolenbroek 		 */
1264*00b67f09SDavid van Moolenbroek 		deactivate_dispsocket(disp, dispsock);
1265*00b67f09SDavid van Moolenbroek 		dispsock = NULL;
1266*00b67f09SDavid van Moolenbroek 	}
1267*00b67f09SDavid van Moolenbroek 
1268*00b67f09SDavid van Moolenbroek 	if (disp->shutting_down) {
1269*00b67f09SDavid van Moolenbroek 		/*
1270*00b67f09SDavid van Moolenbroek 		 * This dispatcher is shutting down.
1271*00b67f09SDavid van Moolenbroek 		 */
1272*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1273*00b67f09SDavid van Moolenbroek 
1274*00b67f09SDavid van Moolenbroek 		isc_event_free(&ev_in);
1275*00b67f09SDavid van Moolenbroek 		ev = NULL;
1276*00b67f09SDavid van Moolenbroek 
1277*00b67f09SDavid van Moolenbroek 		killit = destroy_disp_ok(disp);
1278*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
1279*00b67f09SDavid van Moolenbroek 		if (killit)
1280*00b67f09SDavid van Moolenbroek 			isc_task_send(disp->task[0], &disp->ctlevent);
1281*00b67f09SDavid van Moolenbroek 
1282*00b67f09SDavid van Moolenbroek 		return;
1283*00b67f09SDavid van Moolenbroek 	}
1284*00b67f09SDavid van Moolenbroek 
1285*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
1286*00b67f09SDavid van Moolenbroek 		if (dispsock != NULL) {
1287*00b67f09SDavid van Moolenbroek 			resp = dispsock->resp;
1288*00b67f09SDavid van Moolenbroek 			id = resp->id;
1289*00b67f09SDavid van Moolenbroek 			if (ev->result != ISC_R_SUCCESS) {
1290*00b67f09SDavid van Moolenbroek 				/*
1291*00b67f09SDavid van Moolenbroek 				 * This is most likely a network error on a
1292*00b67f09SDavid van Moolenbroek 				 * connected socket.  It makes no sense to
1293*00b67f09SDavid van Moolenbroek 				 * check the address or parse the packet, but it
1294*00b67f09SDavid van Moolenbroek 				 * will help to return the error to the caller.
1295*00b67f09SDavid van Moolenbroek 				 */
1296*00b67f09SDavid van Moolenbroek 				goto sendresponse;
1297*00b67f09SDavid van Moolenbroek 			}
1298*00b67f09SDavid van Moolenbroek 		} else {
1299*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->region.base, ev->region.length);
1300*00b67f09SDavid van Moolenbroek 
1301*00b67f09SDavid van Moolenbroek 			isc_event_free(&ev_in);
1302*00b67f09SDavid van Moolenbroek 			UNLOCK(&disp->lock);
1303*00b67f09SDavid van Moolenbroek 			return;
1304*00b67f09SDavid van Moolenbroek 		}
1305*00b67f09SDavid van Moolenbroek 	} else if (ev->result != ISC_R_SUCCESS) {
1306*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1307*00b67f09SDavid van Moolenbroek 
1308*00b67f09SDavid van Moolenbroek 		if (ev->result != ISC_R_CANCELED)
1309*00b67f09SDavid van Moolenbroek 			dispatch_log(disp, ISC_LOG_ERROR,
1310*00b67f09SDavid van Moolenbroek 				     "odd socket result in udp_recv(): %s",
1311*00b67f09SDavid van Moolenbroek 				     isc_result_totext(ev->result));
1312*00b67f09SDavid van Moolenbroek 
1313*00b67f09SDavid van Moolenbroek 		isc_event_free(&ev_in);
1314*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
1315*00b67f09SDavid van Moolenbroek 		return;
1316*00b67f09SDavid van Moolenbroek 	}
1317*00b67f09SDavid van Moolenbroek 
1318*00b67f09SDavid van Moolenbroek 	/*
1319*00b67f09SDavid van Moolenbroek 	 * If this is from a blackholed address, drop it.
1320*00b67f09SDavid van Moolenbroek 	 */
1321*00b67f09SDavid van Moolenbroek 	isc_netaddr_fromsockaddr(&netaddr, &ev->address);
1322*00b67f09SDavid van Moolenbroek 	if (disp->mgr->blackhole != NULL &&
1323*00b67f09SDavid van Moolenbroek 	    dns_acl_match(&netaddr, NULL, disp->mgr->blackhole,
1324*00b67f09SDavid van Moolenbroek 			  NULL, &match, NULL) == ISC_R_SUCCESS &&
1325*00b67f09SDavid van Moolenbroek 	    match > 0)
1326*00b67f09SDavid van Moolenbroek 	{
1327*00b67f09SDavid van Moolenbroek 		if (isc_log_wouldlog(dns_lctx, LVL(10))) {
1328*00b67f09SDavid van Moolenbroek 			char netaddrstr[ISC_NETADDR_FORMATSIZE];
1329*00b67f09SDavid van Moolenbroek 			isc_netaddr_format(&netaddr, netaddrstr,
1330*00b67f09SDavid van Moolenbroek 					   sizeof(netaddrstr));
1331*00b67f09SDavid van Moolenbroek 			dispatch_log(disp, LVL(10),
1332*00b67f09SDavid van Moolenbroek 				     "blackholed packet from %s",
1333*00b67f09SDavid van Moolenbroek 				     netaddrstr);
1334*00b67f09SDavid van Moolenbroek 		}
1335*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1336*00b67f09SDavid van Moolenbroek 		goto restart;
1337*00b67f09SDavid van Moolenbroek 	}
1338*00b67f09SDavid van Moolenbroek 
1339*00b67f09SDavid van Moolenbroek 	/*
1340*00b67f09SDavid van Moolenbroek 	 * Peek into the buffer to see what we can see.
1341*00b67f09SDavid van Moolenbroek 	 */
1342*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&source, ev->region.base, ev->region.length);
1343*00b67f09SDavid van Moolenbroek 	isc_buffer_add(&source, ev->n);
1344*00b67f09SDavid van Moolenbroek 	dres = dns_message_peekheader(&source, &id, &flags);
1345*00b67f09SDavid van Moolenbroek 	if (dres != ISC_R_SUCCESS) {
1346*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1347*00b67f09SDavid van Moolenbroek 		dispatch_log(disp, LVL(10), "got garbage packet");
1348*00b67f09SDavid van Moolenbroek 		goto restart;
1349*00b67f09SDavid van Moolenbroek 	}
1350*00b67f09SDavid van Moolenbroek 
1351*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(92),
1352*00b67f09SDavid van Moolenbroek 		     "got valid DNS message header, /QR %c, id %u",
1353*00b67f09SDavid van Moolenbroek 		     ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id);
1354*00b67f09SDavid van Moolenbroek 
1355*00b67f09SDavid van Moolenbroek 	/*
1356*00b67f09SDavid van Moolenbroek 	 * Look at flags.  If query, drop it. If response,
1357*00b67f09SDavid van Moolenbroek 	 * look to see where it goes.
1358*00b67f09SDavid van Moolenbroek 	 */
1359*00b67f09SDavid van Moolenbroek 	if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
1360*00b67f09SDavid van Moolenbroek 		/* query */
1361*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1362*00b67f09SDavid van Moolenbroek 		goto restart;
1363*00b67f09SDavid van Moolenbroek 	}
1364*00b67f09SDavid van Moolenbroek 
1365*00b67f09SDavid van Moolenbroek 	/*
1366*00b67f09SDavid van Moolenbroek 	 * Search for the corresponding response.  If we are using an exclusive
1367*00b67f09SDavid van Moolenbroek 	 * socket, we've already identified it and we can skip the search; but
1368*00b67f09SDavid van Moolenbroek 	 * the ID and the address must match the expected ones.
1369*00b67f09SDavid van Moolenbroek 	 */
1370*00b67f09SDavid van Moolenbroek 	if (resp == NULL) {
1371*00b67f09SDavid van Moolenbroek 		bucket = dns_hash(qid, &ev->address, id, disp->localport);
1372*00b67f09SDavid van Moolenbroek 		LOCK(&qid->lock);
1373*00b67f09SDavid van Moolenbroek 		qidlocked = ISC_TRUE;
1374*00b67f09SDavid van Moolenbroek 		resp = entry_search(qid, &ev->address, id, disp->localport,
1375*00b67f09SDavid van Moolenbroek 				    bucket);
1376*00b67f09SDavid van Moolenbroek 		dispatch_log(disp, LVL(90),
1377*00b67f09SDavid van Moolenbroek 			     "search for response in bucket %d: %s",
1378*00b67f09SDavid van Moolenbroek 			     bucket, (resp == NULL ? "not found" : "found"));
1379*00b67f09SDavid van Moolenbroek 
1380*00b67f09SDavid van Moolenbroek 		if (resp == NULL) {
1381*00b67f09SDavid van Moolenbroek 			inc_stats(mgr, dns_resstatscounter_mismatch);
1382*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->region.base, ev->region.length);
1383*00b67f09SDavid van Moolenbroek 			goto unlock;
1384*00b67f09SDavid van Moolenbroek 		}
1385*00b67f09SDavid van Moolenbroek 	} else if (resp->id != id || !isc_sockaddr_equal(&ev->address,
1386*00b67f09SDavid van Moolenbroek 							 &resp->host)) {
1387*00b67f09SDavid van Moolenbroek 		dispatch_log(disp, LVL(90),
1388*00b67f09SDavid van Moolenbroek 			     "response to an exclusive socket doesn't match");
1389*00b67f09SDavid van Moolenbroek 		inc_stats(mgr, dns_resstatscounter_mismatch);
1390*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1391*00b67f09SDavid van Moolenbroek 		goto unlock;
1392*00b67f09SDavid van Moolenbroek 	}
1393*00b67f09SDavid van Moolenbroek 
1394*00b67f09SDavid van Moolenbroek 	/*
1395*00b67f09SDavid van Moolenbroek 	 * Now that we have the original dispatch the query was sent
1396*00b67f09SDavid van Moolenbroek 	 * from check that the address and port the response was
1397*00b67f09SDavid van Moolenbroek 	 * sent to make sense.
1398*00b67f09SDavid van Moolenbroek 	 */
1399*00b67f09SDavid van Moolenbroek 	if (disp != resp->disp) {
1400*00b67f09SDavid van Moolenbroek 		isc_sockaddr_t a1;
1401*00b67f09SDavid van Moolenbroek 		isc_sockaddr_t a2;
1402*00b67f09SDavid van Moolenbroek 
1403*00b67f09SDavid van Moolenbroek 		/*
1404*00b67f09SDavid van Moolenbroek 		 * Check that the socket types and ports match.
1405*00b67f09SDavid van Moolenbroek 		 */
1406*00b67f09SDavid van Moolenbroek 		if (disp->socktype != resp->disp->socktype ||
1407*00b67f09SDavid van Moolenbroek 		    isc_sockaddr_getport(&disp->local) !=
1408*00b67f09SDavid van Moolenbroek 		    isc_sockaddr_getport(&resp->disp->local)) {
1409*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->region.base, ev->region.length);
1410*00b67f09SDavid van Moolenbroek 			goto unlock;
1411*00b67f09SDavid van Moolenbroek 		}
1412*00b67f09SDavid van Moolenbroek 
1413*00b67f09SDavid van Moolenbroek 		/*
1414*00b67f09SDavid van Moolenbroek 		 * If each dispatch is bound to a different address
1415*00b67f09SDavid van Moolenbroek 		 * then fail.
1416*00b67f09SDavid van Moolenbroek 		 *
1417*00b67f09SDavid van Moolenbroek 		 * Note under Linux a packet can be sent out via IPv4 socket
1418*00b67f09SDavid van Moolenbroek 		 * and the response be received via a IPv6 socket.
1419*00b67f09SDavid van Moolenbroek 		 *
1420*00b67f09SDavid van Moolenbroek 		 * Requests sent out via IPv6 should always come back in
1421*00b67f09SDavid van Moolenbroek 		 * via IPv6.
1422*00b67f09SDavid van Moolenbroek 		 */
1423*00b67f09SDavid van Moolenbroek 		if (isc_sockaddr_pf(&resp->disp->local) == PF_INET6 &&
1424*00b67f09SDavid van Moolenbroek 		    isc_sockaddr_pf(&disp->local) != PF_INET6) {
1425*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->region.base, ev->region.length);
1426*00b67f09SDavid van Moolenbroek 			goto unlock;
1427*00b67f09SDavid van Moolenbroek 		}
1428*00b67f09SDavid van Moolenbroek 		isc_sockaddr_anyofpf(&a1, isc_sockaddr_pf(&resp->disp->local));
1429*00b67f09SDavid van Moolenbroek 		isc_sockaddr_anyofpf(&a2, isc_sockaddr_pf(&disp->local));
1430*00b67f09SDavid van Moolenbroek 		if (!isc_sockaddr_eqaddr(&disp->local, &resp->disp->local) &&
1431*00b67f09SDavid van Moolenbroek 		    !isc_sockaddr_eqaddr(&a1, &resp->disp->local) &&
1432*00b67f09SDavid van Moolenbroek 		    !isc_sockaddr_eqaddr(&a2, &disp->local)) {
1433*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->region.base, ev->region.length);
1434*00b67f09SDavid van Moolenbroek 			goto unlock;
1435*00b67f09SDavid van Moolenbroek 		}
1436*00b67f09SDavid van Moolenbroek 	}
1437*00b67f09SDavid van Moolenbroek 
1438*00b67f09SDavid van Moolenbroek   sendresponse:
1439*00b67f09SDavid van Moolenbroek 	queue_response = resp->item_out;
1440*00b67f09SDavid van Moolenbroek 	rev = allocate_devent(resp->disp);
1441*00b67f09SDavid van Moolenbroek 	if (rev == NULL) {
1442*00b67f09SDavid van Moolenbroek 		free_buffer(disp, ev->region.base, ev->region.length);
1443*00b67f09SDavid van Moolenbroek 		goto unlock;
1444*00b67f09SDavid van Moolenbroek 	}
1445*00b67f09SDavid van Moolenbroek 
1446*00b67f09SDavid van Moolenbroek 	/*
1447*00b67f09SDavid van Moolenbroek 	 * At this point, rev contains the event we want to fill in, and
1448*00b67f09SDavid van Moolenbroek 	 * resp contains the information on the place to send it to.
1449*00b67f09SDavid van Moolenbroek 	 * Send the event off.
1450*00b67f09SDavid van Moolenbroek 	 */
1451*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&rev->buffer, ev->region.base, ev->region.length);
1452*00b67f09SDavid van Moolenbroek 	isc_buffer_add(&rev->buffer, ev->n);
1453*00b67f09SDavid van Moolenbroek 	rev->result = ev->result;
1454*00b67f09SDavid van Moolenbroek 	rev->id = id;
1455*00b67f09SDavid van Moolenbroek 	rev->addr = ev->address;
1456*00b67f09SDavid van Moolenbroek 	rev->pktinfo = ev->pktinfo;
1457*00b67f09SDavid van Moolenbroek 	rev->attributes = ev->attributes;
1458*00b67f09SDavid van Moolenbroek 	if (queue_response) {
1459*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(resp->items, rev, ev_link);
1460*00b67f09SDavid van Moolenbroek 	} else {
1461*00b67f09SDavid van Moolenbroek 		ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL,
1462*00b67f09SDavid van Moolenbroek 			       DNS_EVENT_DISPATCH,
1463*00b67f09SDavid van Moolenbroek 			       resp->action, resp->arg, resp, NULL, NULL);
1464*00b67f09SDavid van Moolenbroek 		request_log(disp, resp, LVL(90),
1465*00b67f09SDavid van Moolenbroek 			    "[a] Sent event %p buffer %p len %d to task %p",
1466*00b67f09SDavid van Moolenbroek 			    rev, rev->buffer.base, rev->buffer.length,
1467*00b67f09SDavid van Moolenbroek 			    resp->task);
1468*00b67f09SDavid van Moolenbroek 		resp->item_out = ISC_TRUE;
1469*00b67f09SDavid van Moolenbroek 		isc_task_send(resp->task, ISC_EVENT_PTR(&rev));
1470*00b67f09SDavid van Moolenbroek 	}
1471*00b67f09SDavid van Moolenbroek  unlock:
1472*00b67f09SDavid van Moolenbroek 	if (qidlocked)
1473*00b67f09SDavid van Moolenbroek 		UNLOCK(&qid->lock);
1474*00b67f09SDavid van Moolenbroek 
1475*00b67f09SDavid van Moolenbroek 	/*
1476*00b67f09SDavid van Moolenbroek 	 * Restart recv() to get the next packet.
1477*00b67f09SDavid van Moolenbroek 	 */
1478*00b67f09SDavid van Moolenbroek  restart:
1479*00b67f09SDavid van Moolenbroek 	result = startrecv(disp, dispsock);
1480*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && dispsock != NULL) {
1481*00b67f09SDavid van Moolenbroek 		/*
1482*00b67f09SDavid van Moolenbroek 		 * XXX: wired. There seems to be no recovery process other than
1483*00b67f09SDavid van Moolenbroek 		 * deactivate this socket anyway (since we cannot start
1484*00b67f09SDavid van Moolenbroek 		 * receiving, we won't be able to receive a cancel event
1485*00b67f09SDavid van Moolenbroek 		 * from the user).
1486*00b67f09SDavid van Moolenbroek 		 */
1487*00b67f09SDavid van Moolenbroek 		deactivate_dispsocket(disp, dispsock);
1488*00b67f09SDavid van Moolenbroek 	}
1489*00b67f09SDavid van Moolenbroek 	isc_event_free(&ev_in);
1490*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
1491*00b67f09SDavid van Moolenbroek }
1492*00b67f09SDavid van Moolenbroek 
1493*00b67f09SDavid van Moolenbroek /*
1494*00b67f09SDavid van Moolenbroek  * General flow:
1495*00b67f09SDavid van Moolenbroek  *
1496*00b67f09SDavid van Moolenbroek  * If I/O result == CANCELED, EOF, or error, notify everyone as the
1497*00b67f09SDavid van Moolenbroek  * various queues drain.
1498*00b67f09SDavid van Moolenbroek  *
1499*00b67f09SDavid van Moolenbroek  * If query, restart.
1500*00b67f09SDavid van Moolenbroek  *
1501*00b67f09SDavid van Moolenbroek  * If response:
1502*00b67f09SDavid van Moolenbroek  *	Allocate event, fill in details.
1503*00b67f09SDavid van Moolenbroek  *		If cannot allocate, restart.
1504*00b67f09SDavid van Moolenbroek  *	find target.  If not found, restart.
1505*00b67f09SDavid van Moolenbroek  *	if event queue is not empty, queue.  else, send.
1506*00b67f09SDavid van Moolenbroek  *	restart.
1507*00b67f09SDavid van Moolenbroek  */
1508*00b67f09SDavid van Moolenbroek static void
tcp_recv(isc_task_t * task,isc_event_t * ev_in)1509*00b67f09SDavid van Moolenbroek tcp_recv(isc_task_t *task, isc_event_t *ev_in) {
1510*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp = ev_in->ev_arg;
1511*00b67f09SDavid van Moolenbroek 	dns_tcpmsg_t *tcpmsg = &disp->tcpmsg;
1512*00b67f09SDavid van Moolenbroek 	dns_messageid_t id;
1513*00b67f09SDavid van Moolenbroek 	isc_result_t dres;
1514*00b67f09SDavid van Moolenbroek 	unsigned int flags;
1515*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *resp;
1516*00b67f09SDavid van Moolenbroek 	dns_dispatchevent_t *rev;
1517*00b67f09SDavid van Moolenbroek 	unsigned int bucket;
1518*00b67f09SDavid van Moolenbroek 	isc_boolean_t killit;
1519*00b67f09SDavid van Moolenbroek 	isc_boolean_t queue_response;
1520*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
1521*00b67f09SDavid van Moolenbroek 	int level;
1522*00b67f09SDavid van Moolenbroek 	char buf[ISC_SOCKADDR_FORMATSIZE];
1523*00b67f09SDavid van Moolenbroek 
1524*00b67f09SDavid van Moolenbroek 	UNUSED(task);
1525*00b67f09SDavid van Moolenbroek 
1526*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
1527*00b67f09SDavid van Moolenbroek 
1528*00b67f09SDavid van Moolenbroek 	qid = disp->qid;
1529*00b67f09SDavid van Moolenbroek 
1530*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90),
1531*00b67f09SDavid van Moolenbroek 		     "got TCP packet: requests %d, buffers %d, recvs %d",
1532*00b67f09SDavid van Moolenbroek 		     disp->requests, disp->tcpbuffers, disp->recv_pending);
1533*00b67f09SDavid van Moolenbroek 
1534*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
1535*00b67f09SDavid van Moolenbroek 
1536*00b67f09SDavid van Moolenbroek 	INSIST(disp->recv_pending != 0);
1537*00b67f09SDavid van Moolenbroek 	disp->recv_pending = 0;
1538*00b67f09SDavid van Moolenbroek 
1539*00b67f09SDavid van Moolenbroek 	if (disp->refcount == 0) {
1540*00b67f09SDavid van Moolenbroek 		/*
1541*00b67f09SDavid van Moolenbroek 		 * This dispatcher is shutting down.  Force cancelation.
1542*00b67f09SDavid van Moolenbroek 		 */
1543*00b67f09SDavid van Moolenbroek 		tcpmsg->result = ISC_R_CANCELED;
1544*00b67f09SDavid van Moolenbroek 	}
1545*00b67f09SDavid van Moolenbroek 
1546*00b67f09SDavid van Moolenbroek 	if (tcpmsg->result != ISC_R_SUCCESS) {
1547*00b67f09SDavid van Moolenbroek 		switch (tcpmsg->result) {
1548*00b67f09SDavid van Moolenbroek 		case ISC_R_CANCELED:
1549*00b67f09SDavid van Moolenbroek 			break;
1550*00b67f09SDavid van Moolenbroek 
1551*00b67f09SDavid van Moolenbroek 		case ISC_R_EOF:
1552*00b67f09SDavid van Moolenbroek 			dispatch_log(disp, LVL(90), "shutting down on EOF");
1553*00b67f09SDavid van Moolenbroek 			do_cancel(disp);
1554*00b67f09SDavid van Moolenbroek 			break;
1555*00b67f09SDavid van Moolenbroek 
1556*00b67f09SDavid van Moolenbroek 		case ISC_R_CONNECTIONRESET:
1557*00b67f09SDavid van Moolenbroek 			level = ISC_LOG_INFO;
1558*00b67f09SDavid van Moolenbroek 			goto logit;
1559*00b67f09SDavid van Moolenbroek 
1560*00b67f09SDavid van Moolenbroek 		default:
1561*00b67f09SDavid van Moolenbroek 			level = ISC_LOG_ERROR;
1562*00b67f09SDavid van Moolenbroek 		logit:
1563*00b67f09SDavid van Moolenbroek 			isc_sockaddr_format(&tcpmsg->address, buf, sizeof(buf));
1564*00b67f09SDavid van Moolenbroek 			dispatch_log(disp, level, "shutting down due to TCP "
1565*00b67f09SDavid van Moolenbroek 				     "receive error: %s: %s", buf,
1566*00b67f09SDavid van Moolenbroek 				     isc_result_totext(tcpmsg->result));
1567*00b67f09SDavid van Moolenbroek 			do_cancel(disp);
1568*00b67f09SDavid van Moolenbroek 			break;
1569*00b67f09SDavid van Moolenbroek 		}
1570*00b67f09SDavid van Moolenbroek 
1571*00b67f09SDavid van Moolenbroek 		/*
1572*00b67f09SDavid van Moolenbroek 		 * The event is statically allocated in the tcpmsg
1573*00b67f09SDavid van Moolenbroek 		 * structure, and destroy_disp() frees the tcpmsg, so we must
1574*00b67f09SDavid van Moolenbroek 		 * free the event *before* calling destroy_disp().
1575*00b67f09SDavid van Moolenbroek 		 */
1576*00b67f09SDavid van Moolenbroek 		isc_event_free(&ev_in);
1577*00b67f09SDavid van Moolenbroek 
1578*00b67f09SDavid van Moolenbroek 		disp->shutting_down = 1;
1579*00b67f09SDavid van Moolenbroek 		disp->shutdown_why = tcpmsg->result;
1580*00b67f09SDavid van Moolenbroek 
1581*00b67f09SDavid van Moolenbroek 		/*
1582*00b67f09SDavid van Moolenbroek 		 * If the recv() was canceled pass the word on.
1583*00b67f09SDavid van Moolenbroek 		 */
1584*00b67f09SDavid van Moolenbroek 		killit = destroy_disp_ok(disp);
1585*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
1586*00b67f09SDavid van Moolenbroek 		if (killit)
1587*00b67f09SDavid van Moolenbroek 			isc_task_send(disp->task[0], &disp->ctlevent);
1588*00b67f09SDavid van Moolenbroek 		return;
1589*00b67f09SDavid van Moolenbroek 	}
1590*00b67f09SDavid van Moolenbroek 
1591*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90), "result %d, length == %d, addr = %p",
1592*00b67f09SDavid van Moolenbroek 		     tcpmsg->result,
1593*00b67f09SDavid van Moolenbroek 		     tcpmsg->buffer.length, tcpmsg->buffer.base);
1594*00b67f09SDavid van Moolenbroek 
1595*00b67f09SDavid van Moolenbroek 	/*
1596*00b67f09SDavid van Moolenbroek 	 * Peek into the buffer to see what we can see.
1597*00b67f09SDavid van Moolenbroek 	 */
1598*00b67f09SDavid van Moolenbroek 	dres = dns_message_peekheader(&tcpmsg->buffer, &id, &flags);
1599*00b67f09SDavid van Moolenbroek 	if (dres != ISC_R_SUCCESS) {
1600*00b67f09SDavid van Moolenbroek 		dispatch_log(disp, LVL(10), "got garbage packet");
1601*00b67f09SDavid van Moolenbroek 		goto restart;
1602*00b67f09SDavid van Moolenbroek 	}
1603*00b67f09SDavid van Moolenbroek 
1604*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(92),
1605*00b67f09SDavid van Moolenbroek 		     "got valid DNS message header, /QR %c, id %u",
1606*00b67f09SDavid van Moolenbroek 		     ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id);
1607*00b67f09SDavid van Moolenbroek 
1608*00b67f09SDavid van Moolenbroek 	/*
1609*00b67f09SDavid van Moolenbroek 	 * Allocate an event to send to the query or response client, and
1610*00b67f09SDavid van Moolenbroek 	 * allocate a new buffer for our use.
1611*00b67f09SDavid van Moolenbroek 	 */
1612*00b67f09SDavid van Moolenbroek 
1613*00b67f09SDavid van Moolenbroek 	/*
1614*00b67f09SDavid van Moolenbroek 	 * Look at flags.  If query, drop it. If response,
1615*00b67f09SDavid van Moolenbroek 	 * look to see where it goes.
1616*00b67f09SDavid van Moolenbroek 	 */
1617*00b67f09SDavid van Moolenbroek 	if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
1618*00b67f09SDavid van Moolenbroek 		/*
1619*00b67f09SDavid van Moolenbroek 		 * Query.
1620*00b67f09SDavid van Moolenbroek 		 */
1621*00b67f09SDavid van Moolenbroek 		goto restart;
1622*00b67f09SDavid van Moolenbroek 	}
1623*00b67f09SDavid van Moolenbroek 
1624*00b67f09SDavid van Moolenbroek 	/*
1625*00b67f09SDavid van Moolenbroek 	 * Response.
1626*00b67f09SDavid van Moolenbroek 	 */
1627*00b67f09SDavid van Moolenbroek 	bucket = dns_hash(qid, &tcpmsg->address, id, disp->localport);
1628*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
1629*00b67f09SDavid van Moolenbroek 	resp = entry_search(qid, &tcpmsg->address, id, disp->localport, bucket);
1630*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90),
1631*00b67f09SDavid van Moolenbroek 		     "search for response in bucket %d: %s",
1632*00b67f09SDavid van Moolenbroek 		     bucket, (resp == NULL ? "not found" : "found"));
1633*00b67f09SDavid van Moolenbroek 
1634*00b67f09SDavid van Moolenbroek 	if (resp == NULL)
1635*00b67f09SDavid van Moolenbroek 		goto unlock;
1636*00b67f09SDavid van Moolenbroek 	queue_response = resp->item_out;
1637*00b67f09SDavid van Moolenbroek 	rev = allocate_devent(disp);
1638*00b67f09SDavid van Moolenbroek 	if (rev == NULL)
1639*00b67f09SDavid van Moolenbroek 		goto unlock;
1640*00b67f09SDavid van Moolenbroek 
1641*00b67f09SDavid van Moolenbroek 	/*
1642*00b67f09SDavid van Moolenbroek 	 * At this point, rev contains the event we want to fill in, and
1643*00b67f09SDavid van Moolenbroek 	 * resp contains the information on the place to send it to.
1644*00b67f09SDavid van Moolenbroek 	 * Send the event off.
1645*00b67f09SDavid van Moolenbroek 	 */
1646*00b67f09SDavid van Moolenbroek 	dns_tcpmsg_keepbuffer(tcpmsg, &rev->buffer);
1647*00b67f09SDavid van Moolenbroek 	disp->tcpbuffers++;
1648*00b67f09SDavid van Moolenbroek 	rev->result = ISC_R_SUCCESS;
1649*00b67f09SDavid van Moolenbroek 	rev->id = id;
1650*00b67f09SDavid van Moolenbroek 	rev->addr = tcpmsg->address;
1651*00b67f09SDavid van Moolenbroek 	if (queue_response) {
1652*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(resp->items, rev, ev_link);
1653*00b67f09SDavid van Moolenbroek 	} else {
1654*00b67f09SDavid van Moolenbroek 		ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL, DNS_EVENT_DISPATCH,
1655*00b67f09SDavid van Moolenbroek 			       resp->action, resp->arg, resp, NULL, NULL);
1656*00b67f09SDavid van Moolenbroek 		request_log(disp, resp, LVL(90),
1657*00b67f09SDavid van Moolenbroek 			    "[b] Sent event %p buffer %p len %d to task %p",
1658*00b67f09SDavid van Moolenbroek 			    rev, rev->buffer.base, rev->buffer.length,
1659*00b67f09SDavid van Moolenbroek 			    resp->task);
1660*00b67f09SDavid van Moolenbroek 		resp->item_out = ISC_TRUE;
1661*00b67f09SDavid van Moolenbroek 		isc_task_send(resp->task, ISC_EVENT_PTR(&rev));
1662*00b67f09SDavid van Moolenbroek 	}
1663*00b67f09SDavid van Moolenbroek  unlock:
1664*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
1665*00b67f09SDavid van Moolenbroek 
1666*00b67f09SDavid van Moolenbroek 	/*
1667*00b67f09SDavid van Moolenbroek 	 * Restart recv() to get the next packet.
1668*00b67f09SDavid van Moolenbroek 	 */
1669*00b67f09SDavid van Moolenbroek  restart:
1670*00b67f09SDavid van Moolenbroek 	(void)startrecv(disp, NULL);
1671*00b67f09SDavid van Moolenbroek 
1672*00b67f09SDavid van Moolenbroek 	isc_event_free(&ev_in);
1673*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
1674*00b67f09SDavid van Moolenbroek }
1675*00b67f09SDavid van Moolenbroek 
1676*00b67f09SDavid van Moolenbroek /*
1677*00b67f09SDavid van Moolenbroek  * disp must be locked.
1678*00b67f09SDavid van Moolenbroek  */
1679*00b67f09SDavid van Moolenbroek static isc_result_t
startrecv(dns_dispatch_t * disp,dispsocket_t * dispsock)1680*00b67f09SDavid van Moolenbroek startrecv(dns_dispatch_t *disp, dispsocket_t *dispsock) {
1681*00b67f09SDavid van Moolenbroek 	isc_result_t res;
1682*00b67f09SDavid van Moolenbroek 	isc_region_t region;
1683*00b67f09SDavid van Moolenbroek 	isc_socket_t *socket;
1684*00b67f09SDavid van Moolenbroek 
1685*00b67f09SDavid van Moolenbroek 	if (disp->shutting_down == 1)
1686*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1687*00b67f09SDavid van Moolenbroek 
1688*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0)
1689*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1690*00b67f09SDavid van Moolenbroek 
1691*00b67f09SDavid van Moolenbroek 	if (disp->recv_pending != 0 && dispsock == NULL)
1692*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1693*00b67f09SDavid van Moolenbroek 
1694*00b67f09SDavid van Moolenbroek 	if (disp->mgr->buffers >= disp->mgr->maxbuffers)
1695*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
1696*00b67f09SDavid van Moolenbroek 
1697*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0 &&
1698*00b67f09SDavid van Moolenbroek 	    dispsock == NULL)
1699*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1700*00b67f09SDavid van Moolenbroek 
1701*00b67f09SDavid van Moolenbroek 	if (dispsock != NULL)
1702*00b67f09SDavid van Moolenbroek 		socket = dispsock->socket;
1703*00b67f09SDavid van Moolenbroek 	else
1704*00b67f09SDavid van Moolenbroek 		socket = disp->socket;
1705*00b67f09SDavid van Moolenbroek 	INSIST(socket != NULL);
1706*00b67f09SDavid van Moolenbroek 
1707*00b67f09SDavid van Moolenbroek 	switch (disp->socktype) {
1708*00b67f09SDavid van Moolenbroek 		/*
1709*00b67f09SDavid van Moolenbroek 		 * UDP reads are always maximal.
1710*00b67f09SDavid van Moolenbroek 		 */
1711*00b67f09SDavid van Moolenbroek 	case isc_sockettype_udp:
1712*00b67f09SDavid van Moolenbroek 		region.length = disp->mgr->buffersize;
1713*00b67f09SDavid van Moolenbroek 		region.base = allocate_udp_buffer(disp);
1714*00b67f09SDavid van Moolenbroek 		if (region.base == NULL)
1715*00b67f09SDavid van Moolenbroek 			return (ISC_R_NOMEMORY);
1716*00b67f09SDavid van Moolenbroek 		if (dispsock != NULL) {
1717*00b67f09SDavid van Moolenbroek 			isc_task_t *dt = dispsock->task;
1718*00b67f09SDavid van Moolenbroek 			isc_socketevent_t *sev =
1719*00b67f09SDavid van Moolenbroek 				allocate_sevent(disp, socket,
1720*00b67f09SDavid van Moolenbroek 						ISC_SOCKEVENT_RECVDONE,
1721*00b67f09SDavid van Moolenbroek 						udp_exrecv, dispsock);
1722*00b67f09SDavid van Moolenbroek 			if (sev == NULL) {
1723*00b67f09SDavid van Moolenbroek 				free_buffer(disp, region.base, region.length);
1724*00b67f09SDavid van Moolenbroek 				return (ISC_R_NOMEMORY);
1725*00b67f09SDavid van Moolenbroek 			}
1726*00b67f09SDavid van Moolenbroek 
1727*00b67f09SDavid van Moolenbroek 			res = isc_socket_recv2(socket, &region, 1, dt, sev, 0);
1728*00b67f09SDavid van Moolenbroek 			if (res != ISC_R_SUCCESS) {
1729*00b67f09SDavid van Moolenbroek 				free_buffer(disp, region.base, region.length);
1730*00b67f09SDavid van Moolenbroek 				return (res);
1731*00b67f09SDavid van Moolenbroek 			}
1732*00b67f09SDavid van Moolenbroek 		} else {
1733*00b67f09SDavid van Moolenbroek 			isc_task_t *dt = disp->task[0];
1734*00b67f09SDavid van Moolenbroek 			isc_socketevent_t *sev =
1735*00b67f09SDavid van Moolenbroek 				allocate_sevent(disp, socket,
1736*00b67f09SDavid van Moolenbroek 						ISC_SOCKEVENT_RECVDONE,
1737*00b67f09SDavid van Moolenbroek 						udp_shrecv, disp);
1738*00b67f09SDavid van Moolenbroek 			if (sev == NULL) {
1739*00b67f09SDavid van Moolenbroek 				free_buffer(disp, region.base, region.length);
1740*00b67f09SDavid van Moolenbroek 				return (ISC_R_NOMEMORY);
1741*00b67f09SDavid van Moolenbroek 			}
1742*00b67f09SDavid van Moolenbroek 
1743*00b67f09SDavid van Moolenbroek 			res = isc_socket_recv2(socket, &region, 1, dt, sev, 0);
1744*00b67f09SDavid van Moolenbroek 			if (res != ISC_R_SUCCESS) {
1745*00b67f09SDavid van Moolenbroek 				free_buffer(disp, region.base, region.length);
1746*00b67f09SDavid van Moolenbroek 				disp->shutdown_why = res;
1747*00b67f09SDavid van Moolenbroek 				disp->shutting_down = 1;
1748*00b67f09SDavid van Moolenbroek 				do_cancel(disp);
1749*00b67f09SDavid van Moolenbroek 				return (ISC_R_SUCCESS); /* recover by cancel */
1750*00b67f09SDavid van Moolenbroek 			}
1751*00b67f09SDavid van Moolenbroek 			INSIST(disp->recv_pending == 0);
1752*00b67f09SDavid van Moolenbroek 			disp->recv_pending = 1;
1753*00b67f09SDavid van Moolenbroek 		}
1754*00b67f09SDavid van Moolenbroek 		break;
1755*00b67f09SDavid van Moolenbroek 
1756*00b67f09SDavid van Moolenbroek 	case isc_sockettype_tcp:
1757*00b67f09SDavid van Moolenbroek 		res = dns_tcpmsg_readmessage(&disp->tcpmsg, disp->task[0],
1758*00b67f09SDavid van Moolenbroek 					     tcp_recv, disp);
1759*00b67f09SDavid van Moolenbroek 		if (res != ISC_R_SUCCESS) {
1760*00b67f09SDavid van Moolenbroek 			disp->shutdown_why = res;
1761*00b67f09SDavid van Moolenbroek 			disp->shutting_down = 1;
1762*00b67f09SDavid van Moolenbroek 			do_cancel(disp);
1763*00b67f09SDavid van Moolenbroek 			return (ISC_R_SUCCESS); /* recover by cancel */
1764*00b67f09SDavid van Moolenbroek 		}
1765*00b67f09SDavid van Moolenbroek 		INSIST(disp->recv_pending == 0);
1766*00b67f09SDavid van Moolenbroek 		disp->recv_pending = 1;
1767*00b67f09SDavid van Moolenbroek 		break;
1768*00b67f09SDavid van Moolenbroek 	default:
1769*00b67f09SDavid van Moolenbroek 		INSIST(0);
1770*00b67f09SDavid van Moolenbroek 		break;
1771*00b67f09SDavid van Moolenbroek 	}
1772*00b67f09SDavid van Moolenbroek 
1773*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1774*00b67f09SDavid van Moolenbroek }
1775*00b67f09SDavid van Moolenbroek 
1776*00b67f09SDavid van Moolenbroek /*
1777*00b67f09SDavid van Moolenbroek  * Mgr must be locked when calling this function.
1778*00b67f09SDavid van Moolenbroek  */
1779*00b67f09SDavid van Moolenbroek static isc_boolean_t
destroy_mgr_ok(dns_dispatchmgr_t * mgr)1780*00b67f09SDavid van Moolenbroek destroy_mgr_ok(dns_dispatchmgr_t *mgr) {
1781*00b67f09SDavid van Moolenbroek 	mgr_log(mgr, LVL(90),
1782*00b67f09SDavid van Moolenbroek 		"destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, "
1783*00b67f09SDavid van Moolenbroek 		"depool=%d, rpool=%d, dpool=%d",
1784*00b67f09SDavid van Moolenbroek 		MGR_IS_SHUTTINGDOWN(mgr), !ISC_LIST_EMPTY(mgr->list),
1785*00b67f09SDavid van Moolenbroek 		isc_mempool_getallocated(mgr->depool),
1786*00b67f09SDavid van Moolenbroek 		isc_mempool_getallocated(mgr->rpool),
1787*00b67f09SDavid van Moolenbroek 		isc_mempool_getallocated(mgr->dpool));
1788*00b67f09SDavid van Moolenbroek 	if (!MGR_IS_SHUTTINGDOWN(mgr))
1789*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
1790*00b67f09SDavid van Moolenbroek 	if (!ISC_LIST_EMPTY(mgr->list))
1791*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
1792*00b67f09SDavid van Moolenbroek 	if (isc_mempool_getallocated(mgr->depool) != 0)
1793*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
1794*00b67f09SDavid van Moolenbroek 	if (isc_mempool_getallocated(mgr->rpool) != 0)
1795*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
1796*00b67f09SDavid van Moolenbroek 	if (isc_mempool_getallocated(mgr->dpool) != 0)
1797*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
1798*00b67f09SDavid van Moolenbroek 
1799*00b67f09SDavid van Moolenbroek 	return (ISC_TRUE);
1800*00b67f09SDavid van Moolenbroek }
1801*00b67f09SDavid van Moolenbroek 
1802*00b67f09SDavid van Moolenbroek /*
1803*00b67f09SDavid van Moolenbroek  * Mgr must be unlocked when calling this function.
1804*00b67f09SDavid van Moolenbroek  */
1805*00b67f09SDavid van Moolenbroek static void
destroy_mgr(dns_dispatchmgr_t ** mgrp)1806*00b67f09SDavid van Moolenbroek destroy_mgr(dns_dispatchmgr_t **mgrp) {
1807*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx;
1808*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
1809*00b67f09SDavid van Moolenbroek 
1810*00b67f09SDavid van Moolenbroek 	mgr = *mgrp;
1811*00b67f09SDavid van Moolenbroek 	*mgrp = NULL;
1812*00b67f09SDavid van Moolenbroek 
1813*00b67f09SDavid van Moolenbroek 	mctx = mgr->mctx;
1814*00b67f09SDavid van Moolenbroek 
1815*00b67f09SDavid van Moolenbroek 	mgr->magic = 0;
1816*00b67f09SDavid van Moolenbroek 	mgr->mctx = NULL;
1817*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->lock);
1818*00b67f09SDavid van Moolenbroek 	mgr->state = 0;
1819*00b67f09SDavid van Moolenbroek 
1820*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->arc4_lock);
1821*00b67f09SDavid van Moolenbroek 
1822*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->depool);
1823*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->rpool);
1824*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->dpool);
1825*00b67f09SDavid van Moolenbroek 	if (mgr->bpool != NULL)
1826*00b67f09SDavid van Moolenbroek 		isc_mempool_destroy(&mgr->bpool);
1827*00b67f09SDavid van Moolenbroek 	if (mgr->spool != NULL)
1828*00b67f09SDavid van Moolenbroek 		isc_mempool_destroy(&mgr->spool);
1829*00b67f09SDavid van Moolenbroek 
1830*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->spool_lock);
1831*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->bpool_lock);
1832*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->dpool_lock);
1833*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->rpool_lock);
1834*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->depool_lock);
1835*00b67f09SDavid van Moolenbroek 
1836*00b67f09SDavid van Moolenbroek 	if (mgr->entropy != NULL)
1837*00b67f09SDavid van Moolenbroek 		isc_entropy_detach(&mgr->entropy);
1838*00b67f09SDavid van Moolenbroek 	if (mgr->qid != NULL)
1839*00b67f09SDavid van Moolenbroek 		qid_destroy(mctx, &mgr->qid);
1840*00b67f09SDavid van Moolenbroek 
1841*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->buffer_lock);
1842*00b67f09SDavid van Moolenbroek 
1843*00b67f09SDavid van Moolenbroek 	if (mgr->blackhole != NULL)
1844*00b67f09SDavid van Moolenbroek 		dns_acl_detach(&mgr->blackhole);
1845*00b67f09SDavid van Moolenbroek 
1846*00b67f09SDavid van Moolenbroek 	if (mgr->stats != NULL)
1847*00b67f09SDavid van Moolenbroek 		isc_stats_detach(&mgr->stats);
1848*00b67f09SDavid van Moolenbroek 
1849*00b67f09SDavid van Moolenbroek 	if (mgr->v4ports != NULL) {
1850*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, mgr->v4ports,
1851*00b67f09SDavid van Moolenbroek 			    mgr->nv4ports * sizeof(in_port_t));
1852*00b67f09SDavid van Moolenbroek 	}
1853*00b67f09SDavid van Moolenbroek 	if (mgr->v6ports != NULL) {
1854*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, mgr->v6ports,
1855*00b67f09SDavid van Moolenbroek 			    mgr->nv6ports * sizeof(in_port_t));
1856*00b67f09SDavid van Moolenbroek 	}
1857*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t));
1858*00b67f09SDavid van Moolenbroek 	isc_mem_detach(&mctx);
1859*00b67f09SDavid van Moolenbroek }
1860*00b67f09SDavid van Moolenbroek 
1861*00b67f09SDavid van Moolenbroek static isc_result_t
open_socket(isc_socketmgr_t * mgr,isc_sockaddr_t * local,unsigned int options,isc_socket_t ** sockp,isc_socket_t * dup_socket)1862*00b67f09SDavid van Moolenbroek open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local,
1863*00b67f09SDavid van Moolenbroek 	    unsigned int options, isc_socket_t **sockp,
1864*00b67f09SDavid van Moolenbroek 	    isc_socket_t *dup_socket)
1865*00b67f09SDavid van Moolenbroek {
1866*00b67f09SDavid van Moolenbroek 	isc_socket_t *sock;
1867*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1868*00b67f09SDavid van Moolenbroek 
1869*00b67f09SDavid van Moolenbroek 	sock = *sockp;
1870*00b67f09SDavid van Moolenbroek 	if (sock != NULL) {
1871*00b67f09SDavid van Moolenbroek 		result = isc_socket_open(sock);
1872*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
1873*00b67f09SDavid van Moolenbroek 			return (result);
1874*00b67f09SDavid van Moolenbroek 	} else if (dup_socket != NULL) {
1875*00b67f09SDavid van Moolenbroek 		result = isc_socket_dup(dup_socket, &sock);
1876*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
1877*00b67f09SDavid van Moolenbroek 			return (result);
1878*00b67f09SDavid van Moolenbroek 
1879*00b67f09SDavid van Moolenbroek 		isc_socket_setname(sock, "dispatcher", NULL);
1880*00b67f09SDavid van Moolenbroek 		*sockp = sock;
1881*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
1882*00b67f09SDavid van Moolenbroek 	} else {
1883*00b67f09SDavid van Moolenbroek 		result = isc_socket_create(mgr, isc_sockaddr_pf(local),
1884*00b67f09SDavid van Moolenbroek 					isc_sockettype_udp, &sock);
1885*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
1886*00b67f09SDavid van Moolenbroek 			return (result);
1887*00b67f09SDavid van Moolenbroek 	}
1888*00b67f09SDavid van Moolenbroek 
1889*00b67f09SDavid van Moolenbroek 	isc_socket_setname(sock, "dispatcher", NULL);
1890*00b67f09SDavid van Moolenbroek 
1891*00b67f09SDavid van Moolenbroek #ifndef ISC_ALLOW_MAPPED
1892*00b67f09SDavid van Moolenbroek 	isc_socket_ipv6only(sock, ISC_TRUE);
1893*00b67f09SDavid van Moolenbroek #endif
1894*00b67f09SDavid van Moolenbroek 	result = isc_socket_bind(sock, local, options);
1895*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1896*00b67f09SDavid van Moolenbroek 		if (*sockp == NULL)
1897*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&sock);
1898*00b67f09SDavid van Moolenbroek 		else {
1899*00b67f09SDavid van Moolenbroek 			isc_socket_close(sock);
1900*00b67f09SDavid van Moolenbroek 		}
1901*00b67f09SDavid van Moolenbroek 		return (result);
1902*00b67f09SDavid van Moolenbroek 	}
1903*00b67f09SDavid van Moolenbroek 
1904*00b67f09SDavid van Moolenbroek 	*sockp = sock;
1905*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1906*00b67f09SDavid van Moolenbroek }
1907*00b67f09SDavid van Moolenbroek 
1908*00b67f09SDavid van Moolenbroek /*%
1909*00b67f09SDavid van Moolenbroek  * Create a temporary port list to set the initial default set of dispatch
1910*00b67f09SDavid van Moolenbroek  * ports: [1024, 65535].  This is almost meaningless as the application will
1911*00b67f09SDavid van Moolenbroek  * normally set the ports explicitly, but is provided to fill some minor corner
1912*00b67f09SDavid van Moolenbroek  * cases.
1913*00b67f09SDavid van Moolenbroek  */
1914*00b67f09SDavid van Moolenbroek static isc_result_t
create_default_portset(isc_mem_t * mctx,isc_portset_t ** portsetp)1915*00b67f09SDavid van Moolenbroek create_default_portset(isc_mem_t *mctx, isc_portset_t **portsetp) {
1916*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1917*00b67f09SDavid van Moolenbroek 
1918*00b67f09SDavid van Moolenbroek 	result = isc_portset_create(mctx, portsetp);
1919*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1920*00b67f09SDavid van Moolenbroek 		return (result);
1921*00b67f09SDavid van Moolenbroek 	isc_portset_addrange(*portsetp, 1024, 65535);
1922*00b67f09SDavid van Moolenbroek 
1923*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1924*00b67f09SDavid van Moolenbroek }
1925*00b67f09SDavid van Moolenbroek 
1926*00b67f09SDavid van Moolenbroek /*
1927*00b67f09SDavid van Moolenbroek  * Publics.
1928*00b67f09SDavid van Moolenbroek  */
1929*00b67f09SDavid van Moolenbroek 
1930*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatchmgr_create(isc_mem_t * mctx,isc_entropy_t * entropy,dns_dispatchmgr_t ** mgrp)1931*00b67f09SDavid van Moolenbroek dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
1932*00b67f09SDavid van Moolenbroek 		       dns_dispatchmgr_t **mgrp)
1933*00b67f09SDavid van Moolenbroek {
1934*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
1935*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1936*00b67f09SDavid van Moolenbroek 	isc_portset_t *v4portset = NULL;
1937*00b67f09SDavid van Moolenbroek 	isc_portset_t *v6portset = NULL;
1938*00b67f09SDavid van Moolenbroek 
1939*00b67f09SDavid van Moolenbroek 	REQUIRE(mctx != NULL);
1940*00b67f09SDavid van Moolenbroek 	REQUIRE(mgrp != NULL && *mgrp == NULL);
1941*00b67f09SDavid van Moolenbroek 
1942*00b67f09SDavid van Moolenbroek 	mgr = isc_mem_get(mctx, sizeof(dns_dispatchmgr_t));
1943*00b67f09SDavid van Moolenbroek 	if (mgr == NULL)
1944*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
1945*00b67f09SDavid van Moolenbroek 
1946*00b67f09SDavid van Moolenbroek 	mgr->mctx = NULL;
1947*00b67f09SDavid van Moolenbroek 	isc_mem_attach(mctx, &mgr->mctx);
1948*00b67f09SDavid van Moolenbroek 
1949*00b67f09SDavid van Moolenbroek 	mgr->blackhole = NULL;
1950*00b67f09SDavid van Moolenbroek 	mgr->stats = NULL;
1951*00b67f09SDavid van Moolenbroek 
1952*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->lock);
1953*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1954*00b67f09SDavid van Moolenbroek 		goto deallocate;
1955*00b67f09SDavid van Moolenbroek 
1956*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->arc4_lock);
1957*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1958*00b67f09SDavid van Moolenbroek 		goto kill_lock;
1959*00b67f09SDavid van Moolenbroek 
1960*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->buffer_lock);
1961*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1962*00b67f09SDavid van Moolenbroek 		goto kill_arc4_lock;
1963*00b67f09SDavid van Moolenbroek 
1964*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->depool_lock);
1965*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1966*00b67f09SDavid van Moolenbroek 		goto kill_buffer_lock;
1967*00b67f09SDavid van Moolenbroek 
1968*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->rpool_lock);
1969*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1970*00b67f09SDavid van Moolenbroek 		goto kill_depool_lock;
1971*00b67f09SDavid van Moolenbroek 
1972*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->dpool_lock);
1973*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1974*00b67f09SDavid van Moolenbroek 		goto kill_rpool_lock;
1975*00b67f09SDavid van Moolenbroek 
1976*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->bpool_lock);
1977*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1978*00b67f09SDavid van Moolenbroek 		goto kill_dpool_lock;
1979*00b67f09SDavid van Moolenbroek 
1980*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&mgr->spool_lock);
1981*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
1982*00b67f09SDavid van Moolenbroek 		goto kill_bpool_lock;
1983*00b67f09SDavid van Moolenbroek 
1984*00b67f09SDavid van Moolenbroek 	mgr->depool = NULL;
1985*00b67f09SDavid van Moolenbroek 	if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t),
1986*00b67f09SDavid van Moolenbroek 			       &mgr->depool) != ISC_R_SUCCESS) {
1987*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
1988*00b67f09SDavid van Moolenbroek 		goto kill_spool_lock;
1989*00b67f09SDavid van Moolenbroek 	}
1990*00b67f09SDavid van Moolenbroek 
1991*00b67f09SDavid van Moolenbroek 	mgr->rpool = NULL;
1992*00b67f09SDavid van Moolenbroek 	if (isc_mempool_create(mgr->mctx, sizeof(dns_dispentry_t),
1993*00b67f09SDavid van Moolenbroek 			       &mgr->rpool) != ISC_R_SUCCESS) {
1994*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
1995*00b67f09SDavid van Moolenbroek 		goto kill_depool;
1996*00b67f09SDavid van Moolenbroek 	}
1997*00b67f09SDavid van Moolenbroek 
1998*00b67f09SDavid van Moolenbroek 	mgr->dpool = NULL;
1999*00b67f09SDavid van Moolenbroek 	if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatch_t),
2000*00b67f09SDavid van Moolenbroek 			       &mgr->dpool) != ISC_R_SUCCESS) {
2001*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
2002*00b67f09SDavid van Moolenbroek 		goto kill_rpool;
2003*00b67f09SDavid van Moolenbroek 	}
2004*00b67f09SDavid van Moolenbroek 
2005*00b67f09SDavid van Moolenbroek 	isc_mempool_setname(mgr->depool, "dispmgr_depool");
2006*00b67f09SDavid van Moolenbroek 	isc_mempool_setmaxalloc(mgr->depool, 32768);
2007*00b67f09SDavid van Moolenbroek 	isc_mempool_setfreemax(mgr->depool, 32768);
2008*00b67f09SDavid van Moolenbroek 	isc_mempool_associatelock(mgr->depool, &mgr->depool_lock);
2009*00b67f09SDavid van Moolenbroek 	isc_mempool_setfillcount(mgr->depool, 32);
2010*00b67f09SDavid van Moolenbroek 
2011*00b67f09SDavid van Moolenbroek 	isc_mempool_setname(mgr->rpool, "dispmgr_rpool");
2012*00b67f09SDavid van Moolenbroek 	isc_mempool_setmaxalloc(mgr->rpool, 32768);
2013*00b67f09SDavid van Moolenbroek 	isc_mempool_setfreemax(mgr->rpool, 32768);
2014*00b67f09SDavid van Moolenbroek 	isc_mempool_associatelock(mgr->rpool, &mgr->rpool_lock);
2015*00b67f09SDavid van Moolenbroek 	isc_mempool_setfillcount(mgr->rpool, 32);
2016*00b67f09SDavid van Moolenbroek 
2017*00b67f09SDavid van Moolenbroek 	isc_mempool_setname(mgr->dpool, "dispmgr_dpool");
2018*00b67f09SDavid van Moolenbroek 	isc_mempool_setmaxalloc(mgr->dpool, 32768);
2019*00b67f09SDavid van Moolenbroek 	isc_mempool_setfreemax(mgr->dpool, 32768);
2020*00b67f09SDavid van Moolenbroek 	isc_mempool_associatelock(mgr->dpool, &mgr->dpool_lock);
2021*00b67f09SDavid van Moolenbroek 	isc_mempool_setfillcount(mgr->dpool, 32);
2022*00b67f09SDavid van Moolenbroek 
2023*00b67f09SDavid van Moolenbroek 	mgr->buffers = 0;
2024*00b67f09SDavid van Moolenbroek 	mgr->buffersize = 0;
2025*00b67f09SDavid van Moolenbroek 	mgr->maxbuffers = 0;
2026*00b67f09SDavid van Moolenbroek 	mgr->bpool = NULL;
2027*00b67f09SDavid van Moolenbroek 	mgr->spool = NULL;
2028*00b67f09SDavid van Moolenbroek 	mgr->entropy = NULL;
2029*00b67f09SDavid van Moolenbroek 	mgr->qid = NULL;
2030*00b67f09SDavid van Moolenbroek 	mgr->state = 0;
2031*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(mgr->list);
2032*00b67f09SDavid van Moolenbroek 	mgr->v4ports = NULL;
2033*00b67f09SDavid van Moolenbroek 	mgr->v6ports = NULL;
2034*00b67f09SDavid van Moolenbroek 	mgr->nv4ports = 0;
2035*00b67f09SDavid van Moolenbroek 	mgr->nv6ports = 0;
2036*00b67f09SDavid van Moolenbroek 	mgr->magic = DNS_DISPATCHMGR_MAGIC;
2037*00b67f09SDavid van Moolenbroek 
2038*00b67f09SDavid van Moolenbroek 	result = create_default_portset(mctx, &v4portset);
2039*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS) {
2040*00b67f09SDavid van Moolenbroek 		result = create_default_portset(mctx, &v6portset);
2041*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS) {
2042*00b67f09SDavid van Moolenbroek 			result = dns_dispatchmgr_setavailports(mgr,
2043*00b67f09SDavid van Moolenbroek 							       v4portset,
2044*00b67f09SDavid van Moolenbroek 							       v6portset);
2045*00b67f09SDavid van Moolenbroek 		}
2046*00b67f09SDavid van Moolenbroek 	}
2047*00b67f09SDavid van Moolenbroek 	if (v4portset != NULL)
2048*00b67f09SDavid van Moolenbroek 		isc_portset_destroy(mctx, &v4portset);
2049*00b67f09SDavid van Moolenbroek 	if (v6portset != NULL)
2050*00b67f09SDavid van Moolenbroek 		isc_portset_destroy(mctx, &v6portset);
2051*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2052*00b67f09SDavid van Moolenbroek 		goto kill_dpool;
2053*00b67f09SDavid van Moolenbroek 
2054*00b67f09SDavid van Moolenbroek 	if (entropy != NULL)
2055*00b67f09SDavid van Moolenbroek 		isc_entropy_attach(entropy, &mgr->entropy);
2056*00b67f09SDavid van Moolenbroek 
2057*00b67f09SDavid van Moolenbroek 	dispatch_initrandom(&mgr->arc4ctx, mgr->entropy, &mgr->arc4_lock);
2058*00b67f09SDavid van Moolenbroek 
2059*00b67f09SDavid van Moolenbroek 	*mgrp = mgr;
2060*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2061*00b67f09SDavid van Moolenbroek 
2062*00b67f09SDavid van Moolenbroek  kill_dpool:
2063*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->dpool);
2064*00b67f09SDavid van Moolenbroek  kill_rpool:
2065*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->rpool);
2066*00b67f09SDavid van Moolenbroek  kill_depool:
2067*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->depool);
2068*00b67f09SDavid van Moolenbroek  kill_spool_lock:
2069*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->spool_lock);
2070*00b67f09SDavid van Moolenbroek  kill_bpool_lock:
2071*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->bpool_lock);
2072*00b67f09SDavid van Moolenbroek  kill_dpool_lock:
2073*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->dpool_lock);
2074*00b67f09SDavid van Moolenbroek  kill_rpool_lock:
2075*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->rpool_lock);
2076*00b67f09SDavid van Moolenbroek  kill_depool_lock:
2077*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->depool_lock);
2078*00b67f09SDavid van Moolenbroek  kill_buffer_lock:
2079*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->buffer_lock);
2080*00b67f09SDavid van Moolenbroek  kill_arc4_lock:
2081*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->arc4_lock);
2082*00b67f09SDavid van Moolenbroek  kill_lock:
2083*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&mgr->lock);
2084*00b67f09SDavid van Moolenbroek  deallocate:
2085*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t));
2086*00b67f09SDavid van Moolenbroek 	isc_mem_detach(&mctx);
2087*00b67f09SDavid van Moolenbroek 
2088*00b67f09SDavid van Moolenbroek 	return (result);
2089*00b67f09SDavid van Moolenbroek }
2090*00b67f09SDavid van Moolenbroek 
2091*00b67f09SDavid van Moolenbroek void
dns_dispatchmgr_setblackhole(dns_dispatchmgr_t * mgr,dns_acl_t * blackhole)2092*00b67f09SDavid van Moolenbroek dns_dispatchmgr_setblackhole(dns_dispatchmgr_t *mgr, dns_acl_t *blackhole) {
2093*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2094*00b67f09SDavid van Moolenbroek 	if (mgr->blackhole != NULL)
2095*00b67f09SDavid van Moolenbroek 		dns_acl_detach(&mgr->blackhole);
2096*00b67f09SDavid van Moolenbroek 	dns_acl_attach(blackhole, &mgr->blackhole);
2097*00b67f09SDavid van Moolenbroek }
2098*00b67f09SDavid van Moolenbroek 
2099*00b67f09SDavid van Moolenbroek dns_acl_t *
dns_dispatchmgr_getblackhole(dns_dispatchmgr_t * mgr)2100*00b67f09SDavid van Moolenbroek dns_dispatchmgr_getblackhole(dns_dispatchmgr_t *mgr) {
2101*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2102*00b67f09SDavid van Moolenbroek 	return (mgr->blackhole);
2103*00b67f09SDavid van Moolenbroek }
2104*00b67f09SDavid van Moolenbroek 
2105*00b67f09SDavid van Moolenbroek void
dns_dispatchmgr_setblackportlist(dns_dispatchmgr_t * mgr,dns_portlist_t * portlist)2106*00b67f09SDavid van Moolenbroek dns_dispatchmgr_setblackportlist(dns_dispatchmgr_t *mgr,
2107*00b67f09SDavid van Moolenbroek 				 dns_portlist_t *portlist)
2108*00b67f09SDavid van Moolenbroek {
2109*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2110*00b67f09SDavid van Moolenbroek 	UNUSED(portlist);
2111*00b67f09SDavid van Moolenbroek 
2112*00b67f09SDavid van Moolenbroek 	/* This function is deprecated: use dns_dispatchmgr_setavailports(). */
2113*00b67f09SDavid van Moolenbroek 	return;
2114*00b67f09SDavid van Moolenbroek }
2115*00b67f09SDavid van Moolenbroek 
2116*00b67f09SDavid van Moolenbroek dns_portlist_t *
dns_dispatchmgr_getblackportlist(dns_dispatchmgr_t * mgr)2117*00b67f09SDavid van Moolenbroek dns_dispatchmgr_getblackportlist(dns_dispatchmgr_t *mgr) {
2118*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2119*00b67f09SDavid van Moolenbroek 	return (NULL);		/* this function is deprecated */
2120*00b67f09SDavid van Moolenbroek }
2121*00b67f09SDavid van Moolenbroek 
2122*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatchmgr_setavailports(dns_dispatchmgr_t * mgr,isc_portset_t * v4portset,isc_portset_t * v6portset)2123*00b67f09SDavid van Moolenbroek dns_dispatchmgr_setavailports(dns_dispatchmgr_t *mgr, isc_portset_t *v4portset,
2124*00b67f09SDavid van Moolenbroek 			      isc_portset_t *v6portset)
2125*00b67f09SDavid van Moolenbroek {
2126*00b67f09SDavid van Moolenbroek 	in_port_t *v4ports, *v6ports, p;
2127*00b67f09SDavid van Moolenbroek 	unsigned int nv4ports, nv6ports, i4, i6;
2128*00b67f09SDavid van Moolenbroek 
2129*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2130*00b67f09SDavid van Moolenbroek 
2131*00b67f09SDavid van Moolenbroek 	nv4ports = isc_portset_nports(v4portset);
2132*00b67f09SDavid van Moolenbroek 	nv6ports = isc_portset_nports(v6portset);
2133*00b67f09SDavid van Moolenbroek 
2134*00b67f09SDavid van Moolenbroek 	v4ports = NULL;
2135*00b67f09SDavid van Moolenbroek 	if (nv4ports != 0) {
2136*00b67f09SDavid van Moolenbroek 		v4ports = isc_mem_get(mgr->mctx, sizeof(in_port_t) * nv4ports);
2137*00b67f09SDavid van Moolenbroek 		if (v4ports == NULL)
2138*00b67f09SDavid van Moolenbroek 			return (ISC_R_NOMEMORY);
2139*00b67f09SDavid van Moolenbroek 	}
2140*00b67f09SDavid van Moolenbroek 	v6ports = NULL;
2141*00b67f09SDavid van Moolenbroek 	if (nv6ports != 0) {
2142*00b67f09SDavid van Moolenbroek 		v6ports = isc_mem_get(mgr->mctx, sizeof(in_port_t) * nv6ports);
2143*00b67f09SDavid van Moolenbroek 		if (v6ports == NULL) {
2144*00b67f09SDavid van Moolenbroek 			if (v4ports != NULL) {
2145*00b67f09SDavid van Moolenbroek 				isc_mem_put(mgr->mctx, v4ports,
2146*00b67f09SDavid van Moolenbroek 					    sizeof(in_port_t) *
2147*00b67f09SDavid van Moolenbroek 					    isc_portset_nports(v4portset));
2148*00b67f09SDavid van Moolenbroek 			}
2149*00b67f09SDavid van Moolenbroek 			return (ISC_R_NOMEMORY);
2150*00b67f09SDavid van Moolenbroek 		}
2151*00b67f09SDavid van Moolenbroek 	}
2152*00b67f09SDavid van Moolenbroek 
2153*00b67f09SDavid van Moolenbroek 	p = 0;
2154*00b67f09SDavid van Moolenbroek 	i4 = 0;
2155*00b67f09SDavid van Moolenbroek 	i6 = 0;
2156*00b67f09SDavid van Moolenbroek 	do {
2157*00b67f09SDavid van Moolenbroek 		if (isc_portset_isset(v4portset, p)) {
2158*00b67f09SDavid van Moolenbroek 			INSIST(i4 < nv4ports);
2159*00b67f09SDavid van Moolenbroek 			v4ports[i4++] = p;
2160*00b67f09SDavid van Moolenbroek 		}
2161*00b67f09SDavid van Moolenbroek 		if (isc_portset_isset(v6portset, p)) {
2162*00b67f09SDavid van Moolenbroek 			INSIST(i6 < nv6ports);
2163*00b67f09SDavid van Moolenbroek 			v6ports[i6++] = p;
2164*00b67f09SDavid van Moolenbroek 		}
2165*00b67f09SDavid van Moolenbroek 	} while (p++ < 65535);
2166*00b67f09SDavid van Moolenbroek 	INSIST(i4 == nv4ports && i6 == nv6ports);
2167*00b67f09SDavid van Moolenbroek 
2168*00b67f09SDavid van Moolenbroek 	PORTBUFLOCK(mgr);
2169*00b67f09SDavid van Moolenbroek 	if (mgr->v4ports != NULL) {
2170*00b67f09SDavid van Moolenbroek 		isc_mem_put(mgr->mctx, mgr->v4ports,
2171*00b67f09SDavid van Moolenbroek 			    mgr->nv4ports * sizeof(in_port_t));
2172*00b67f09SDavid van Moolenbroek 	}
2173*00b67f09SDavid van Moolenbroek 	mgr->v4ports = v4ports;
2174*00b67f09SDavid van Moolenbroek 	mgr->nv4ports = nv4ports;
2175*00b67f09SDavid van Moolenbroek 
2176*00b67f09SDavid van Moolenbroek 	if (mgr->v6ports != NULL) {
2177*00b67f09SDavid van Moolenbroek 		isc_mem_put(mgr->mctx, mgr->v6ports,
2178*00b67f09SDavid van Moolenbroek 			    mgr->nv6ports * sizeof(in_port_t));
2179*00b67f09SDavid van Moolenbroek 	}
2180*00b67f09SDavid van Moolenbroek 	mgr->v6ports = v6ports;
2181*00b67f09SDavid van Moolenbroek 	mgr->nv6ports = nv6ports;
2182*00b67f09SDavid van Moolenbroek 	PORTBUFUNLOCK(mgr);
2183*00b67f09SDavid van Moolenbroek 
2184*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2185*00b67f09SDavid van Moolenbroek }
2186*00b67f09SDavid van Moolenbroek 
2187*00b67f09SDavid van Moolenbroek static isc_result_t
dns_dispatchmgr_setudp(dns_dispatchmgr_t * mgr,unsigned int buffersize,unsigned int maxbuffers,unsigned int maxrequests,unsigned int buckets,unsigned int increment)2188*00b67f09SDavid van Moolenbroek dns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr,
2189*00b67f09SDavid van Moolenbroek 		       unsigned int buffersize, unsigned int maxbuffers,
2190*00b67f09SDavid van Moolenbroek 		       unsigned int maxrequests, unsigned int buckets,
2191*00b67f09SDavid van Moolenbroek 		       unsigned int increment)
2192*00b67f09SDavid van Moolenbroek {
2193*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2194*00b67f09SDavid van Moolenbroek 
2195*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2196*00b67f09SDavid van Moolenbroek 	REQUIRE(buffersize >= 512 && buffersize < (64 * 1024));
2197*00b67f09SDavid van Moolenbroek 	REQUIRE(maxbuffers > 0);
2198*00b67f09SDavid van Moolenbroek 	REQUIRE(buckets < 2097169);  /* next prime > 65536 * 32 */
2199*00b67f09SDavid van Moolenbroek 	REQUIRE(increment > buckets);
2200*00b67f09SDavid van Moolenbroek 
2201*00b67f09SDavid van Moolenbroek 	/*
2202*00b67f09SDavid van Moolenbroek 	 * Keep some number of items around.  This should be a config
2203*00b67f09SDavid van Moolenbroek 	 * option.  For now, keep 8, but later keep at least two even
2204*00b67f09SDavid van Moolenbroek 	 * if the caller wants less.  This allows us to ensure certain
2205*00b67f09SDavid van Moolenbroek 	 * things, like an event can be "freed" and the next allocation
2206*00b67f09SDavid van Moolenbroek 	 * will always succeed.
2207*00b67f09SDavid van Moolenbroek 	 *
2208*00b67f09SDavid van Moolenbroek 	 * Note that if limits are placed on anything here, we use one
2209*00b67f09SDavid van Moolenbroek 	 * event internally, so the actual limit should be "wanted + 1."
2210*00b67f09SDavid van Moolenbroek 	 *
2211*00b67f09SDavid van Moolenbroek 	 * XXXMLG
2212*00b67f09SDavid van Moolenbroek 	 */
2213*00b67f09SDavid van Moolenbroek 
2214*00b67f09SDavid van Moolenbroek 	if (maxbuffers < 8)
2215*00b67f09SDavid van Moolenbroek 		maxbuffers = 8;
2216*00b67f09SDavid van Moolenbroek 
2217*00b67f09SDavid van Moolenbroek 	LOCK(&mgr->buffer_lock);
2218*00b67f09SDavid van Moolenbroek 
2219*00b67f09SDavid van Moolenbroek 	/* Create or adjust buffer pool */
2220*00b67f09SDavid van Moolenbroek 	if (mgr->bpool != NULL) {
2221*00b67f09SDavid van Moolenbroek 		/*
2222*00b67f09SDavid van Moolenbroek 		 * We only increase the maxbuffers to avoid accidental buffer
2223*00b67f09SDavid van Moolenbroek 		 * shortage.  Ideally we'd separate the manager-wide maximum
2224*00b67f09SDavid van Moolenbroek 		 * from per-dispatch limits and respect the latter within the
2225*00b67f09SDavid van Moolenbroek 		 * global limit.  But at this moment that's deemed to be
2226*00b67f09SDavid van Moolenbroek 		 * overkilling and isn't worth additional implementation
2227*00b67f09SDavid van Moolenbroek 		 * complexity.
2228*00b67f09SDavid van Moolenbroek 		 */
2229*00b67f09SDavid van Moolenbroek 		if (maxbuffers > mgr->maxbuffers) {
2230*00b67f09SDavid van Moolenbroek 			isc_mempool_setmaxalloc(mgr->bpool, maxbuffers);
2231*00b67f09SDavid van Moolenbroek 			isc_mempool_setfreemax(mgr->bpool, maxbuffers);
2232*00b67f09SDavid van Moolenbroek 			mgr->maxbuffers = maxbuffers;
2233*00b67f09SDavid van Moolenbroek 		}
2234*00b67f09SDavid van Moolenbroek 	} else {
2235*00b67f09SDavid van Moolenbroek 		result = isc_mempool_create(mgr->mctx, buffersize, &mgr->bpool);
2236*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
2237*00b67f09SDavid van Moolenbroek 			UNLOCK(&mgr->buffer_lock);
2238*00b67f09SDavid van Moolenbroek 			return (result);
2239*00b67f09SDavid van Moolenbroek 		}
2240*00b67f09SDavid van Moolenbroek 		isc_mempool_setname(mgr->bpool, "dispmgr_bpool");
2241*00b67f09SDavid van Moolenbroek 		isc_mempool_setmaxalloc(mgr->bpool, maxbuffers);
2242*00b67f09SDavid van Moolenbroek 		isc_mempool_setfreemax(mgr->bpool, maxbuffers);
2243*00b67f09SDavid van Moolenbroek 		isc_mempool_associatelock(mgr->bpool, &mgr->bpool_lock);
2244*00b67f09SDavid van Moolenbroek 		isc_mempool_setfillcount(mgr->bpool, 32);
2245*00b67f09SDavid van Moolenbroek 	}
2246*00b67f09SDavid van Moolenbroek 
2247*00b67f09SDavid van Moolenbroek 	/* Create or adjust socket pool */
2248*00b67f09SDavid van Moolenbroek 	if (mgr->spool != NULL) {
2249*00b67f09SDavid van Moolenbroek 		if (maxrequests < DNS_DISPATCH_POOLSOCKS * 2) {
2250*00b67f09SDavid van Moolenbroek 			isc_mempool_setmaxalloc(mgr->spool,
2251*00b67f09SDavid van Moolenbroek 						DNS_DISPATCH_POOLSOCKS * 2);
2252*00b67f09SDavid van Moolenbroek 			isc_mempool_setfreemax(mgr->spool,
2253*00b67f09SDavid van Moolenbroek 					       DNS_DISPATCH_POOLSOCKS * 2);
2254*00b67f09SDavid van Moolenbroek 		}
2255*00b67f09SDavid van Moolenbroek 		UNLOCK(&mgr->buffer_lock);
2256*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
2257*00b67f09SDavid van Moolenbroek 	}
2258*00b67f09SDavid van Moolenbroek 	result = isc_mempool_create(mgr->mctx, sizeof(dispsocket_t),
2259*00b67f09SDavid van Moolenbroek 				    &mgr->spool);
2260*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2261*00b67f09SDavid van Moolenbroek 		UNLOCK(&mgr->buffer_lock);
2262*00b67f09SDavid van Moolenbroek 		goto cleanup;
2263*00b67f09SDavid van Moolenbroek 	}
2264*00b67f09SDavid van Moolenbroek 	isc_mempool_setname(mgr->spool, "dispmgr_spool");
2265*00b67f09SDavid van Moolenbroek 	isc_mempool_setmaxalloc(mgr->spool, maxrequests);
2266*00b67f09SDavid van Moolenbroek 	isc_mempool_setfreemax(mgr->spool, maxrequests);
2267*00b67f09SDavid van Moolenbroek 	isc_mempool_associatelock(mgr->spool, &mgr->spool_lock);
2268*00b67f09SDavid van Moolenbroek 	isc_mempool_setfillcount(mgr->spool, 32);
2269*00b67f09SDavid van Moolenbroek 
2270*00b67f09SDavid van Moolenbroek 	result = qid_allocate(mgr, buckets, increment, &mgr->qid, ISC_TRUE);
2271*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2272*00b67f09SDavid van Moolenbroek 		goto cleanup;
2273*00b67f09SDavid van Moolenbroek 
2274*00b67f09SDavid van Moolenbroek 	mgr->buffersize = buffersize;
2275*00b67f09SDavid van Moolenbroek 	mgr->maxbuffers = maxbuffers;
2276*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->buffer_lock);
2277*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2278*00b67f09SDavid van Moolenbroek 
2279*00b67f09SDavid van Moolenbroek  cleanup:
2280*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&mgr->bpool);
2281*00b67f09SDavid van Moolenbroek 	if (mgr->spool != NULL)
2282*00b67f09SDavid van Moolenbroek 		isc_mempool_destroy(&mgr->spool);
2283*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->buffer_lock);
2284*00b67f09SDavid van Moolenbroek 	return (result);
2285*00b67f09SDavid van Moolenbroek }
2286*00b67f09SDavid van Moolenbroek 
2287*00b67f09SDavid van Moolenbroek void
dns_dispatchmgr_destroy(dns_dispatchmgr_t ** mgrp)2288*00b67f09SDavid van Moolenbroek dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) {
2289*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
2290*00b67f09SDavid van Moolenbroek 	isc_boolean_t killit;
2291*00b67f09SDavid van Moolenbroek 
2292*00b67f09SDavid van Moolenbroek 	REQUIRE(mgrp != NULL);
2293*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(*mgrp));
2294*00b67f09SDavid van Moolenbroek 
2295*00b67f09SDavid van Moolenbroek 	mgr = *mgrp;
2296*00b67f09SDavid van Moolenbroek 	*mgrp = NULL;
2297*00b67f09SDavid van Moolenbroek 
2298*00b67f09SDavid van Moolenbroek 	LOCK(&mgr->lock);
2299*00b67f09SDavid van Moolenbroek 	mgr->state |= MGR_SHUTTINGDOWN;
2300*00b67f09SDavid van Moolenbroek 
2301*00b67f09SDavid van Moolenbroek 	killit = destroy_mgr_ok(mgr);
2302*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
2303*00b67f09SDavid van Moolenbroek 
2304*00b67f09SDavid van Moolenbroek 	mgr_log(mgr, LVL(90), "destroy: killit=%d", killit);
2305*00b67f09SDavid van Moolenbroek 
2306*00b67f09SDavid van Moolenbroek 	if (killit)
2307*00b67f09SDavid van Moolenbroek 		destroy_mgr(&mgr);
2308*00b67f09SDavid van Moolenbroek }
2309*00b67f09SDavid van Moolenbroek 
2310*00b67f09SDavid van Moolenbroek void
dns_dispatchmgr_setstats(dns_dispatchmgr_t * mgr,isc_stats_t * stats)2311*00b67f09SDavid van Moolenbroek dns_dispatchmgr_setstats(dns_dispatchmgr_t *mgr, isc_stats_t *stats) {
2312*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2313*00b67f09SDavid van Moolenbroek 	REQUIRE(ISC_LIST_EMPTY(mgr->list));
2314*00b67f09SDavid van Moolenbroek 	REQUIRE(mgr->stats == NULL);
2315*00b67f09SDavid van Moolenbroek 
2316*00b67f09SDavid van Moolenbroek 	isc_stats_attach(stats, &mgr->stats);
2317*00b67f09SDavid van Moolenbroek }
2318*00b67f09SDavid van Moolenbroek 
2319*00b67f09SDavid van Moolenbroek static int
port_cmp(const void * key,const void * ent)2320*00b67f09SDavid van Moolenbroek port_cmp(const void *key, const void *ent) {
2321*00b67f09SDavid van Moolenbroek 	in_port_t p1 = *(const in_port_t *)key;
2322*00b67f09SDavid van Moolenbroek 	in_port_t p2 = *(const in_port_t *)ent;
2323*00b67f09SDavid van Moolenbroek 
2324*00b67f09SDavid van Moolenbroek 	if (p1 < p2)
2325*00b67f09SDavid van Moolenbroek 		return (-1);
2326*00b67f09SDavid van Moolenbroek 	else if (p1 == p2)
2327*00b67f09SDavid van Moolenbroek 		return (0);
2328*00b67f09SDavid van Moolenbroek 	else
2329*00b67f09SDavid van Moolenbroek 		return (1);
2330*00b67f09SDavid van Moolenbroek }
2331*00b67f09SDavid van Moolenbroek 
2332*00b67f09SDavid van Moolenbroek static isc_boolean_t
portavailable(dns_dispatchmgr_t * mgr,isc_socket_t * sock,isc_sockaddr_t * sockaddrp)2333*00b67f09SDavid van Moolenbroek portavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
2334*00b67f09SDavid van Moolenbroek 	      isc_sockaddr_t *sockaddrp)
2335*00b67f09SDavid van Moolenbroek {
2336*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t sockaddr;
2337*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2338*00b67f09SDavid van Moolenbroek 	in_port_t *ports, port;
2339*00b67f09SDavid van Moolenbroek 	unsigned int nports;
2340*00b67f09SDavid van Moolenbroek 	isc_boolean_t available = ISC_FALSE;
2341*00b67f09SDavid van Moolenbroek 
2342*00b67f09SDavid van Moolenbroek 	REQUIRE(sock != NULL || sockaddrp != NULL);
2343*00b67f09SDavid van Moolenbroek 
2344*00b67f09SDavid van Moolenbroek 	PORTBUFLOCK(mgr);
2345*00b67f09SDavid van Moolenbroek 	if (sock != NULL) {
2346*00b67f09SDavid van Moolenbroek 		sockaddrp = &sockaddr;
2347*00b67f09SDavid van Moolenbroek 		result = isc_socket_getsockname(sock, sockaddrp);
2348*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
2349*00b67f09SDavid van Moolenbroek 			goto unlock;
2350*00b67f09SDavid van Moolenbroek 	}
2351*00b67f09SDavid van Moolenbroek 
2352*00b67f09SDavid van Moolenbroek 	if (isc_sockaddr_pf(sockaddrp) == AF_INET) {
2353*00b67f09SDavid van Moolenbroek 		ports = mgr->v4ports;
2354*00b67f09SDavid van Moolenbroek 		nports = mgr->nv4ports;
2355*00b67f09SDavid van Moolenbroek 	} else {
2356*00b67f09SDavid van Moolenbroek 		ports = mgr->v6ports;
2357*00b67f09SDavid van Moolenbroek 		nports = mgr->nv6ports;
2358*00b67f09SDavid van Moolenbroek 	}
2359*00b67f09SDavid van Moolenbroek 	if (ports == NULL)
2360*00b67f09SDavid van Moolenbroek 		goto unlock;
2361*00b67f09SDavid van Moolenbroek 
2362*00b67f09SDavid van Moolenbroek 	port = isc_sockaddr_getport(sockaddrp);
2363*00b67f09SDavid van Moolenbroek 	if (bsearch(&port, ports, nports, sizeof(in_port_t), port_cmp) != NULL)
2364*00b67f09SDavid van Moolenbroek 		available = ISC_TRUE;
2365*00b67f09SDavid van Moolenbroek 
2366*00b67f09SDavid van Moolenbroek unlock:
2367*00b67f09SDavid van Moolenbroek 	PORTBUFUNLOCK(mgr);
2368*00b67f09SDavid van Moolenbroek 	return (available);
2369*00b67f09SDavid van Moolenbroek }
2370*00b67f09SDavid van Moolenbroek 
2371*00b67f09SDavid van Moolenbroek #define ATTRMATCH(_a1, _a2, _mask) (((_a1) & (_mask)) == ((_a2) & (_mask)))
2372*00b67f09SDavid van Moolenbroek 
2373*00b67f09SDavid van Moolenbroek static isc_boolean_t
local_addr_match(dns_dispatch_t * disp,isc_sockaddr_t * addr)2374*00b67f09SDavid van Moolenbroek local_addr_match(dns_dispatch_t *disp, isc_sockaddr_t *addr) {
2375*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t sockaddr;
2376*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2377*00b67f09SDavid van Moolenbroek 
2378*00b67f09SDavid van Moolenbroek 	REQUIRE(disp->socket != NULL);
2379*00b67f09SDavid van Moolenbroek 
2380*00b67f09SDavid van Moolenbroek 	if (addr == NULL)
2381*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
2382*00b67f09SDavid van Moolenbroek 
2383*00b67f09SDavid van Moolenbroek 	/*
2384*00b67f09SDavid van Moolenbroek 	 * Don't match wildcard ports unless the port is available in the
2385*00b67f09SDavid van Moolenbroek 	 * current configuration.
2386*00b67f09SDavid van Moolenbroek 	 */
2387*00b67f09SDavid van Moolenbroek 	if (isc_sockaddr_getport(addr) == 0 &&
2388*00b67f09SDavid van Moolenbroek 	    isc_sockaddr_getport(&disp->local) == 0 &&
2389*00b67f09SDavid van Moolenbroek 	    !portavailable(disp->mgr, disp->socket, NULL)) {
2390*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
2391*00b67f09SDavid van Moolenbroek 	}
2392*00b67f09SDavid van Moolenbroek 
2393*00b67f09SDavid van Moolenbroek 	/*
2394*00b67f09SDavid van Moolenbroek 	 * Check if we match the binding <address,port>.
2395*00b67f09SDavid van Moolenbroek 	 * Wildcard ports match/fail here.
2396*00b67f09SDavid van Moolenbroek 	 */
2397*00b67f09SDavid van Moolenbroek 	if (isc_sockaddr_equal(&disp->local, addr))
2398*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
2399*00b67f09SDavid van Moolenbroek 	if (isc_sockaddr_getport(addr) == 0)
2400*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
2401*00b67f09SDavid van Moolenbroek 
2402*00b67f09SDavid van Moolenbroek 	/*
2403*00b67f09SDavid van Moolenbroek 	 * Check if we match a bound wildcard port <address,port>.
2404*00b67f09SDavid van Moolenbroek 	 */
2405*00b67f09SDavid van Moolenbroek 	if (!isc_sockaddr_eqaddr(&disp->local, addr))
2406*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
2407*00b67f09SDavid van Moolenbroek 	result = isc_socket_getsockname(disp->socket, &sockaddr);
2408*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2409*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
2410*00b67f09SDavid van Moolenbroek 
2411*00b67f09SDavid van Moolenbroek 	return (isc_sockaddr_equal(&sockaddr, addr));
2412*00b67f09SDavid van Moolenbroek }
2413*00b67f09SDavid van Moolenbroek 
2414*00b67f09SDavid van Moolenbroek /*
2415*00b67f09SDavid van Moolenbroek  * Requires mgr be locked.
2416*00b67f09SDavid van Moolenbroek  *
2417*00b67f09SDavid van Moolenbroek  * No dispatcher can be locked by this thread when calling this function.
2418*00b67f09SDavid van Moolenbroek  *
2419*00b67f09SDavid van Moolenbroek  *
2420*00b67f09SDavid van Moolenbroek  * NOTE:
2421*00b67f09SDavid van Moolenbroek  *	If a matching dispatcher is found, it is locked after this function
2422*00b67f09SDavid van Moolenbroek  *	returns, and must be unlocked by the caller.
2423*00b67f09SDavid van Moolenbroek  */
2424*00b67f09SDavid van Moolenbroek static isc_result_t
dispatch_find(dns_dispatchmgr_t * mgr,isc_sockaddr_t * local,unsigned int attributes,unsigned int mask,dns_dispatch_t ** dispp)2425*00b67f09SDavid van Moolenbroek dispatch_find(dns_dispatchmgr_t *mgr, isc_sockaddr_t *local,
2426*00b67f09SDavid van Moolenbroek 	      unsigned int attributes, unsigned int mask,
2427*00b67f09SDavid van Moolenbroek 	      dns_dispatch_t **dispp)
2428*00b67f09SDavid van Moolenbroek {
2429*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
2430*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2431*00b67f09SDavid van Moolenbroek 
2432*00b67f09SDavid van Moolenbroek 	/*
2433*00b67f09SDavid van Moolenbroek 	 * Make certain that we will not match a private or exclusive dispatch.
2434*00b67f09SDavid van Moolenbroek 	 */
2435*00b67f09SDavid van Moolenbroek 	attributes &= ~(DNS_DISPATCHATTR_PRIVATE|DNS_DISPATCHATTR_EXCLUSIVE);
2436*00b67f09SDavid van Moolenbroek 	mask |= (DNS_DISPATCHATTR_PRIVATE|DNS_DISPATCHATTR_EXCLUSIVE);
2437*00b67f09SDavid van Moolenbroek 
2438*00b67f09SDavid van Moolenbroek 	disp = ISC_LIST_HEAD(mgr->list);
2439*00b67f09SDavid van Moolenbroek 	while (disp != NULL) {
2440*00b67f09SDavid van Moolenbroek 		LOCK(&disp->lock);
2441*00b67f09SDavid van Moolenbroek 		if ((disp->shutting_down == 0)
2442*00b67f09SDavid van Moolenbroek 		    && ATTRMATCH(disp->attributes, attributes, mask)
2443*00b67f09SDavid van Moolenbroek 		    && local_addr_match(disp, local))
2444*00b67f09SDavid van Moolenbroek 			break;
2445*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
2446*00b67f09SDavid van Moolenbroek 		disp = ISC_LIST_NEXT(disp, link);
2447*00b67f09SDavid van Moolenbroek 	}
2448*00b67f09SDavid van Moolenbroek 
2449*00b67f09SDavid van Moolenbroek 	if (disp == NULL) {
2450*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOTFOUND;
2451*00b67f09SDavid van Moolenbroek 		goto out;
2452*00b67f09SDavid van Moolenbroek 	}
2453*00b67f09SDavid van Moolenbroek 
2454*00b67f09SDavid van Moolenbroek 	*dispp = disp;
2455*00b67f09SDavid van Moolenbroek 	result = ISC_R_SUCCESS;
2456*00b67f09SDavid van Moolenbroek  out:
2457*00b67f09SDavid van Moolenbroek 
2458*00b67f09SDavid van Moolenbroek 	return (result);
2459*00b67f09SDavid van Moolenbroek }
2460*00b67f09SDavid van Moolenbroek 
2461*00b67f09SDavid van Moolenbroek static isc_result_t
qid_allocate(dns_dispatchmgr_t * mgr,unsigned int buckets,unsigned int increment,dns_qid_t ** qidp,isc_boolean_t needsocktable)2462*00b67f09SDavid van Moolenbroek qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
2463*00b67f09SDavid van Moolenbroek 	     unsigned int increment, dns_qid_t **qidp,
2464*00b67f09SDavid van Moolenbroek 	     isc_boolean_t needsocktable)
2465*00b67f09SDavid van Moolenbroek {
2466*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
2467*00b67f09SDavid van Moolenbroek 	unsigned int i;
2468*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2469*00b67f09SDavid van Moolenbroek 
2470*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2471*00b67f09SDavid van Moolenbroek 	REQUIRE(buckets < 2097169);  /* next prime > 65536 * 32 */
2472*00b67f09SDavid van Moolenbroek 	REQUIRE(increment > buckets);
2473*00b67f09SDavid van Moolenbroek 	REQUIRE(qidp != NULL && *qidp == NULL);
2474*00b67f09SDavid van Moolenbroek 
2475*00b67f09SDavid van Moolenbroek 	qid = isc_mem_get(mgr->mctx, sizeof(*qid));
2476*00b67f09SDavid van Moolenbroek 	if (qid == NULL)
2477*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
2478*00b67f09SDavid van Moolenbroek 
2479*00b67f09SDavid van Moolenbroek 	qid->qid_table = isc_mem_get(mgr->mctx,
2480*00b67f09SDavid van Moolenbroek 				     buckets * sizeof(dns_displist_t));
2481*00b67f09SDavid van Moolenbroek 	if (qid->qid_table == NULL) {
2482*00b67f09SDavid van Moolenbroek 		isc_mem_put(mgr->mctx, qid, sizeof(*qid));
2483*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
2484*00b67f09SDavid van Moolenbroek 	}
2485*00b67f09SDavid van Moolenbroek 
2486*00b67f09SDavid van Moolenbroek 	qid->sock_table = NULL;
2487*00b67f09SDavid van Moolenbroek 	if (needsocktable) {
2488*00b67f09SDavid van Moolenbroek 		qid->sock_table = isc_mem_get(mgr->mctx, buckets *
2489*00b67f09SDavid van Moolenbroek 					      sizeof(dispsocketlist_t));
2490*00b67f09SDavid van Moolenbroek 		if (qid->sock_table == NULL) {
2491*00b67f09SDavid van Moolenbroek 			isc_mem_put(mgr->mctx, qid->qid_table,
2492*00b67f09SDavid van Moolenbroek 				    buckets * sizeof(dns_displist_t));
2493*00b67f09SDavid van Moolenbroek 			isc_mem_put(mgr->mctx, qid, sizeof(*qid));
2494*00b67f09SDavid van Moolenbroek 			return (ISC_R_NOMEMORY);
2495*00b67f09SDavid van Moolenbroek 		}
2496*00b67f09SDavid van Moolenbroek 	}
2497*00b67f09SDavid van Moolenbroek 
2498*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&qid->lock);
2499*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2500*00b67f09SDavid van Moolenbroek 		if (qid->sock_table != NULL) {
2501*00b67f09SDavid van Moolenbroek 			isc_mem_put(mgr->mctx, qid->sock_table,
2502*00b67f09SDavid van Moolenbroek 				    buckets * sizeof(dispsocketlist_t));
2503*00b67f09SDavid van Moolenbroek 		}
2504*00b67f09SDavid van Moolenbroek 		isc_mem_put(mgr->mctx, qid->qid_table,
2505*00b67f09SDavid van Moolenbroek 			    buckets * sizeof(dns_displist_t));
2506*00b67f09SDavid van Moolenbroek 		isc_mem_put(mgr->mctx, qid, sizeof(*qid));
2507*00b67f09SDavid van Moolenbroek 		return (result);
2508*00b67f09SDavid van Moolenbroek 	}
2509*00b67f09SDavid van Moolenbroek 
2510*00b67f09SDavid van Moolenbroek 	for (i = 0; i < buckets; i++) {
2511*00b67f09SDavid van Moolenbroek 		ISC_LIST_INIT(qid->qid_table[i]);
2512*00b67f09SDavid van Moolenbroek 		if (qid->sock_table != NULL)
2513*00b67f09SDavid van Moolenbroek 			ISC_LIST_INIT(qid->sock_table[i]);
2514*00b67f09SDavid van Moolenbroek 	}
2515*00b67f09SDavid van Moolenbroek 
2516*00b67f09SDavid van Moolenbroek 	qid->qid_nbuckets = buckets;
2517*00b67f09SDavid van Moolenbroek 	qid->qid_increment = increment;
2518*00b67f09SDavid van Moolenbroek 	qid->magic = QID_MAGIC;
2519*00b67f09SDavid van Moolenbroek 	*qidp = qid;
2520*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2521*00b67f09SDavid van Moolenbroek }
2522*00b67f09SDavid van Moolenbroek 
2523*00b67f09SDavid van Moolenbroek static void
qid_destroy(isc_mem_t * mctx,dns_qid_t ** qidp)2524*00b67f09SDavid van Moolenbroek qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp) {
2525*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
2526*00b67f09SDavid van Moolenbroek 
2527*00b67f09SDavid van Moolenbroek 	REQUIRE(qidp != NULL);
2528*00b67f09SDavid van Moolenbroek 	qid = *qidp;
2529*00b67f09SDavid van Moolenbroek 
2530*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_QID(qid));
2531*00b67f09SDavid van Moolenbroek 
2532*00b67f09SDavid van Moolenbroek 	*qidp = NULL;
2533*00b67f09SDavid van Moolenbroek 	qid->magic = 0;
2534*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, qid->qid_table,
2535*00b67f09SDavid van Moolenbroek 		    qid->qid_nbuckets * sizeof(dns_displist_t));
2536*00b67f09SDavid van Moolenbroek 	if (qid->sock_table != NULL) {
2537*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, qid->sock_table,
2538*00b67f09SDavid van Moolenbroek 			    qid->qid_nbuckets * sizeof(dispsocketlist_t));
2539*00b67f09SDavid van Moolenbroek 	}
2540*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&qid->lock);
2541*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, qid, sizeof(*qid));
2542*00b67f09SDavid van Moolenbroek }
2543*00b67f09SDavid van Moolenbroek 
2544*00b67f09SDavid van Moolenbroek /*
2545*00b67f09SDavid van Moolenbroek  * Allocate and set important limits.
2546*00b67f09SDavid van Moolenbroek  */
2547*00b67f09SDavid van Moolenbroek static isc_result_t
dispatch_allocate(dns_dispatchmgr_t * mgr,unsigned int maxrequests,dns_dispatch_t ** dispp)2548*00b67f09SDavid van Moolenbroek dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests,
2549*00b67f09SDavid van Moolenbroek 		  dns_dispatch_t **dispp)
2550*00b67f09SDavid van Moolenbroek {
2551*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
2552*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2553*00b67f09SDavid van Moolenbroek 
2554*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2555*00b67f09SDavid van Moolenbroek 	REQUIRE(dispp != NULL && *dispp == NULL);
2556*00b67f09SDavid van Moolenbroek 
2557*00b67f09SDavid van Moolenbroek 	/*
2558*00b67f09SDavid van Moolenbroek 	 * Set up the dispatcher, mostly.  Don't bother setting some of
2559*00b67f09SDavid van Moolenbroek 	 * the options that are controlled by tcp vs. udp, etc.
2560*00b67f09SDavid van Moolenbroek 	 */
2561*00b67f09SDavid van Moolenbroek 
2562*00b67f09SDavid van Moolenbroek 	disp = isc_mempool_get(mgr->dpool);
2563*00b67f09SDavid van Moolenbroek 	if (disp == NULL)
2564*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
2565*00b67f09SDavid van Moolenbroek 
2566*00b67f09SDavid van Moolenbroek 	disp->magic = 0;
2567*00b67f09SDavid van Moolenbroek 	disp->mgr = mgr;
2568*00b67f09SDavid van Moolenbroek 	disp->maxrequests = maxrequests;
2569*00b67f09SDavid van Moolenbroek 	disp->attributes = 0;
2570*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(disp, link);
2571*00b67f09SDavid van Moolenbroek 	disp->refcount = 1;
2572*00b67f09SDavid van Moolenbroek 	disp->recv_pending = 0;
2573*00b67f09SDavid van Moolenbroek 	memset(&disp->local, 0, sizeof(disp->local));
2574*00b67f09SDavid van Moolenbroek 	disp->localport = 0;
2575*00b67f09SDavid van Moolenbroek 	disp->shutting_down = 0;
2576*00b67f09SDavid van Moolenbroek 	disp->shutdown_out = 0;
2577*00b67f09SDavid van Moolenbroek 	disp->connected = 0;
2578*00b67f09SDavid van Moolenbroek 	disp->tcpmsg_valid = 0;
2579*00b67f09SDavid van Moolenbroek 	disp->shutdown_why = ISC_R_UNEXPECTED;
2580*00b67f09SDavid van Moolenbroek 	disp->requests = 0;
2581*00b67f09SDavid van Moolenbroek 	disp->tcpbuffers = 0;
2582*00b67f09SDavid van Moolenbroek 	disp->qid = NULL;
2583*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(disp->activesockets);
2584*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(disp->inactivesockets);
2585*00b67f09SDavid van Moolenbroek 	disp->nsockets = 0;
2586*00b67f09SDavid van Moolenbroek 	dispatch_initrandom(&disp->arc4ctx, mgr->entropy, NULL);
2587*00b67f09SDavid van Moolenbroek 	disp->port_table = NULL;
2588*00b67f09SDavid van Moolenbroek 	disp->portpool = NULL;
2589*00b67f09SDavid van Moolenbroek 	disp->dscp = -1;
2590*00b67f09SDavid van Moolenbroek 
2591*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&disp->lock);
2592*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2593*00b67f09SDavid van Moolenbroek 		goto deallocate;
2594*00b67f09SDavid van Moolenbroek 
2595*00b67f09SDavid van Moolenbroek 	disp->failsafe_ev = allocate_devent(disp);
2596*00b67f09SDavid van Moolenbroek 	if (disp->failsafe_ev == NULL) {
2597*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
2598*00b67f09SDavid van Moolenbroek 		goto kill_lock;
2599*00b67f09SDavid van Moolenbroek 	}
2600*00b67f09SDavid van Moolenbroek 
2601*00b67f09SDavid van Moolenbroek 	disp->magic = DISPATCH_MAGIC;
2602*00b67f09SDavid van Moolenbroek 
2603*00b67f09SDavid van Moolenbroek 	*dispp = disp;
2604*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2605*00b67f09SDavid van Moolenbroek 
2606*00b67f09SDavid van Moolenbroek 	/*
2607*00b67f09SDavid van Moolenbroek 	 * error returns
2608*00b67f09SDavid van Moolenbroek 	 */
2609*00b67f09SDavid van Moolenbroek  kill_lock:
2610*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&disp->lock);
2611*00b67f09SDavid van Moolenbroek  deallocate:
2612*00b67f09SDavid van Moolenbroek 	isc_mempool_put(mgr->dpool, disp);
2613*00b67f09SDavid van Moolenbroek 
2614*00b67f09SDavid van Moolenbroek 	return (result);
2615*00b67f09SDavid van Moolenbroek }
2616*00b67f09SDavid van Moolenbroek 
2617*00b67f09SDavid van Moolenbroek 
2618*00b67f09SDavid van Moolenbroek /*
2619*00b67f09SDavid van Moolenbroek  * MUST be unlocked, and not used by anything.
2620*00b67f09SDavid van Moolenbroek  */
2621*00b67f09SDavid van Moolenbroek static void
dispatch_free(dns_dispatch_t ** dispp)2622*00b67f09SDavid van Moolenbroek dispatch_free(dns_dispatch_t **dispp) {
2623*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
2624*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
2625*00b67f09SDavid van Moolenbroek 	int i;
2626*00b67f09SDavid van Moolenbroek 
2627*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(*dispp));
2628*00b67f09SDavid van Moolenbroek 	disp = *dispp;
2629*00b67f09SDavid van Moolenbroek 	*dispp = NULL;
2630*00b67f09SDavid van Moolenbroek 
2631*00b67f09SDavid van Moolenbroek 	mgr = disp->mgr;
2632*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2633*00b67f09SDavid van Moolenbroek 
2634*00b67f09SDavid van Moolenbroek 	if (disp->tcpmsg_valid) {
2635*00b67f09SDavid van Moolenbroek 		dns_tcpmsg_invalidate(&disp->tcpmsg);
2636*00b67f09SDavid van Moolenbroek 		disp->tcpmsg_valid = 0;
2637*00b67f09SDavid van Moolenbroek 	}
2638*00b67f09SDavid van Moolenbroek 
2639*00b67f09SDavid van Moolenbroek 	INSIST(disp->tcpbuffers == 0);
2640*00b67f09SDavid van Moolenbroek 	INSIST(disp->requests == 0);
2641*00b67f09SDavid van Moolenbroek 	INSIST(disp->recv_pending == 0);
2642*00b67f09SDavid van Moolenbroek 	INSIST(ISC_LIST_EMPTY(disp->activesockets));
2643*00b67f09SDavid van Moolenbroek 	INSIST(ISC_LIST_EMPTY(disp->inactivesockets));
2644*00b67f09SDavid van Moolenbroek 
2645*00b67f09SDavid van Moolenbroek 	isc_mempool_put(mgr->depool, disp->failsafe_ev);
2646*00b67f09SDavid van Moolenbroek 	disp->failsafe_ev = NULL;
2647*00b67f09SDavid van Moolenbroek 
2648*00b67f09SDavid van Moolenbroek 	if (disp->qid != NULL)
2649*00b67f09SDavid van Moolenbroek 		qid_destroy(mgr->mctx, &disp->qid);
2650*00b67f09SDavid van Moolenbroek 
2651*00b67f09SDavid van Moolenbroek 	if (disp->port_table != NULL) {
2652*00b67f09SDavid van Moolenbroek 		for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++)
2653*00b67f09SDavid van Moolenbroek 			INSIST(ISC_LIST_EMPTY(disp->port_table[i]));
2654*00b67f09SDavid van Moolenbroek 		isc_mem_put(mgr->mctx, disp->port_table,
2655*00b67f09SDavid van Moolenbroek 			    sizeof(disp->port_table[0]) *
2656*00b67f09SDavid van Moolenbroek 			    DNS_DISPATCH_PORTTABLESIZE);
2657*00b67f09SDavid van Moolenbroek 	}
2658*00b67f09SDavid van Moolenbroek 
2659*00b67f09SDavid van Moolenbroek 	if (disp->portpool != NULL)
2660*00b67f09SDavid van Moolenbroek 		isc_mempool_destroy(&disp->portpool);
2661*00b67f09SDavid van Moolenbroek 
2662*00b67f09SDavid van Moolenbroek 	disp->mgr = NULL;
2663*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&disp->lock);
2664*00b67f09SDavid van Moolenbroek 	disp->magic = 0;
2665*00b67f09SDavid van Moolenbroek 	isc_mempool_put(mgr->dpool, disp);
2666*00b67f09SDavid van Moolenbroek }
2667*00b67f09SDavid van Moolenbroek 
2668*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_createtcp(dns_dispatchmgr_t * mgr,isc_socket_t * sock,isc_taskmgr_t * taskmgr,unsigned int buffersize,unsigned int maxbuffers,unsigned int maxrequests,unsigned int buckets,unsigned int increment,unsigned int attributes,dns_dispatch_t ** dispp)2669*00b67f09SDavid van Moolenbroek dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
2670*00b67f09SDavid van Moolenbroek 		       isc_taskmgr_t *taskmgr, unsigned int buffersize,
2671*00b67f09SDavid van Moolenbroek 		       unsigned int maxbuffers, unsigned int maxrequests,
2672*00b67f09SDavid van Moolenbroek 		       unsigned int buckets, unsigned int increment,
2673*00b67f09SDavid van Moolenbroek 		       unsigned int attributes, dns_dispatch_t **dispp)
2674*00b67f09SDavid van Moolenbroek {
2675*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2676*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
2677*00b67f09SDavid van Moolenbroek 
2678*00b67f09SDavid van Moolenbroek 	UNUSED(maxbuffers);
2679*00b67f09SDavid van Moolenbroek 	UNUSED(buffersize);
2680*00b67f09SDavid van Moolenbroek 
2681*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2682*00b67f09SDavid van Moolenbroek 	REQUIRE(isc_socket_gettype(sock) == isc_sockettype_tcp);
2683*00b67f09SDavid van Moolenbroek 	REQUIRE((attributes & DNS_DISPATCHATTR_TCP) != 0);
2684*00b67f09SDavid van Moolenbroek 	REQUIRE((attributes & DNS_DISPATCHATTR_UDP) == 0);
2685*00b67f09SDavid van Moolenbroek 
2686*00b67f09SDavid van Moolenbroek 	attributes |= DNS_DISPATCHATTR_PRIVATE;  /* XXXMLG */
2687*00b67f09SDavid van Moolenbroek 
2688*00b67f09SDavid van Moolenbroek 	LOCK(&mgr->lock);
2689*00b67f09SDavid van Moolenbroek 
2690*00b67f09SDavid van Moolenbroek 	/*
2691*00b67f09SDavid van Moolenbroek 	 * dispatch_allocate() checks mgr for us.
2692*00b67f09SDavid van Moolenbroek 	 * qid_allocate() checks buckets and increment for us.
2693*00b67f09SDavid van Moolenbroek 	 */
2694*00b67f09SDavid van Moolenbroek 	disp = NULL;
2695*00b67f09SDavid van Moolenbroek 	result = dispatch_allocate(mgr, maxrequests, &disp);
2696*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2697*00b67f09SDavid van Moolenbroek 		UNLOCK(&mgr->lock);
2698*00b67f09SDavid van Moolenbroek 		return (result);
2699*00b67f09SDavid van Moolenbroek 	}
2700*00b67f09SDavid van Moolenbroek 
2701*00b67f09SDavid van Moolenbroek 	result = qid_allocate(mgr, buckets, increment, &disp->qid, ISC_FALSE);
2702*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2703*00b67f09SDavid van Moolenbroek 		goto deallocate_dispatch;
2704*00b67f09SDavid van Moolenbroek 
2705*00b67f09SDavid van Moolenbroek 	disp->socktype = isc_sockettype_tcp;
2706*00b67f09SDavid van Moolenbroek 	disp->socket = NULL;
2707*00b67f09SDavid van Moolenbroek 	isc_socket_attach(sock, &disp->socket);
2708*00b67f09SDavid van Moolenbroek 
2709*00b67f09SDavid van Moolenbroek 	disp->sepool = NULL;
2710*00b67f09SDavid van Moolenbroek 
2711*00b67f09SDavid van Moolenbroek 	disp->ntasks = 1;
2712*00b67f09SDavid van Moolenbroek 	disp->task[0] = NULL;
2713*00b67f09SDavid van Moolenbroek 	result = isc_task_create(taskmgr, 0, &disp->task[0]);
2714*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2715*00b67f09SDavid van Moolenbroek 		goto kill_socket;
2716*00b67f09SDavid van Moolenbroek 
2717*00b67f09SDavid van Moolenbroek 	disp->ctlevent = isc_event_allocate(mgr->mctx, disp,
2718*00b67f09SDavid van Moolenbroek 					    DNS_EVENT_DISPATCHCONTROL,
2719*00b67f09SDavid van Moolenbroek 					    destroy_disp, disp,
2720*00b67f09SDavid van Moolenbroek 					    sizeof(isc_event_t));
2721*00b67f09SDavid van Moolenbroek 	if (disp->ctlevent == NULL) {
2722*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
2723*00b67f09SDavid van Moolenbroek 		goto kill_task;
2724*00b67f09SDavid van Moolenbroek 	}
2725*00b67f09SDavid van Moolenbroek 
2726*00b67f09SDavid van Moolenbroek 	isc_task_setname(disp->task[0], "tcpdispatch", disp);
2727*00b67f09SDavid van Moolenbroek 
2728*00b67f09SDavid van Moolenbroek 	dns_tcpmsg_init(mgr->mctx, disp->socket, &disp->tcpmsg);
2729*00b67f09SDavid van Moolenbroek 	disp->tcpmsg_valid = 1;
2730*00b67f09SDavid van Moolenbroek 
2731*00b67f09SDavid van Moolenbroek 	disp->attributes = attributes;
2732*00b67f09SDavid van Moolenbroek 
2733*00b67f09SDavid van Moolenbroek 	/*
2734*00b67f09SDavid van Moolenbroek 	 * Append it to the dispatcher list.
2735*00b67f09SDavid van Moolenbroek 	 */
2736*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(mgr->list, disp, link);
2737*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
2738*00b67f09SDavid van Moolenbroek 
2739*00b67f09SDavid van Moolenbroek 	mgr_log(mgr, LVL(90), "created TCP dispatcher %p", disp);
2740*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90), "created task %p", disp->task[0]);
2741*00b67f09SDavid van Moolenbroek 
2742*00b67f09SDavid van Moolenbroek 	*dispp = disp;
2743*00b67f09SDavid van Moolenbroek 
2744*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2745*00b67f09SDavid van Moolenbroek 
2746*00b67f09SDavid van Moolenbroek 	/*
2747*00b67f09SDavid van Moolenbroek 	 * Error returns.
2748*00b67f09SDavid van Moolenbroek 	 */
2749*00b67f09SDavid van Moolenbroek  kill_task:
2750*00b67f09SDavid van Moolenbroek 	isc_task_detach(&disp->task[0]);
2751*00b67f09SDavid van Moolenbroek  kill_socket:
2752*00b67f09SDavid van Moolenbroek 	isc_socket_detach(&disp->socket);
2753*00b67f09SDavid van Moolenbroek  deallocate_dispatch:
2754*00b67f09SDavid van Moolenbroek 	dispatch_free(&disp);
2755*00b67f09SDavid van Moolenbroek 
2756*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
2757*00b67f09SDavid van Moolenbroek 
2758*00b67f09SDavid van Moolenbroek 	return (result);
2759*00b67f09SDavid van Moolenbroek }
2760*00b67f09SDavid van Moolenbroek 
2761*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_getudp_dup(dns_dispatchmgr_t * mgr,isc_socketmgr_t * sockmgr,isc_taskmgr_t * taskmgr,isc_sockaddr_t * localaddr,unsigned int buffersize,unsigned int maxbuffers,unsigned int maxrequests,unsigned int buckets,unsigned int increment,unsigned int attributes,unsigned int mask,dns_dispatch_t ** dispp,dns_dispatch_t * dup_dispatch)2762*00b67f09SDavid van Moolenbroek dns_dispatch_getudp_dup(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
2763*00b67f09SDavid van Moolenbroek 		    isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr,
2764*00b67f09SDavid van Moolenbroek 		    unsigned int buffersize,
2765*00b67f09SDavid van Moolenbroek 		    unsigned int maxbuffers, unsigned int maxrequests,
2766*00b67f09SDavid van Moolenbroek 		    unsigned int buckets, unsigned int increment,
2767*00b67f09SDavid van Moolenbroek 		    unsigned int attributes, unsigned int mask,
2768*00b67f09SDavid van Moolenbroek 		    dns_dispatch_t **dispp, dns_dispatch_t *dup_dispatch)
2769*00b67f09SDavid van Moolenbroek {
2770*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2771*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp = NULL;
2772*00b67f09SDavid van Moolenbroek 
2773*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
2774*00b67f09SDavid van Moolenbroek 	REQUIRE(sockmgr != NULL);
2775*00b67f09SDavid van Moolenbroek 	REQUIRE(localaddr != NULL);
2776*00b67f09SDavid van Moolenbroek 	REQUIRE(taskmgr != NULL);
2777*00b67f09SDavid van Moolenbroek 	REQUIRE(buffersize >= 512 && buffersize < (64 * 1024));
2778*00b67f09SDavid van Moolenbroek 	REQUIRE(maxbuffers > 0);
2779*00b67f09SDavid van Moolenbroek 	REQUIRE(buckets < 2097169);  /* next prime > 65536 * 32 */
2780*00b67f09SDavid van Moolenbroek 	REQUIRE(increment > buckets);
2781*00b67f09SDavid van Moolenbroek 	REQUIRE(dispp != NULL && *dispp == NULL);
2782*00b67f09SDavid van Moolenbroek 	REQUIRE((attributes & DNS_DISPATCHATTR_TCP) == 0);
2783*00b67f09SDavid van Moolenbroek 
2784*00b67f09SDavid van Moolenbroek 	result = dns_dispatchmgr_setudp(mgr, buffersize, maxbuffers,
2785*00b67f09SDavid van Moolenbroek 					maxrequests, buckets, increment);
2786*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2787*00b67f09SDavid van Moolenbroek 		return (result);
2788*00b67f09SDavid van Moolenbroek 
2789*00b67f09SDavid van Moolenbroek 	LOCK(&mgr->lock);
2790*00b67f09SDavid van Moolenbroek 
2791*00b67f09SDavid van Moolenbroek 	if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
2792*00b67f09SDavid van Moolenbroek 		REQUIRE(isc_sockaddr_getport(localaddr) == 0);
2793*00b67f09SDavid van Moolenbroek 		goto createudp;
2794*00b67f09SDavid van Moolenbroek 	}
2795*00b67f09SDavid van Moolenbroek 
2796*00b67f09SDavid van Moolenbroek 	/*
2797*00b67f09SDavid van Moolenbroek 	 * See if we have a dispatcher that matches.
2798*00b67f09SDavid van Moolenbroek 	 */
2799*00b67f09SDavid van Moolenbroek 	if (dup_dispatch == NULL) {
2800*00b67f09SDavid van Moolenbroek 		result = dispatch_find(mgr, localaddr, attributes, mask, &disp);
2801*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS) {
2802*00b67f09SDavid van Moolenbroek 			disp->refcount++;
2803*00b67f09SDavid van Moolenbroek 
2804*00b67f09SDavid van Moolenbroek 			if (disp->maxrequests < maxrequests)
2805*00b67f09SDavid van Moolenbroek 				disp->maxrequests = maxrequests;
2806*00b67f09SDavid van Moolenbroek 
2807*00b67f09SDavid van Moolenbroek 			if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0
2808*00b67f09SDavid van Moolenbroek 			    && (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0)
2809*00b67f09SDavid van Moolenbroek 			{
2810*00b67f09SDavid van Moolenbroek 				disp->attributes |= DNS_DISPATCHATTR_NOLISTEN;
2811*00b67f09SDavid van Moolenbroek 				if (disp->recv_pending != 0)
2812*00b67f09SDavid van Moolenbroek 					isc_socket_cancel(disp->socket,
2813*00b67f09SDavid van Moolenbroek 							  disp->task[0],
2814*00b67f09SDavid van Moolenbroek 							  ISC_SOCKCANCEL_RECV);
2815*00b67f09SDavid van Moolenbroek 			}
2816*00b67f09SDavid van Moolenbroek 
2817*00b67f09SDavid van Moolenbroek 			UNLOCK(&disp->lock);
2818*00b67f09SDavid van Moolenbroek 			UNLOCK(&mgr->lock);
2819*00b67f09SDavid van Moolenbroek 
2820*00b67f09SDavid van Moolenbroek 			*dispp = disp;
2821*00b67f09SDavid van Moolenbroek 
2822*00b67f09SDavid van Moolenbroek 			return (ISC_R_SUCCESS);
2823*00b67f09SDavid van Moolenbroek 		}
2824*00b67f09SDavid van Moolenbroek 	}
2825*00b67f09SDavid van Moolenbroek 
2826*00b67f09SDavid van Moolenbroek  createudp:
2827*00b67f09SDavid van Moolenbroek 	/*
2828*00b67f09SDavid van Moolenbroek 	 * Nope, create one.
2829*00b67f09SDavid van Moolenbroek 	 */
2830*00b67f09SDavid van Moolenbroek 	result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr,
2831*00b67f09SDavid van Moolenbroek 				    maxrequests, attributes, &disp,
2832*00b67f09SDavid van Moolenbroek 				    dup_dispatch == NULL
2833*00b67f09SDavid van Moolenbroek 					    ? NULL
2834*00b67f09SDavid van Moolenbroek 					    : dup_dispatch->socket);
2835*00b67f09SDavid van Moolenbroek 
2836*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
2837*00b67f09SDavid van Moolenbroek 		UNLOCK(&mgr->lock);
2838*00b67f09SDavid van Moolenbroek 		return (result);
2839*00b67f09SDavid van Moolenbroek 	}
2840*00b67f09SDavid van Moolenbroek 
2841*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
2842*00b67f09SDavid van Moolenbroek 	*dispp = disp;
2843*00b67f09SDavid van Moolenbroek 
2844*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
2845*00b67f09SDavid van Moolenbroek }
2846*00b67f09SDavid van Moolenbroek 
2847*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_getudp(dns_dispatchmgr_t * mgr,isc_socketmgr_t * sockmgr,isc_taskmgr_t * taskmgr,isc_sockaddr_t * localaddr,unsigned int buffersize,unsigned int maxbuffers,unsigned int maxrequests,unsigned int buckets,unsigned int increment,unsigned int attributes,unsigned int mask,dns_dispatch_t ** dispp)2848*00b67f09SDavid van Moolenbroek dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
2849*00b67f09SDavid van Moolenbroek 		    isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr,
2850*00b67f09SDavid van Moolenbroek 		    unsigned int buffersize,
2851*00b67f09SDavid van Moolenbroek 		    unsigned int maxbuffers, unsigned int maxrequests,
2852*00b67f09SDavid van Moolenbroek 		    unsigned int buckets, unsigned int increment,
2853*00b67f09SDavid van Moolenbroek 		    unsigned int attributes, unsigned int mask,
2854*00b67f09SDavid van Moolenbroek 		    dns_dispatch_t **dispp)
2855*00b67f09SDavid van Moolenbroek {
2856*00b67f09SDavid van Moolenbroek 	return (dns_dispatch_getudp_dup(mgr, sockmgr, taskmgr, localaddr,
2857*00b67f09SDavid van Moolenbroek 					buffersize, maxbuffers, maxrequests,
2858*00b67f09SDavid van Moolenbroek 					buckets, increment, attributes,
2859*00b67f09SDavid van Moolenbroek 					mask, dispp, NULL));
2860*00b67f09SDavid van Moolenbroek }
2861*00b67f09SDavid van Moolenbroek 
2862*00b67f09SDavid van Moolenbroek /*
2863*00b67f09SDavid van Moolenbroek  * mgr should be locked.
2864*00b67f09SDavid van Moolenbroek  */
2865*00b67f09SDavid van Moolenbroek 
2866*00b67f09SDavid van Moolenbroek #ifndef DNS_DISPATCH_HELD
2867*00b67f09SDavid van Moolenbroek #define DNS_DISPATCH_HELD 20U
2868*00b67f09SDavid van Moolenbroek #endif
2869*00b67f09SDavid van Moolenbroek 
2870*00b67f09SDavid van Moolenbroek static isc_result_t
get_udpsocket(dns_dispatchmgr_t * mgr,dns_dispatch_t * disp,isc_socketmgr_t * sockmgr,isc_sockaddr_t * localaddr,isc_socket_t ** sockp,isc_socket_t * dup_socket)2871*00b67f09SDavid van Moolenbroek get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp,
2872*00b67f09SDavid van Moolenbroek 	      isc_socketmgr_t *sockmgr, isc_sockaddr_t *localaddr,
2873*00b67f09SDavid van Moolenbroek 	      isc_socket_t **sockp, isc_socket_t *dup_socket)
2874*00b67f09SDavid van Moolenbroek {
2875*00b67f09SDavid van Moolenbroek 	unsigned int i, j;
2876*00b67f09SDavid van Moolenbroek 	isc_socket_t *held[DNS_DISPATCH_HELD];
2877*00b67f09SDavid van Moolenbroek 	isc_sockaddr_t localaddr_bound;
2878*00b67f09SDavid van Moolenbroek 	isc_socket_t *sock = NULL;
2879*00b67f09SDavid van Moolenbroek 	isc_result_t result = ISC_R_SUCCESS;
2880*00b67f09SDavid van Moolenbroek 	isc_boolean_t anyport;
2881*00b67f09SDavid van Moolenbroek 
2882*00b67f09SDavid van Moolenbroek 	INSIST(sockp != NULL && *sockp == NULL);
2883*00b67f09SDavid van Moolenbroek 
2884*00b67f09SDavid van Moolenbroek 	localaddr_bound = *localaddr;
2885*00b67f09SDavid van Moolenbroek 	anyport = ISC_TF(isc_sockaddr_getport(localaddr) == 0);
2886*00b67f09SDavid van Moolenbroek 
2887*00b67f09SDavid van Moolenbroek 	if (anyport) {
2888*00b67f09SDavid van Moolenbroek 		unsigned int nports;
2889*00b67f09SDavid van Moolenbroek 		in_port_t *ports;
2890*00b67f09SDavid van Moolenbroek 
2891*00b67f09SDavid van Moolenbroek 		/*
2892*00b67f09SDavid van Moolenbroek 		 * If no port is specified, we first try to pick up a random
2893*00b67f09SDavid van Moolenbroek 		 * port by ourselves.
2894*00b67f09SDavid van Moolenbroek 		 */
2895*00b67f09SDavid van Moolenbroek 		if (isc_sockaddr_pf(localaddr) == AF_INET) {
2896*00b67f09SDavid van Moolenbroek 			nports = disp->mgr->nv4ports;
2897*00b67f09SDavid van Moolenbroek 			ports = disp->mgr->v4ports;
2898*00b67f09SDavid van Moolenbroek 		} else {
2899*00b67f09SDavid van Moolenbroek 			nports = disp->mgr->nv6ports;
2900*00b67f09SDavid van Moolenbroek 			ports = disp->mgr->v6ports;
2901*00b67f09SDavid van Moolenbroek 		}
2902*00b67f09SDavid van Moolenbroek 		if (nports == 0)
2903*00b67f09SDavid van Moolenbroek 			return (ISC_R_ADDRNOTAVAIL);
2904*00b67f09SDavid van Moolenbroek 
2905*00b67f09SDavid van Moolenbroek 		for (i = 0; i < 1024; i++) {
2906*00b67f09SDavid van Moolenbroek 			in_port_t prt;
2907*00b67f09SDavid van Moolenbroek 
2908*00b67f09SDavid van Moolenbroek 			prt = ports[dispatch_uniformrandom(
2909*00b67f09SDavid van Moolenbroek 					DISP_ARC4CTX(disp),
2910*00b67f09SDavid van Moolenbroek 					nports)];
2911*00b67f09SDavid van Moolenbroek 			isc_sockaddr_setport(&localaddr_bound, prt);
2912*00b67f09SDavid van Moolenbroek 			result = open_socket(sockmgr, &localaddr_bound,
2913*00b67f09SDavid van Moolenbroek 					     0, &sock, NULL);
2914*00b67f09SDavid van Moolenbroek 			/*
2915*00b67f09SDavid van Moolenbroek 			 * Continue if the port choosen is already in use
2916*00b67f09SDavid van Moolenbroek 			 * or the OS has reserved it.
2917*00b67f09SDavid van Moolenbroek 			 */
2918*00b67f09SDavid van Moolenbroek 			if (result == ISC_R_NOPERM ||
2919*00b67f09SDavid van Moolenbroek 			    result == ISC_R_ADDRINUSE)
2920*00b67f09SDavid van Moolenbroek 				continue;
2921*00b67f09SDavid van Moolenbroek 			disp->localport = prt;
2922*00b67f09SDavid van Moolenbroek 			*sockp = sock;
2923*00b67f09SDavid van Moolenbroek 			return (result);
2924*00b67f09SDavid van Moolenbroek 		}
2925*00b67f09SDavid van Moolenbroek 
2926*00b67f09SDavid van Moolenbroek 		/*
2927*00b67f09SDavid van Moolenbroek 		 * If this fails 1024 times, we then ask the kernel for
2928*00b67f09SDavid van Moolenbroek 		 * choosing one.
2929*00b67f09SDavid van Moolenbroek 		 */
2930*00b67f09SDavid van Moolenbroek 	} else {
2931*00b67f09SDavid van Moolenbroek 		/* Allow to reuse address for non-random ports. */
2932*00b67f09SDavid van Moolenbroek 		result = open_socket(sockmgr, localaddr,
2933*00b67f09SDavid van Moolenbroek 				     ISC_SOCKET_REUSEADDRESS, &sock,
2934*00b67f09SDavid van Moolenbroek 				     dup_socket);
2935*00b67f09SDavid van Moolenbroek 
2936*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_SUCCESS)
2937*00b67f09SDavid van Moolenbroek 			*sockp = sock;
2938*00b67f09SDavid van Moolenbroek 
2939*00b67f09SDavid van Moolenbroek 		return (result);
2940*00b67f09SDavid van Moolenbroek 	}
2941*00b67f09SDavid van Moolenbroek 
2942*00b67f09SDavid van Moolenbroek 	memset(held, 0, sizeof(held));
2943*00b67f09SDavid van Moolenbroek 	i = 0;
2944*00b67f09SDavid van Moolenbroek 
2945*00b67f09SDavid van Moolenbroek 	for (j = 0; j < 0xffffU; j++) {
2946*00b67f09SDavid van Moolenbroek 		result = open_socket(sockmgr, localaddr, 0, &sock, NULL);
2947*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
2948*00b67f09SDavid van Moolenbroek 			goto end;
2949*00b67f09SDavid van Moolenbroek 		else if (portavailable(mgr, sock, NULL))
2950*00b67f09SDavid van Moolenbroek 			break;
2951*00b67f09SDavid van Moolenbroek 		if (held[i] != NULL)
2952*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&held[i]);
2953*00b67f09SDavid van Moolenbroek 		held[i++] = sock;
2954*00b67f09SDavid van Moolenbroek 		sock = NULL;
2955*00b67f09SDavid van Moolenbroek 		if (i == DNS_DISPATCH_HELD)
2956*00b67f09SDavid van Moolenbroek 			i = 0;
2957*00b67f09SDavid van Moolenbroek 	}
2958*00b67f09SDavid van Moolenbroek 	if (j == 0xffffU) {
2959*00b67f09SDavid van Moolenbroek 		mgr_log(mgr, ISC_LOG_ERROR,
2960*00b67f09SDavid van Moolenbroek 			"avoid-v%s-udp-ports: unable to allocate "
2961*00b67f09SDavid van Moolenbroek 			"an available port",
2962*00b67f09SDavid van Moolenbroek 			isc_sockaddr_pf(localaddr) == AF_INET ? "4" : "6");
2963*00b67f09SDavid van Moolenbroek 		result = ISC_R_FAILURE;
2964*00b67f09SDavid van Moolenbroek 		goto end;
2965*00b67f09SDavid van Moolenbroek 	}
2966*00b67f09SDavid van Moolenbroek 	*sockp = sock;
2967*00b67f09SDavid van Moolenbroek 
2968*00b67f09SDavid van Moolenbroek end:
2969*00b67f09SDavid van Moolenbroek 	for (i = 0; i < DNS_DISPATCH_HELD; i++) {
2970*00b67f09SDavid van Moolenbroek 		if (held[i] != NULL)
2971*00b67f09SDavid van Moolenbroek 			isc_socket_detach(&held[i]);
2972*00b67f09SDavid van Moolenbroek 	}
2973*00b67f09SDavid van Moolenbroek 
2974*00b67f09SDavid van Moolenbroek 	return (result);
2975*00b67f09SDavid van Moolenbroek }
2976*00b67f09SDavid van Moolenbroek 
2977*00b67f09SDavid van Moolenbroek static isc_result_t
dispatch_createudp(dns_dispatchmgr_t * mgr,isc_socketmgr_t * sockmgr,isc_taskmgr_t * taskmgr,isc_sockaddr_t * localaddr,unsigned int maxrequests,unsigned int attributes,dns_dispatch_t ** dispp,isc_socket_t * dup_socket)2978*00b67f09SDavid van Moolenbroek dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
2979*00b67f09SDavid van Moolenbroek 		   isc_taskmgr_t *taskmgr,
2980*00b67f09SDavid van Moolenbroek 		   isc_sockaddr_t *localaddr,
2981*00b67f09SDavid van Moolenbroek 		   unsigned int maxrequests,
2982*00b67f09SDavid van Moolenbroek 		   unsigned int attributes,
2983*00b67f09SDavid van Moolenbroek 		   dns_dispatch_t **dispp,
2984*00b67f09SDavid van Moolenbroek 		   isc_socket_t *dup_socket)
2985*00b67f09SDavid van Moolenbroek {
2986*00b67f09SDavid van Moolenbroek 	isc_result_t result;
2987*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
2988*00b67f09SDavid van Moolenbroek 	isc_socket_t *sock = NULL;
2989*00b67f09SDavid van Moolenbroek 	int i = 0;
2990*00b67f09SDavid van Moolenbroek 
2991*00b67f09SDavid van Moolenbroek 	/*
2992*00b67f09SDavid van Moolenbroek 	 * dispatch_allocate() checks mgr for us.
2993*00b67f09SDavid van Moolenbroek 	 */
2994*00b67f09SDavid van Moolenbroek 	disp = NULL;
2995*00b67f09SDavid van Moolenbroek 	result = dispatch_allocate(mgr, maxrequests, &disp);
2996*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
2997*00b67f09SDavid van Moolenbroek 		return (result);
2998*00b67f09SDavid van Moolenbroek 
2999*00b67f09SDavid van Moolenbroek 	disp->socktype = isc_sockettype_udp;
3000*00b67f09SDavid van Moolenbroek 
3001*00b67f09SDavid van Moolenbroek 	if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0) {
3002*00b67f09SDavid van Moolenbroek 		result = get_udpsocket(mgr, disp, sockmgr, localaddr, &sock,
3003*00b67f09SDavid van Moolenbroek 				       dup_socket);
3004*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
3005*00b67f09SDavid van Moolenbroek 			goto deallocate_dispatch;
3006*00b67f09SDavid van Moolenbroek 
3007*00b67f09SDavid van Moolenbroek 		if (isc_log_wouldlog(dns_lctx, 90)) {
3008*00b67f09SDavid van Moolenbroek 			char addrbuf[ISC_SOCKADDR_FORMATSIZE];
3009*00b67f09SDavid van Moolenbroek 
3010*00b67f09SDavid van Moolenbroek 			isc_sockaddr_format(localaddr, addrbuf,
3011*00b67f09SDavid van Moolenbroek 					    ISC_SOCKADDR_FORMATSIZE);
3012*00b67f09SDavid van Moolenbroek 			mgr_log(mgr, LVL(90), "dns_dispatch_createudp: Created"
3013*00b67f09SDavid van Moolenbroek 				" UDP dispatch for %s with socket fd %d\n",
3014*00b67f09SDavid van Moolenbroek 				addrbuf, isc_socket_getfd(sock));
3015*00b67f09SDavid van Moolenbroek 		}
3016*00b67f09SDavid van Moolenbroek 
3017*00b67f09SDavid van Moolenbroek 	} else {
3018*00b67f09SDavid van Moolenbroek 		isc_sockaddr_t sa_any;
3019*00b67f09SDavid van Moolenbroek 
3020*00b67f09SDavid van Moolenbroek 		/*
3021*00b67f09SDavid van Moolenbroek 		 * For dispatches using exclusive sockets with a specific
3022*00b67f09SDavid van Moolenbroek 		 * source address, we only check if the specified address is
3023*00b67f09SDavid van Moolenbroek 		 * available on the system.  Query sockets will be created later
3024*00b67f09SDavid van Moolenbroek 		 * on demand.
3025*00b67f09SDavid van Moolenbroek 		 */
3026*00b67f09SDavid van Moolenbroek 		isc_sockaddr_anyofpf(&sa_any, isc_sockaddr_pf(localaddr));
3027*00b67f09SDavid van Moolenbroek 		if (!isc_sockaddr_eqaddr(&sa_any, localaddr)) {
3028*00b67f09SDavid van Moolenbroek 			result = open_socket(sockmgr, localaddr, 0, &sock, NULL);
3029*00b67f09SDavid van Moolenbroek 			if (sock != NULL)
3030*00b67f09SDavid van Moolenbroek 				isc_socket_detach(&sock);
3031*00b67f09SDavid van Moolenbroek 			if (result != ISC_R_SUCCESS)
3032*00b67f09SDavid van Moolenbroek 				goto deallocate_dispatch;
3033*00b67f09SDavid van Moolenbroek 		}
3034*00b67f09SDavid van Moolenbroek 
3035*00b67f09SDavid van Moolenbroek 		disp->port_table = isc_mem_get(mgr->mctx,
3036*00b67f09SDavid van Moolenbroek 					       sizeof(disp->port_table[0]) *
3037*00b67f09SDavid van Moolenbroek 					       DNS_DISPATCH_PORTTABLESIZE);
3038*00b67f09SDavid van Moolenbroek 		if (disp->port_table == NULL)
3039*00b67f09SDavid van Moolenbroek 			goto deallocate_dispatch;
3040*00b67f09SDavid van Moolenbroek 		for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++)
3041*00b67f09SDavid van Moolenbroek 			ISC_LIST_INIT(disp->port_table[i]);
3042*00b67f09SDavid van Moolenbroek 
3043*00b67f09SDavid van Moolenbroek 		result = isc_mempool_create(mgr->mctx, sizeof(dispportentry_t),
3044*00b67f09SDavid van Moolenbroek 					    &disp->portpool);
3045*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
3046*00b67f09SDavid van Moolenbroek 			goto deallocate_dispatch;
3047*00b67f09SDavid van Moolenbroek 		isc_mempool_setname(disp->portpool, "disp_portpool");
3048*00b67f09SDavid van Moolenbroek 		isc_mempool_setfreemax(disp->portpool, 128);
3049*00b67f09SDavid van Moolenbroek 	}
3050*00b67f09SDavid van Moolenbroek 	disp->socket = sock;
3051*00b67f09SDavid van Moolenbroek 	disp->local = *localaddr;
3052*00b67f09SDavid van Moolenbroek 
3053*00b67f09SDavid van Moolenbroek 	if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0)
3054*00b67f09SDavid van Moolenbroek 		disp->ntasks = MAX_INTERNAL_TASKS;
3055*00b67f09SDavid van Moolenbroek 	else
3056*00b67f09SDavid van Moolenbroek 		disp->ntasks = 1;
3057*00b67f09SDavid van Moolenbroek 	for (i = 0; i < disp->ntasks; i++) {
3058*00b67f09SDavid van Moolenbroek 		disp->task[i] = NULL;
3059*00b67f09SDavid van Moolenbroek 		result = isc_task_create(taskmgr, 0, &disp->task[i]);
3060*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
3061*00b67f09SDavid van Moolenbroek 			while (--i >= 0) {
3062*00b67f09SDavid van Moolenbroek 				isc_task_shutdown(disp->task[i]);
3063*00b67f09SDavid van Moolenbroek 				isc_task_detach(&disp->task[i]);
3064*00b67f09SDavid van Moolenbroek 			}
3065*00b67f09SDavid van Moolenbroek 			goto kill_socket;
3066*00b67f09SDavid van Moolenbroek 		}
3067*00b67f09SDavid van Moolenbroek 		isc_task_setname(disp->task[i], "udpdispatch", disp);
3068*00b67f09SDavid van Moolenbroek 	}
3069*00b67f09SDavid van Moolenbroek 
3070*00b67f09SDavid van Moolenbroek 	disp->ctlevent = isc_event_allocate(mgr->mctx, disp,
3071*00b67f09SDavid van Moolenbroek 					    DNS_EVENT_DISPATCHCONTROL,
3072*00b67f09SDavid van Moolenbroek 					    destroy_disp, disp,
3073*00b67f09SDavid van Moolenbroek 					    sizeof(isc_event_t));
3074*00b67f09SDavid van Moolenbroek 	if (disp->ctlevent == NULL) {
3075*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
3076*00b67f09SDavid van Moolenbroek 		goto kill_task;
3077*00b67f09SDavid van Moolenbroek 	}
3078*00b67f09SDavid van Moolenbroek 
3079*00b67f09SDavid van Moolenbroek 	disp->sepool = NULL;
3080*00b67f09SDavid van Moolenbroek 	if (isc_mempool_create(mgr->mctx, sizeof(isc_socketevent_t),
3081*00b67f09SDavid van Moolenbroek 			       &disp->sepool) != ISC_R_SUCCESS)
3082*00b67f09SDavid van Moolenbroek 	{
3083*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
3084*00b67f09SDavid van Moolenbroek 		goto kill_ctlevent;
3085*00b67f09SDavid van Moolenbroek 	}
3086*00b67f09SDavid van Moolenbroek 
3087*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&disp->sepool_lock);
3088*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
3089*00b67f09SDavid van Moolenbroek 		goto kill_sepool;
3090*00b67f09SDavid van Moolenbroek 
3091*00b67f09SDavid van Moolenbroek 	isc_mempool_setname(disp->sepool, "disp_sepool");
3092*00b67f09SDavid van Moolenbroek 	isc_mempool_setmaxalloc(disp->sepool, 32768);
3093*00b67f09SDavid van Moolenbroek 	isc_mempool_setfreemax(disp->sepool, 32768);
3094*00b67f09SDavid van Moolenbroek 	isc_mempool_associatelock(disp->sepool, &disp->sepool_lock);
3095*00b67f09SDavid van Moolenbroek 	isc_mempool_setfillcount(disp->sepool, 16);
3096*00b67f09SDavid van Moolenbroek 
3097*00b67f09SDavid van Moolenbroek 	attributes &= ~DNS_DISPATCHATTR_TCP;
3098*00b67f09SDavid van Moolenbroek 	attributes |= DNS_DISPATCHATTR_UDP;
3099*00b67f09SDavid van Moolenbroek 	disp->attributes = attributes;
3100*00b67f09SDavid van Moolenbroek 
3101*00b67f09SDavid van Moolenbroek 	/*
3102*00b67f09SDavid van Moolenbroek 	 * Append it to the dispatcher list.
3103*00b67f09SDavid van Moolenbroek 	 */
3104*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(mgr->list, disp, link);
3105*00b67f09SDavid van Moolenbroek 
3106*00b67f09SDavid van Moolenbroek 	mgr_log(mgr, LVL(90), "created UDP dispatcher %p", disp);
3107*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90), "created task %p", disp->task[0]); /* XXX */
3108*00b67f09SDavid van Moolenbroek 	if (disp->socket != NULL)
3109*00b67f09SDavid van Moolenbroek 		dispatch_log(disp, LVL(90), "created socket %p", disp->socket);
3110*00b67f09SDavid van Moolenbroek 
3111*00b67f09SDavid van Moolenbroek 	*dispp = disp;
3112*00b67f09SDavid van Moolenbroek 
3113*00b67f09SDavid van Moolenbroek 	return (result);
3114*00b67f09SDavid van Moolenbroek 
3115*00b67f09SDavid van Moolenbroek 	/*
3116*00b67f09SDavid van Moolenbroek 	 * Error returns.
3117*00b67f09SDavid van Moolenbroek 	 */
3118*00b67f09SDavid van Moolenbroek  kill_sepool:
3119*00b67f09SDavid van Moolenbroek 	isc_mempool_destroy(&disp->sepool);
3120*00b67f09SDavid van Moolenbroek  kill_ctlevent:
3121*00b67f09SDavid van Moolenbroek 	isc_event_free(&disp->ctlevent);
3122*00b67f09SDavid van Moolenbroek  kill_task:
3123*00b67f09SDavid van Moolenbroek 	for (i = 0; i < disp->ntasks; i++)
3124*00b67f09SDavid van Moolenbroek 		isc_task_detach(&disp->task[i]);
3125*00b67f09SDavid van Moolenbroek  kill_socket:
3126*00b67f09SDavid van Moolenbroek 	if (disp->socket != NULL)
3127*00b67f09SDavid van Moolenbroek 		isc_socket_detach(&disp->socket);
3128*00b67f09SDavid van Moolenbroek  deallocate_dispatch:
3129*00b67f09SDavid van Moolenbroek 	dispatch_free(&disp);
3130*00b67f09SDavid van Moolenbroek 
3131*00b67f09SDavid van Moolenbroek 	return (result);
3132*00b67f09SDavid van Moolenbroek }
3133*00b67f09SDavid van Moolenbroek 
3134*00b67f09SDavid van Moolenbroek void
dns_dispatch_attach(dns_dispatch_t * disp,dns_dispatch_t ** dispp)3135*00b67f09SDavid van Moolenbroek dns_dispatch_attach(dns_dispatch_t *disp, dns_dispatch_t **dispp) {
3136*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3137*00b67f09SDavid van Moolenbroek 	REQUIRE(dispp != NULL && *dispp == NULL);
3138*00b67f09SDavid van Moolenbroek 
3139*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3140*00b67f09SDavid van Moolenbroek 	disp->refcount++;
3141*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3142*00b67f09SDavid van Moolenbroek 
3143*00b67f09SDavid van Moolenbroek 	*dispp = disp;
3144*00b67f09SDavid van Moolenbroek }
3145*00b67f09SDavid van Moolenbroek 
3146*00b67f09SDavid van Moolenbroek /*
3147*00b67f09SDavid van Moolenbroek  * It is important to lock the manager while we are deleting the dispatch,
3148*00b67f09SDavid van Moolenbroek  * since dns_dispatch_getudp will call dispatch_find, which returns to
3149*00b67f09SDavid van Moolenbroek  * the caller a dispatch but does not attach to it until later.  _getudp
3150*00b67f09SDavid van Moolenbroek  * locks the manager, however, so locking it here will keep us from attaching
3151*00b67f09SDavid van Moolenbroek  * to a dispatcher that is in the process of going away.
3152*00b67f09SDavid van Moolenbroek  */
3153*00b67f09SDavid van Moolenbroek void
dns_dispatch_detach(dns_dispatch_t ** dispp)3154*00b67f09SDavid van Moolenbroek dns_dispatch_detach(dns_dispatch_t **dispp) {
3155*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
3156*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsock;
3157*00b67f09SDavid van Moolenbroek 	isc_boolean_t killit;
3158*00b67f09SDavid van Moolenbroek 
3159*00b67f09SDavid van Moolenbroek 	REQUIRE(dispp != NULL && VALID_DISPATCH(*dispp));
3160*00b67f09SDavid van Moolenbroek 
3161*00b67f09SDavid van Moolenbroek 	disp = *dispp;
3162*00b67f09SDavid van Moolenbroek 	*dispp = NULL;
3163*00b67f09SDavid van Moolenbroek 
3164*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3165*00b67f09SDavid van Moolenbroek 
3166*00b67f09SDavid van Moolenbroek 	INSIST(disp->refcount > 0);
3167*00b67f09SDavid van Moolenbroek 	disp->refcount--;
3168*00b67f09SDavid van Moolenbroek 	if (disp->refcount == 0) {
3169*00b67f09SDavid van Moolenbroek 		if (disp->recv_pending > 0)
3170*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(disp->socket, disp->task[0],
3171*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_RECV);
3172*00b67f09SDavid van Moolenbroek 		for (dispsock = ISC_LIST_HEAD(disp->activesockets);
3173*00b67f09SDavid van Moolenbroek 		     dispsock != NULL;
3174*00b67f09SDavid van Moolenbroek 		     dispsock = ISC_LIST_NEXT(dispsock, link)) {
3175*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(dispsock->socket, dispsock->task,
3176*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_RECV);
3177*00b67f09SDavid van Moolenbroek 		}
3178*00b67f09SDavid van Moolenbroek 		disp->shutting_down = 1;
3179*00b67f09SDavid van Moolenbroek 	}
3180*00b67f09SDavid van Moolenbroek 
3181*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90), "detach: refcount %d", disp->refcount);
3182*00b67f09SDavid van Moolenbroek 
3183*00b67f09SDavid van Moolenbroek 	killit = destroy_disp_ok(disp);
3184*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3185*00b67f09SDavid van Moolenbroek 	if (killit)
3186*00b67f09SDavid van Moolenbroek 		isc_task_send(disp->task[0], &disp->ctlevent);
3187*00b67f09SDavid van Moolenbroek }
3188*00b67f09SDavid van Moolenbroek 
3189*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_addresponse2(dns_dispatch_t * disp,isc_sockaddr_t * dest,isc_task_t * task,isc_taskaction_t action,void * arg,dns_messageid_t * idp,dns_dispentry_t ** resp,isc_socketmgr_t * sockmgr)3190*00b67f09SDavid van Moolenbroek dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
3191*00b67f09SDavid van Moolenbroek 			  isc_task_t *task, isc_taskaction_t action, void *arg,
3192*00b67f09SDavid van Moolenbroek 			  dns_messageid_t *idp, dns_dispentry_t **resp,
3193*00b67f09SDavid van Moolenbroek 			  isc_socketmgr_t *sockmgr)
3194*00b67f09SDavid van Moolenbroek {
3195*00b67f09SDavid van Moolenbroek 	return (dns_dispatch_addresponse3(disp, 0, dest, task, action, arg,
3196*00b67f09SDavid van Moolenbroek 					  idp, resp, sockmgr));
3197*00b67f09SDavid van Moolenbroek }
3198*00b67f09SDavid van Moolenbroek 
3199*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_addresponse3(dns_dispatch_t * disp,unsigned int options,isc_sockaddr_t * dest,isc_task_t * task,isc_taskaction_t action,void * arg,dns_messageid_t * idp,dns_dispentry_t ** resp,isc_socketmgr_t * sockmgr)3200*00b67f09SDavid van Moolenbroek dns_dispatch_addresponse3(dns_dispatch_t *disp, unsigned int options,
3201*00b67f09SDavid van Moolenbroek 			  isc_sockaddr_t *dest, isc_task_t *task,
3202*00b67f09SDavid van Moolenbroek 			  isc_taskaction_t action, void *arg,
3203*00b67f09SDavid van Moolenbroek 			  dns_messageid_t *idp, dns_dispentry_t **resp,
3204*00b67f09SDavid van Moolenbroek 			  isc_socketmgr_t *sockmgr)
3205*00b67f09SDavid van Moolenbroek {
3206*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *res;
3207*00b67f09SDavid van Moolenbroek 	unsigned int bucket;
3208*00b67f09SDavid van Moolenbroek 	in_port_t localport = 0;
3209*00b67f09SDavid van Moolenbroek 	dns_messageid_t id;
3210*00b67f09SDavid van Moolenbroek 	int i;
3211*00b67f09SDavid van Moolenbroek 	isc_boolean_t ok;
3212*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
3213*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsocket = NULL;
3214*00b67f09SDavid van Moolenbroek 	isc_result_t result;
3215*00b67f09SDavid van Moolenbroek 
3216*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3217*00b67f09SDavid van Moolenbroek 	REQUIRE(task != NULL);
3218*00b67f09SDavid van Moolenbroek 	REQUIRE(dest != NULL);
3219*00b67f09SDavid van Moolenbroek 	REQUIRE(resp != NULL && *resp == NULL);
3220*00b67f09SDavid van Moolenbroek 	REQUIRE(idp != NULL);
3221*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0)
3222*00b67f09SDavid van Moolenbroek 		REQUIRE(sockmgr != NULL);
3223*00b67f09SDavid van Moolenbroek 
3224*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3225*00b67f09SDavid van Moolenbroek 
3226*00b67f09SDavid van Moolenbroek 	if (disp->shutting_down == 1) {
3227*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
3228*00b67f09SDavid van Moolenbroek 		return (ISC_R_SHUTTINGDOWN);
3229*00b67f09SDavid van Moolenbroek 	}
3230*00b67f09SDavid van Moolenbroek 
3231*00b67f09SDavid van Moolenbroek 	if (disp->requests >= disp->maxrequests) {
3232*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
3233*00b67f09SDavid van Moolenbroek 		return (ISC_R_QUOTA);
3234*00b67f09SDavid van Moolenbroek 	}
3235*00b67f09SDavid van Moolenbroek 
3236*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0 &&
3237*00b67f09SDavid van Moolenbroek 	    disp->nsockets > DNS_DISPATCH_SOCKSQUOTA) {
3238*00b67f09SDavid van Moolenbroek 		dispsocket_t *oldestsocket;
3239*00b67f09SDavid van Moolenbroek 		dns_dispentry_t *oldestresp;
3240*00b67f09SDavid van Moolenbroek 		dns_dispatchevent_t *rev;
3241*00b67f09SDavid van Moolenbroek 
3242*00b67f09SDavid van Moolenbroek 		/*
3243*00b67f09SDavid van Moolenbroek 		 * Kill oldest outstanding query if the number of sockets
3244*00b67f09SDavid van Moolenbroek 		 * exceeds the quota to keep the room for new queries.
3245*00b67f09SDavid van Moolenbroek 		 */
3246*00b67f09SDavid van Moolenbroek 		oldestsocket = ISC_LIST_HEAD(disp->activesockets);
3247*00b67f09SDavid van Moolenbroek 		oldestresp = oldestsocket->resp;
3248*00b67f09SDavid van Moolenbroek 		if (oldestresp != NULL && !oldestresp->item_out) {
3249*00b67f09SDavid van Moolenbroek 			rev = allocate_devent(oldestresp->disp);
3250*00b67f09SDavid van Moolenbroek 			if (rev != NULL) {
3251*00b67f09SDavid van Moolenbroek 				rev->buffer.base = NULL;
3252*00b67f09SDavid van Moolenbroek 				rev->result = ISC_R_CANCELED;
3253*00b67f09SDavid van Moolenbroek 				rev->id = oldestresp->id;
3254*00b67f09SDavid van Moolenbroek 				ISC_EVENT_INIT(rev, sizeof(*rev), 0,
3255*00b67f09SDavid van Moolenbroek 					       NULL, DNS_EVENT_DISPATCH,
3256*00b67f09SDavid van Moolenbroek 					       oldestresp->action,
3257*00b67f09SDavid van Moolenbroek 					       oldestresp->arg, oldestresp,
3258*00b67f09SDavid van Moolenbroek 					       NULL, NULL);
3259*00b67f09SDavid van Moolenbroek 				oldestresp->item_out = ISC_TRUE;
3260*00b67f09SDavid van Moolenbroek 				isc_task_send(oldestresp->task,
3261*00b67f09SDavid van Moolenbroek 					      ISC_EVENT_PTR(&rev));
3262*00b67f09SDavid van Moolenbroek 				inc_stats(disp->mgr,
3263*00b67f09SDavid van Moolenbroek 					  dns_resstatscounter_dispabort);
3264*00b67f09SDavid van Moolenbroek 			}
3265*00b67f09SDavid van Moolenbroek 		}
3266*00b67f09SDavid van Moolenbroek 
3267*00b67f09SDavid van Moolenbroek 		/*
3268*00b67f09SDavid van Moolenbroek 		 * Move this entry to the tail so that it won't (easily) be
3269*00b67f09SDavid van Moolenbroek 		 * examined before actually being canceled.
3270*00b67f09SDavid van Moolenbroek 		 */
3271*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(disp->activesockets, oldestsocket, link);
3272*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(disp->activesockets, oldestsocket, link);
3273*00b67f09SDavid van Moolenbroek 	}
3274*00b67f09SDavid van Moolenbroek 
3275*00b67f09SDavid van Moolenbroek 	qid = DNS_QID(disp);
3276*00b67f09SDavid van Moolenbroek 
3277*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
3278*00b67f09SDavid van Moolenbroek 		/*
3279*00b67f09SDavid van Moolenbroek 		 * Get a separate UDP socket with a random port number.
3280*00b67f09SDavid van Moolenbroek 		 */
3281*00b67f09SDavid van Moolenbroek 		result = get_dispsocket(disp, dest, sockmgr, &dispsocket,
3282*00b67f09SDavid van Moolenbroek 					&localport);
3283*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
3284*00b67f09SDavid van Moolenbroek 			UNLOCK(&disp->lock);
3285*00b67f09SDavid van Moolenbroek 			inc_stats(disp->mgr, dns_resstatscounter_dispsockfail);
3286*00b67f09SDavid van Moolenbroek 			return (result);
3287*00b67f09SDavid van Moolenbroek 		}
3288*00b67f09SDavid van Moolenbroek 	} else {
3289*00b67f09SDavid van Moolenbroek 		localport = disp->localport;
3290*00b67f09SDavid van Moolenbroek 	}
3291*00b67f09SDavid van Moolenbroek 
3292*00b67f09SDavid van Moolenbroek 	/*
3293*00b67f09SDavid van Moolenbroek 	 * Try somewhat hard to find an unique ID unless FIXEDID is set
3294*00b67f09SDavid van Moolenbroek 	 * in which case we use the id passed in via *idp.
3295*00b67f09SDavid van Moolenbroek 	 */
3296*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
3297*00b67f09SDavid van Moolenbroek 	if ((options & DNS_DISPATCHOPT_FIXEDID) != 0)
3298*00b67f09SDavid van Moolenbroek 		id = *idp;
3299*00b67f09SDavid van Moolenbroek 	else
3300*00b67f09SDavid van Moolenbroek 		id = (dns_messageid_t)dispatch_random(DISP_ARC4CTX(disp));
3301*00b67f09SDavid van Moolenbroek 	ok = ISC_FALSE;
3302*00b67f09SDavid van Moolenbroek 	i = 0;
3303*00b67f09SDavid van Moolenbroek 	do {
3304*00b67f09SDavid van Moolenbroek 		bucket = dns_hash(qid, dest, id, localport);
3305*00b67f09SDavid van Moolenbroek 		if (entry_search(qid, dest, id, localport, bucket) == NULL) {
3306*00b67f09SDavid van Moolenbroek 			ok = ISC_TRUE;
3307*00b67f09SDavid van Moolenbroek 			break;
3308*00b67f09SDavid van Moolenbroek 		}
3309*00b67f09SDavid van Moolenbroek 		if ((disp->attributes & DNS_DISPATCHATTR_FIXEDID) != 0)
3310*00b67f09SDavid van Moolenbroek 			break;
3311*00b67f09SDavid van Moolenbroek 		id += qid->qid_increment;
3312*00b67f09SDavid van Moolenbroek 		id &= 0x0000ffff;
3313*00b67f09SDavid van Moolenbroek 	} while (i++ < 64);
3314*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
3315*00b67f09SDavid van Moolenbroek 
3316*00b67f09SDavid van Moolenbroek 	if (!ok) {
3317*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
3318*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMORE);
3319*00b67f09SDavid van Moolenbroek 	}
3320*00b67f09SDavid van Moolenbroek 
3321*00b67f09SDavid van Moolenbroek 	res = isc_mempool_get(disp->mgr->rpool);
3322*00b67f09SDavid van Moolenbroek 	if (res == NULL) {
3323*00b67f09SDavid van Moolenbroek 		if (dispsocket != NULL)
3324*00b67f09SDavid van Moolenbroek 			destroy_dispsocket(disp, &dispsocket);
3325*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
3326*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
3327*00b67f09SDavid van Moolenbroek 	}
3328*00b67f09SDavid van Moolenbroek 
3329*00b67f09SDavid van Moolenbroek 	disp->refcount++;
3330*00b67f09SDavid van Moolenbroek 	disp->requests++;
3331*00b67f09SDavid van Moolenbroek 	res->task = NULL;
3332*00b67f09SDavid van Moolenbroek 	isc_task_attach(task, &res->task);
3333*00b67f09SDavid van Moolenbroek 	res->disp = disp;
3334*00b67f09SDavid van Moolenbroek 	res->id = id;
3335*00b67f09SDavid van Moolenbroek 	res->port = localport;
3336*00b67f09SDavid van Moolenbroek 	res->bucket = bucket;
3337*00b67f09SDavid van Moolenbroek 	res->host = *dest;
3338*00b67f09SDavid van Moolenbroek 	res->action = action;
3339*00b67f09SDavid van Moolenbroek 	res->arg = arg;
3340*00b67f09SDavid van Moolenbroek 	res->dispsocket = dispsocket;
3341*00b67f09SDavid van Moolenbroek 	if (dispsocket != NULL)
3342*00b67f09SDavid van Moolenbroek 		dispsocket->resp = res;
3343*00b67f09SDavid van Moolenbroek 	res->item_out = ISC_FALSE;
3344*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(res->items);
3345*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(res, link);
3346*00b67f09SDavid van Moolenbroek 	res->magic = RESPONSE_MAGIC;
3347*00b67f09SDavid van Moolenbroek 
3348*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
3349*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(qid->qid_table[bucket], res, link);
3350*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
3351*00b67f09SDavid van Moolenbroek 
3352*00b67f09SDavid van Moolenbroek 	inc_stats(disp->mgr, (qid == disp->mgr->qid) ?
3353*00b67f09SDavid van Moolenbroek 			     dns_resstatscounter_disprequdp :
3354*00b67f09SDavid van Moolenbroek 			     dns_resstatscounter_dispreqtcp);
3355*00b67f09SDavid van Moolenbroek 
3356*00b67f09SDavid van Moolenbroek 	request_log(disp, res, LVL(90),
3357*00b67f09SDavid van Moolenbroek 		    "attached to task %p", res->task);
3358*00b67f09SDavid van Moolenbroek 
3359*00b67f09SDavid van Moolenbroek 	if (((disp->attributes & DNS_DISPATCHATTR_UDP) != 0) ||
3360*00b67f09SDavid van Moolenbroek 	    ((disp->attributes & DNS_DISPATCHATTR_CONNECTED) != 0)) {
3361*00b67f09SDavid van Moolenbroek 		result = startrecv(disp, dispsocket);
3362*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
3363*00b67f09SDavid van Moolenbroek 			LOCK(&qid->lock);
3364*00b67f09SDavid van Moolenbroek 			ISC_LIST_UNLINK(qid->qid_table[bucket], res, link);
3365*00b67f09SDavid van Moolenbroek 			UNLOCK(&qid->lock);
3366*00b67f09SDavid van Moolenbroek 
3367*00b67f09SDavid van Moolenbroek 			if (dispsocket != NULL)
3368*00b67f09SDavid van Moolenbroek 				destroy_dispsocket(disp, &dispsocket);
3369*00b67f09SDavid van Moolenbroek 
3370*00b67f09SDavid van Moolenbroek 			disp->refcount--;
3371*00b67f09SDavid van Moolenbroek 			disp->requests--;
3372*00b67f09SDavid van Moolenbroek 
3373*00b67f09SDavid van Moolenbroek 			dec_stats(disp->mgr, (qid == disp->mgr->qid) ?
3374*00b67f09SDavid van Moolenbroek 					     dns_resstatscounter_disprequdp :
3375*00b67f09SDavid van Moolenbroek 					     dns_resstatscounter_dispreqtcp);
3376*00b67f09SDavid van Moolenbroek 
3377*00b67f09SDavid van Moolenbroek 			UNLOCK(&disp->lock);
3378*00b67f09SDavid van Moolenbroek 			isc_task_detach(&res->task);
3379*00b67f09SDavid van Moolenbroek 			isc_mempool_put(disp->mgr->rpool, res);
3380*00b67f09SDavid van Moolenbroek 			return (result);
3381*00b67f09SDavid van Moolenbroek 		}
3382*00b67f09SDavid van Moolenbroek 	}
3383*00b67f09SDavid van Moolenbroek 
3384*00b67f09SDavid van Moolenbroek 	if (dispsocket != NULL)
3385*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(disp->activesockets, dispsocket, link);
3386*00b67f09SDavid van Moolenbroek 
3387*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3388*00b67f09SDavid van Moolenbroek 
3389*00b67f09SDavid van Moolenbroek 	*idp = id;
3390*00b67f09SDavid van Moolenbroek 	*resp = res;
3391*00b67f09SDavid van Moolenbroek 
3392*00b67f09SDavid van Moolenbroek 	if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0)
3393*00b67f09SDavid van Moolenbroek 		INSIST(res->dispsocket != NULL);
3394*00b67f09SDavid van Moolenbroek 
3395*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
3396*00b67f09SDavid van Moolenbroek }
3397*00b67f09SDavid van Moolenbroek 
3398*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_addresponse(dns_dispatch_t * disp,isc_sockaddr_t * dest,isc_task_t * task,isc_taskaction_t action,void * arg,dns_messageid_t * idp,dns_dispentry_t ** resp)3399*00b67f09SDavid van Moolenbroek dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest,
3400*00b67f09SDavid van Moolenbroek 			 isc_task_t *task, isc_taskaction_t action, void *arg,
3401*00b67f09SDavid van Moolenbroek 			 dns_messageid_t *idp, dns_dispentry_t **resp)
3402*00b67f09SDavid van Moolenbroek {
3403*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3404*00b67f09SDavid van Moolenbroek 	REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0);
3405*00b67f09SDavid van Moolenbroek 
3406*00b67f09SDavid van Moolenbroek 	return (dns_dispatch_addresponse3(disp, 0, dest, task, action, arg,
3407*00b67f09SDavid van Moolenbroek 					  idp, resp, NULL));
3408*00b67f09SDavid van Moolenbroek }
3409*00b67f09SDavid van Moolenbroek 
3410*00b67f09SDavid van Moolenbroek void
dns_dispatch_starttcp(dns_dispatch_t * disp)3411*00b67f09SDavid van Moolenbroek dns_dispatch_starttcp(dns_dispatch_t *disp) {
3412*00b67f09SDavid van Moolenbroek 
3413*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3414*00b67f09SDavid van Moolenbroek 
3415*00b67f09SDavid van Moolenbroek 	dispatch_log(disp, LVL(90), "starttcp %p", disp->task[0]);
3416*00b67f09SDavid van Moolenbroek 
3417*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3418*00b67f09SDavid van Moolenbroek 	disp->attributes |= DNS_DISPATCHATTR_CONNECTED;
3419*00b67f09SDavid van Moolenbroek 	(void)startrecv(disp, NULL);
3420*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3421*00b67f09SDavid van Moolenbroek }
3422*00b67f09SDavid van Moolenbroek 
3423*00b67f09SDavid van Moolenbroek void
dns_dispatch_removeresponse(dns_dispentry_t ** resp,dns_dispatchevent_t ** sockevent)3424*00b67f09SDavid van Moolenbroek dns_dispatch_removeresponse(dns_dispentry_t **resp,
3425*00b67f09SDavid van Moolenbroek 			    dns_dispatchevent_t **sockevent)
3426*00b67f09SDavid van Moolenbroek {
3427*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
3428*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
3429*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *res;
3430*00b67f09SDavid van Moolenbroek 	dispsocket_t *dispsock;
3431*00b67f09SDavid van Moolenbroek 	dns_dispatchevent_t *ev;
3432*00b67f09SDavid van Moolenbroek 	unsigned int bucket;
3433*00b67f09SDavid van Moolenbroek 	isc_boolean_t killit;
3434*00b67f09SDavid van Moolenbroek 	unsigned int n;
3435*00b67f09SDavid van Moolenbroek 	isc_eventlist_t events;
3436*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
3437*00b67f09SDavid van Moolenbroek 
3438*00b67f09SDavid van Moolenbroek 	REQUIRE(resp != NULL);
3439*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_RESPONSE(*resp));
3440*00b67f09SDavid van Moolenbroek 
3441*00b67f09SDavid van Moolenbroek 	res = *resp;
3442*00b67f09SDavid van Moolenbroek 	*resp = NULL;
3443*00b67f09SDavid van Moolenbroek 
3444*00b67f09SDavid van Moolenbroek 	disp = res->disp;
3445*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3446*00b67f09SDavid van Moolenbroek 	mgr = disp->mgr;
3447*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCHMGR(mgr));
3448*00b67f09SDavid van Moolenbroek 
3449*00b67f09SDavid van Moolenbroek 	qid = DNS_QID(disp);
3450*00b67f09SDavid van Moolenbroek 
3451*00b67f09SDavid van Moolenbroek 	if (sockevent != NULL) {
3452*00b67f09SDavid van Moolenbroek 		REQUIRE(*sockevent != NULL);
3453*00b67f09SDavid van Moolenbroek 		ev = *sockevent;
3454*00b67f09SDavid van Moolenbroek 		*sockevent = NULL;
3455*00b67f09SDavid van Moolenbroek 	} else {
3456*00b67f09SDavid van Moolenbroek 		ev = NULL;
3457*00b67f09SDavid van Moolenbroek 	}
3458*00b67f09SDavid van Moolenbroek 
3459*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3460*00b67f09SDavid van Moolenbroek 
3461*00b67f09SDavid van Moolenbroek 	INSIST(disp->requests > 0);
3462*00b67f09SDavid van Moolenbroek 	disp->requests--;
3463*00b67f09SDavid van Moolenbroek 	dec_stats(disp->mgr, (qid == disp->mgr->qid) ?
3464*00b67f09SDavid van Moolenbroek 			     dns_resstatscounter_disprequdp :
3465*00b67f09SDavid van Moolenbroek 			     dns_resstatscounter_dispreqtcp);
3466*00b67f09SDavid van Moolenbroek 	INSIST(disp->refcount > 0);
3467*00b67f09SDavid van Moolenbroek 	disp->refcount--;
3468*00b67f09SDavid van Moolenbroek 	if (disp->refcount == 0) {
3469*00b67f09SDavid van Moolenbroek 		if (disp->recv_pending > 0)
3470*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(disp->socket, disp->task[0],
3471*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_RECV);
3472*00b67f09SDavid van Moolenbroek 		for (dispsock = ISC_LIST_HEAD(disp->activesockets);
3473*00b67f09SDavid van Moolenbroek 		     dispsock != NULL;
3474*00b67f09SDavid van Moolenbroek 		     dispsock = ISC_LIST_NEXT(dispsock, link)) {
3475*00b67f09SDavid van Moolenbroek 			isc_socket_cancel(dispsock->socket, dispsock->task,
3476*00b67f09SDavid van Moolenbroek 					  ISC_SOCKCANCEL_RECV);
3477*00b67f09SDavid van Moolenbroek 		}
3478*00b67f09SDavid van Moolenbroek 		disp->shutting_down = 1;
3479*00b67f09SDavid van Moolenbroek 	}
3480*00b67f09SDavid van Moolenbroek 
3481*00b67f09SDavid van Moolenbroek 	bucket = res->bucket;
3482*00b67f09SDavid van Moolenbroek 
3483*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
3484*00b67f09SDavid van Moolenbroek 	ISC_LIST_UNLINK(qid->qid_table[bucket], res, link);
3485*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
3486*00b67f09SDavid van Moolenbroek 
3487*00b67f09SDavid van Moolenbroek 	if (ev == NULL && res->item_out) {
3488*00b67f09SDavid van Moolenbroek 		/*
3489*00b67f09SDavid van Moolenbroek 		 * We've posted our event, but the caller hasn't gotten it
3490*00b67f09SDavid van Moolenbroek 		 * yet.  Take it back.
3491*00b67f09SDavid van Moolenbroek 		 */
3492*00b67f09SDavid van Moolenbroek 		ISC_LIST_INIT(events);
3493*00b67f09SDavid van Moolenbroek 		n = isc_task_unsend(res->task, res, DNS_EVENT_DISPATCH,
3494*00b67f09SDavid van Moolenbroek 				    NULL, &events);
3495*00b67f09SDavid van Moolenbroek 		/*
3496*00b67f09SDavid van Moolenbroek 		 * We had better have gotten it back.
3497*00b67f09SDavid van Moolenbroek 		 */
3498*00b67f09SDavid van Moolenbroek 		INSIST(n == 1);
3499*00b67f09SDavid van Moolenbroek 		ev = (dns_dispatchevent_t *)ISC_LIST_HEAD(events);
3500*00b67f09SDavid van Moolenbroek 	}
3501*00b67f09SDavid van Moolenbroek 
3502*00b67f09SDavid van Moolenbroek 	if (ev != NULL) {
3503*00b67f09SDavid van Moolenbroek 		REQUIRE(res->item_out == ISC_TRUE);
3504*00b67f09SDavid van Moolenbroek 		res->item_out = ISC_FALSE;
3505*00b67f09SDavid van Moolenbroek 		if (ev->buffer.base != NULL)
3506*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->buffer.base, ev->buffer.length);
3507*00b67f09SDavid van Moolenbroek 		free_devent(disp, ev);
3508*00b67f09SDavid van Moolenbroek 	}
3509*00b67f09SDavid van Moolenbroek 
3510*00b67f09SDavid van Moolenbroek 	request_log(disp, res, LVL(90), "detaching from task %p", res->task);
3511*00b67f09SDavid van Moolenbroek 	isc_task_detach(&res->task);
3512*00b67f09SDavid van Moolenbroek 
3513*00b67f09SDavid van Moolenbroek 	if (res->dispsocket != NULL) {
3514*00b67f09SDavid van Moolenbroek 		isc_socket_cancel(res->dispsocket->socket,
3515*00b67f09SDavid van Moolenbroek 				  res->dispsocket->task, ISC_SOCKCANCEL_RECV);
3516*00b67f09SDavid van Moolenbroek 		res->dispsocket->resp = NULL;
3517*00b67f09SDavid van Moolenbroek 	}
3518*00b67f09SDavid van Moolenbroek 
3519*00b67f09SDavid van Moolenbroek 	/*
3520*00b67f09SDavid van Moolenbroek 	 * Free any buffered requests as well
3521*00b67f09SDavid van Moolenbroek 	 */
3522*00b67f09SDavid van Moolenbroek 	ev = ISC_LIST_HEAD(res->items);
3523*00b67f09SDavid van Moolenbroek 	while (ev != NULL) {
3524*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(res->items, ev, ev_link);
3525*00b67f09SDavid van Moolenbroek 		if (ev->buffer.base != NULL)
3526*00b67f09SDavid van Moolenbroek 			free_buffer(disp, ev->buffer.base, ev->buffer.length);
3527*00b67f09SDavid van Moolenbroek 		free_devent(disp, ev);
3528*00b67f09SDavid van Moolenbroek 		ev = ISC_LIST_HEAD(res->items);
3529*00b67f09SDavid van Moolenbroek 	}
3530*00b67f09SDavid van Moolenbroek 	res->magic = 0;
3531*00b67f09SDavid van Moolenbroek 	isc_mempool_put(disp->mgr->rpool, res);
3532*00b67f09SDavid van Moolenbroek 	if (disp->shutting_down == 1)
3533*00b67f09SDavid van Moolenbroek 		do_cancel(disp);
3534*00b67f09SDavid van Moolenbroek 	else
3535*00b67f09SDavid van Moolenbroek 		(void)startrecv(disp, NULL);
3536*00b67f09SDavid van Moolenbroek 
3537*00b67f09SDavid van Moolenbroek 	killit = destroy_disp_ok(disp);
3538*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3539*00b67f09SDavid van Moolenbroek 	if (killit)
3540*00b67f09SDavid van Moolenbroek 		isc_task_send(disp->task[0], &disp->ctlevent);
3541*00b67f09SDavid van Moolenbroek }
3542*00b67f09SDavid van Moolenbroek 
3543*00b67f09SDavid van Moolenbroek static void
do_cancel(dns_dispatch_t * disp)3544*00b67f09SDavid van Moolenbroek do_cancel(dns_dispatch_t *disp) {
3545*00b67f09SDavid van Moolenbroek 	dns_dispatchevent_t *ev;
3546*00b67f09SDavid van Moolenbroek 	dns_dispentry_t *resp;
3547*00b67f09SDavid van Moolenbroek 	dns_qid_t *qid;
3548*00b67f09SDavid van Moolenbroek 
3549*00b67f09SDavid van Moolenbroek 	if (disp->shutdown_out == 1)
3550*00b67f09SDavid van Moolenbroek 		return;
3551*00b67f09SDavid van Moolenbroek 
3552*00b67f09SDavid van Moolenbroek 	qid = DNS_QID(disp);
3553*00b67f09SDavid van Moolenbroek 
3554*00b67f09SDavid van Moolenbroek 	/*
3555*00b67f09SDavid van Moolenbroek 	 * Search for the first response handler without packets outstanding
3556*00b67f09SDavid van Moolenbroek 	 * unless a specific hander is given.
3557*00b67f09SDavid van Moolenbroek 	 */
3558*00b67f09SDavid van Moolenbroek 	LOCK(&qid->lock);
3559*00b67f09SDavid van Moolenbroek 	for (resp = linear_first(qid);
3560*00b67f09SDavid van Moolenbroek 	     resp != NULL && resp->item_out;
3561*00b67f09SDavid van Moolenbroek 	     /* Empty. */)
3562*00b67f09SDavid van Moolenbroek 		resp = linear_next(qid, resp);
3563*00b67f09SDavid van Moolenbroek 
3564*00b67f09SDavid van Moolenbroek 	/*
3565*00b67f09SDavid van Moolenbroek 	 * No one to send the cancel event to, so nothing to do.
3566*00b67f09SDavid van Moolenbroek 	 */
3567*00b67f09SDavid van Moolenbroek 	if (resp == NULL)
3568*00b67f09SDavid van Moolenbroek 		goto unlock;
3569*00b67f09SDavid van Moolenbroek 
3570*00b67f09SDavid van Moolenbroek 	/*
3571*00b67f09SDavid van Moolenbroek 	 * Send the shutdown failsafe event to this resp.
3572*00b67f09SDavid van Moolenbroek 	 */
3573*00b67f09SDavid van Moolenbroek 	ev = disp->failsafe_ev;
3574*00b67f09SDavid van Moolenbroek 	ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, DNS_EVENT_DISPATCH,
3575*00b67f09SDavid van Moolenbroek 		       resp->action, resp->arg, resp, NULL, NULL);
3576*00b67f09SDavid van Moolenbroek 	ev->result = disp->shutdown_why;
3577*00b67f09SDavid van Moolenbroek 	ev->buffer.base = NULL;
3578*00b67f09SDavid van Moolenbroek 	ev->buffer.length = 0;
3579*00b67f09SDavid van Moolenbroek 	disp->shutdown_out = 1;
3580*00b67f09SDavid van Moolenbroek 	request_log(disp, resp, LVL(10),
3581*00b67f09SDavid van Moolenbroek 		    "cancel: failsafe event %p -> task %p",
3582*00b67f09SDavid van Moolenbroek 		    ev, resp->task);
3583*00b67f09SDavid van Moolenbroek 	resp->item_out = ISC_TRUE;
3584*00b67f09SDavid van Moolenbroek 	isc_task_send(resp->task, ISC_EVENT_PTR(&ev));
3585*00b67f09SDavid van Moolenbroek  unlock:
3586*00b67f09SDavid van Moolenbroek 	UNLOCK(&qid->lock);
3587*00b67f09SDavid van Moolenbroek }
3588*00b67f09SDavid van Moolenbroek 
3589*00b67f09SDavid van Moolenbroek isc_socket_t *
dns_dispatch_getsocket(dns_dispatch_t * disp)3590*00b67f09SDavid van Moolenbroek dns_dispatch_getsocket(dns_dispatch_t *disp) {
3591*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3592*00b67f09SDavid van Moolenbroek 
3593*00b67f09SDavid van Moolenbroek 	return (disp->socket);
3594*00b67f09SDavid van Moolenbroek }
3595*00b67f09SDavid van Moolenbroek 
3596*00b67f09SDavid van Moolenbroek isc_socket_t *
dns_dispatch_getentrysocket(dns_dispentry_t * resp)3597*00b67f09SDavid van Moolenbroek dns_dispatch_getentrysocket(dns_dispentry_t *resp) {
3598*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_RESPONSE(resp));
3599*00b67f09SDavid van Moolenbroek 
3600*00b67f09SDavid van Moolenbroek 	if (resp->dispsocket != NULL)
3601*00b67f09SDavid van Moolenbroek 		return (resp->dispsocket->socket);
3602*00b67f09SDavid van Moolenbroek 	else
3603*00b67f09SDavid van Moolenbroek 		return (NULL);
3604*00b67f09SDavid van Moolenbroek }
3605*00b67f09SDavid van Moolenbroek 
3606*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatch_getlocaladdress(dns_dispatch_t * disp,isc_sockaddr_t * addrp)3607*00b67f09SDavid van Moolenbroek dns_dispatch_getlocaladdress(dns_dispatch_t *disp, isc_sockaddr_t *addrp) {
3608*00b67f09SDavid van Moolenbroek 
3609*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3610*00b67f09SDavid van Moolenbroek 	REQUIRE(addrp != NULL);
3611*00b67f09SDavid van Moolenbroek 
3612*00b67f09SDavid van Moolenbroek 	if (disp->socktype == isc_sockettype_udp) {
3613*00b67f09SDavid van Moolenbroek 		*addrp = disp->local;
3614*00b67f09SDavid van Moolenbroek 		return (ISC_R_SUCCESS);
3615*00b67f09SDavid van Moolenbroek 	}
3616*00b67f09SDavid van Moolenbroek 	return (ISC_R_NOTIMPLEMENTED);
3617*00b67f09SDavid van Moolenbroek }
3618*00b67f09SDavid van Moolenbroek 
3619*00b67f09SDavid van Moolenbroek void
dns_dispatch_cancel(dns_dispatch_t * disp)3620*00b67f09SDavid van Moolenbroek dns_dispatch_cancel(dns_dispatch_t *disp) {
3621*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3622*00b67f09SDavid van Moolenbroek 
3623*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3624*00b67f09SDavid van Moolenbroek 
3625*00b67f09SDavid van Moolenbroek 	if (disp->shutting_down == 1) {
3626*00b67f09SDavid van Moolenbroek 		UNLOCK(&disp->lock);
3627*00b67f09SDavid van Moolenbroek 		return;
3628*00b67f09SDavid van Moolenbroek 	}
3629*00b67f09SDavid van Moolenbroek 
3630*00b67f09SDavid van Moolenbroek 	disp->shutdown_why = ISC_R_CANCELED;
3631*00b67f09SDavid van Moolenbroek 	disp->shutting_down = 1;
3632*00b67f09SDavid van Moolenbroek 	do_cancel(disp);
3633*00b67f09SDavid van Moolenbroek 
3634*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3635*00b67f09SDavid van Moolenbroek 
3636*00b67f09SDavid van Moolenbroek 	return;
3637*00b67f09SDavid van Moolenbroek }
3638*00b67f09SDavid van Moolenbroek 
3639*00b67f09SDavid van Moolenbroek unsigned int
dns_dispatch_getattributes(dns_dispatch_t * disp)3640*00b67f09SDavid van Moolenbroek dns_dispatch_getattributes(dns_dispatch_t *disp) {
3641*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3642*00b67f09SDavid van Moolenbroek 
3643*00b67f09SDavid van Moolenbroek 	/*
3644*00b67f09SDavid van Moolenbroek 	 * We don't bother locking disp here; it's the caller's responsibility
3645*00b67f09SDavid van Moolenbroek 	 * to use only non volatile flags.
3646*00b67f09SDavid van Moolenbroek 	 */
3647*00b67f09SDavid van Moolenbroek 	return (disp->attributes);
3648*00b67f09SDavid van Moolenbroek }
3649*00b67f09SDavid van Moolenbroek 
3650*00b67f09SDavid van Moolenbroek void
dns_dispatch_changeattributes(dns_dispatch_t * disp,unsigned int attributes,unsigned int mask)3651*00b67f09SDavid van Moolenbroek dns_dispatch_changeattributes(dns_dispatch_t *disp,
3652*00b67f09SDavid van Moolenbroek 			      unsigned int attributes, unsigned int mask)
3653*00b67f09SDavid van Moolenbroek {
3654*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3655*00b67f09SDavid van Moolenbroek 	/* Exclusive attribute can only be set on creation */
3656*00b67f09SDavid van Moolenbroek 	REQUIRE((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0);
3657*00b67f09SDavid van Moolenbroek 	/* Also, a dispatch with randomport specified cannot start listening */
3658*00b67f09SDavid van Moolenbroek 	REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0 ||
3659*00b67f09SDavid van Moolenbroek 		(attributes & DNS_DISPATCHATTR_NOLISTEN) == 0);
3660*00b67f09SDavid van Moolenbroek 
3661*00b67f09SDavid van Moolenbroek 	/* XXXMLG
3662*00b67f09SDavid van Moolenbroek 	 * Should check for valid attributes here!
3663*00b67f09SDavid van Moolenbroek 	 */
3664*00b67f09SDavid van Moolenbroek 
3665*00b67f09SDavid van Moolenbroek 	LOCK(&disp->lock);
3666*00b67f09SDavid van Moolenbroek 
3667*00b67f09SDavid van Moolenbroek 	if ((mask & DNS_DISPATCHATTR_NOLISTEN) != 0) {
3668*00b67f09SDavid van Moolenbroek 		if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0 &&
3669*00b67f09SDavid van Moolenbroek 		    (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0) {
3670*00b67f09SDavid van Moolenbroek 			disp->attributes &= ~DNS_DISPATCHATTR_NOLISTEN;
3671*00b67f09SDavid van Moolenbroek 			(void)startrecv(disp, NULL);
3672*00b67f09SDavid van Moolenbroek 		} else if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN)
3673*00b67f09SDavid van Moolenbroek 			   == 0 &&
3674*00b67f09SDavid van Moolenbroek 			   (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) {
3675*00b67f09SDavid van Moolenbroek 			disp->attributes |= DNS_DISPATCHATTR_NOLISTEN;
3676*00b67f09SDavid van Moolenbroek 			if (disp->recv_pending != 0)
3677*00b67f09SDavid van Moolenbroek 				isc_socket_cancel(disp->socket, disp->task[0],
3678*00b67f09SDavid van Moolenbroek 						  ISC_SOCKCANCEL_RECV);
3679*00b67f09SDavid van Moolenbroek 		}
3680*00b67f09SDavid van Moolenbroek 	}
3681*00b67f09SDavid van Moolenbroek 
3682*00b67f09SDavid van Moolenbroek 	disp->attributes &= ~mask;
3683*00b67f09SDavid van Moolenbroek 	disp->attributes |= (attributes & mask);
3684*00b67f09SDavid van Moolenbroek 	UNLOCK(&disp->lock);
3685*00b67f09SDavid van Moolenbroek }
3686*00b67f09SDavid van Moolenbroek 
3687*00b67f09SDavid van Moolenbroek void
dns_dispatch_importrecv(dns_dispatch_t * disp,isc_event_t * event)3688*00b67f09SDavid van Moolenbroek dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) {
3689*00b67f09SDavid van Moolenbroek 	void *buf;
3690*00b67f09SDavid van Moolenbroek 	isc_socketevent_t *sevent, *newsevent;
3691*00b67f09SDavid van Moolenbroek 
3692*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3693*00b67f09SDavid van Moolenbroek 	REQUIRE((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0);
3694*00b67f09SDavid van Moolenbroek 	REQUIRE(event != NULL);
3695*00b67f09SDavid van Moolenbroek 
3696*00b67f09SDavid van Moolenbroek 	sevent = (isc_socketevent_t *)event;
3697*00b67f09SDavid van Moolenbroek 
3698*00b67f09SDavid van Moolenbroek 	INSIST(sevent->n <= disp->mgr->buffersize);
3699*00b67f09SDavid van Moolenbroek 	newsevent = (isc_socketevent_t *)
3700*00b67f09SDavid van Moolenbroek 		    isc_event_allocate(disp->mgr->mctx, NULL,
3701*00b67f09SDavid van Moolenbroek 				      DNS_EVENT_IMPORTRECVDONE, udp_shrecv,
3702*00b67f09SDavid van Moolenbroek 				      disp, sizeof(isc_socketevent_t));
3703*00b67f09SDavid van Moolenbroek 	if (newsevent == NULL)
3704*00b67f09SDavid van Moolenbroek 		return;
3705*00b67f09SDavid van Moolenbroek 
3706*00b67f09SDavid van Moolenbroek 	buf = allocate_udp_buffer(disp);
3707*00b67f09SDavid van Moolenbroek 	if (buf == NULL) {
3708*00b67f09SDavid van Moolenbroek 		isc_event_free(ISC_EVENT_PTR(&newsevent));
3709*00b67f09SDavid van Moolenbroek 		return;
3710*00b67f09SDavid van Moolenbroek 	}
3711*00b67f09SDavid van Moolenbroek 	memmove(buf, sevent->region.base, sevent->n);
3712*00b67f09SDavid van Moolenbroek 	newsevent->region.base = buf;
3713*00b67f09SDavid van Moolenbroek 	newsevent->region.length = disp->mgr->buffersize;
3714*00b67f09SDavid van Moolenbroek 	newsevent->n = sevent->n;
3715*00b67f09SDavid van Moolenbroek 	newsevent->result = sevent->result;
3716*00b67f09SDavid van Moolenbroek 	newsevent->address = sevent->address;
3717*00b67f09SDavid van Moolenbroek 	newsevent->timestamp = sevent->timestamp;
3718*00b67f09SDavid van Moolenbroek 	newsevent->pktinfo = sevent->pktinfo;
3719*00b67f09SDavid van Moolenbroek 	newsevent->attributes = sevent->attributes;
3720*00b67f09SDavid van Moolenbroek 
3721*00b67f09SDavid van Moolenbroek 	isc_task_send(disp->task[0], ISC_EVENT_PTR(&newsevent));
3722*00b67f09SDavid van Moolenbroek }
3723*00b67f09SDavid van Moolenbroek 
3724*00b67f09SDavid van Moolenbroek dns_dispatch_t *
dns_dispatchset_get(dns_dispatchset_t * dset)3725*00b67f09SDavid van Moolenbroek dns_dispatchset_get(dns_dispatchset_t *dset) {
3726*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
3727*00b67f09SDavid van Moolenbroek 
3728*00b67f09SDavid van Moolenbroek 	/* check that dispatch set is configured */
3729*00b67f09SDavid van Moolenbroek 	if (dset == NULL || dset->ndisp == 0)
3730*00b67f09SDavid van Moolenbroek 		return (NULL);
3731*00b67f09SDavid van Moolenbroek 
3732*00b67f09SDavid van Moolenbroek 	LOCK(&dset->lock);
3733*00b67f09SDavid van Moolenbroek 	disp = dset->dispatches[dset->cur];
3734*00b67f09SDavid van Moolenbroek 	dset->cur++;
3735*00b67f09SDavid van Moolenbroek 	if (dset->cur == dset->ndisp)
3736*00b67f09SDavid van Moolenbroek 		dset->cur = 0;
3737*00b67f09SDavid van Moolenbroek 	UNLOCK(&dset->lock);
3738*00b67f09SDavid van Moolenbroek 
3739*00b67f09SDavid van Moolenbroek 	return (disp);
3740*00b67f09SDavid van Moolenbroek }
3741*00b67f09SDavid van Moolenbroek 
3742*00b67f09SDavid van Moolenbroek isc_result_t
dns_dispatchset_create(isc_mem_t * mctx,isc_socketmgr_t * sockmgr,isc_taskmgr_t * taskmgr,dns_dispatch_t * source,dns_dispatchset_t ** dsetp,int n)3743*00b67f09SDavid van Moolenbroek dns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr,
3744*00b67f09SDavid van Moolenbroek 		       isc_taskmgr_t *taskmgr, dns_dispatch_t *source,
3745*00b67f09SDavid van Moolenbroek 		       dns_dispatchset_t **dsetp, int n)
3746*00b67f09SDavid van Moolenbroek {
3747*00b67f09SDavid van Moolenbroek 	isc_result_t result;
3748*00b67f09SDavid van Moolenbroek 	dns_dispatchset_t *dset;
3749*00b67f09SDavid van Moolenbroek 	dns_dispatchmgr_t *mgr;
3750*00b67f09SDavid van Moolenbroek 	int i, j;
3751*00b67f09SDavid van Moolenbroek 
3752*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(source));
3753*00b67f09SDavid van Moolenbroek 	REQUIRE((source->attributes & DNS_DISPATCHATTR_UDP) != 0);
3754*00b67f09SDavid van Moolenbroek 	REQUIRE(dsetp != NULL && *dsetp == NULL);
3755*00b67f09SDavid van Moolenbroek 
3756*00b67f09SDavid van Moolenbroek 	mgr = source->mgr;
3757*00b67f09SDavid van Moolenbroek 
3758*00b67f09SDavid van Moolenbroek 	dset = isc_mem_get(mctx, sizeof(dns_dispatchset_t));
3759*00b67f09SDavid van Moolenbroek 	if (dset == NULL)
3760*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
3761*00b67f09SDavid van Moolenbroek 	memset(dset, 0, sizeof(*dset));
3762*00b67f09SDavid van Moolenbroek 
3763*00b67f09SDavid van Moolenbroek 	result = isc_mutex_init(&dset->lock);
3764*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
3765*00b67f09SDavid van Moolenbroek 		goto fail_alloc;
3766*00b67f09SDavid van Moolenbroek 
3767*00b67f09SDavid van Moolenbroek 	dset->dispatches = isc_mem_get(mctx, sizeof(dns_dispatch_t *) * n);
3768*00b67f09SDavid van Moolenbroek 	if (dset->dispatches == NULL) {
3769*00b67f09SDavid van Moolenbroek 		result = ISC_R_NOMEMORY;
3770*00b67f09SDavid van Moolenbroek 		goto fail_lock;
3771*00b67f09SDavid van Moolenbroek 	}
3772*00b67f09SDavid van Moolenbroek 
3773*00b67f09SDavid van Moolenbroek 	isc_mem_attach(mctx, &dset->mctx);
3774*00b67f09SDavid van Moolenbroek 	dset->ndisp = n;
3775*00b67f09SDavid van Moolenbroek 	dset->cur = 0;
3776*00b67f09SDavid van Moolenbroek 
3777*00b67f09SDavid van Moolenbroek 	dset->dispatches[0] = NULL;
3778*00b67f09SDavid van Moolenbroek 	dns_dispatch_attach(source, &dset->dispatches[0]);
3779*00b67f09SDavid van Moolenbroek 
3780*00b67f09SDavid van Moolenbroek 	LOCK(&mgr->lock);
3781*00b67f09SDavid van Moolenbroek 	for (i = 1; i < n; i++) {
3782*00b67f09SDavid van Moolenbroek 		dset->dispatches[i] = NULL;
3783*00b67f09SDavid van Moolenbroek 		result = dispatch_createudp(mgr, sockmgr, taskmgr,
3784*00b67f09SDavid van Moolenbroek 					    &source->local,
3785*00b67f09SDavid van Moolenbroek 					    source->maxrequests,
3786*00b67f09SDavid van Moolenbroek 					    source->attributes,
3787*00b67f09SDavid van Moolenbroek 					    &dset->dispatches[i],
3788*00b67f09SDavid van Moolenbroek 					    source->socket);
3789*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS)
3790*00b67f09SDavid van Moolenbroek 			goto fail;
3791*00b67f09SDavid van Moolenbroek 	}
3792*00b67f09SDavid van Moolenbroek 
3793*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
3794*00b67f09SDavid van Moolenbroek 	*dsetp = dset;
3795*00b67f09SDavid van Moolenbroek 
3796*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
3797*00b67f09SDavid van Moolenbroek 
3798*00b67f09SDavid van Moolenbroek  fail:
3799*00b67f09SDavid van Moolenbroek 	UNLOCK(&mgr->lock);
3800*00b67f09SDavid van Moolenbroek 
3801*00b67f09SDavid van Moolenbroek 	for (j = 0; j < i; j++)
3802*00b67f09SDavid van Moolenbroek 		dns_dispatch_detach(&(dset->dispatches[j]));
3803*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, dset->dispatches, sizeof(dns_dispatch_t *) * n);
3804*00b67f09SDavid van Moolenbroek 	if (dset->mctx == mctx)
3805*00b67f09SDavid van Moolenbroek 		isc_mem_detach(&dset->mctx);
3806*00b67f09SDavid van Moolenbroek 
3807*00b67f09SDavid van Moolenbroek  fail_lock:
3808*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&dset->lock);
3809*00b67f09SDavid van Moolenbroek 
3810*00b67f09SDavid van Moolenbroek  fail_alloc:
3811*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, dset, sizeof(dns_dispatchset_t));
3812*00b67f09SDavid van Moolenbroek 	return (result);
3813*00b67f09SDavid van Moolenbroek }
3814*00b67f09SDavid van Moolenbroek 
3815*00b67f09SDavid van Moolenbroek void
dns_dispatchset_cancelall(dns_dispatchset_t * dset,isc_task_t * task)3816*00b67f09SDavid van Moolenbroek dns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task) {
3817*00b67f09SDavid van Moolenbroek 	int i;
3818*00b67f09SDavid van Moolenbroek 
3819*00b67f09SDavid van Moolenbroek 	REQUIRE(dset != NULL);
3820*00b67f09SDavid van Moolenbroek 
3821*00b67f09SDavid van Moolenbroek 	for (i = 0; i < dset->ndisp; i++) {
3822*00b67f09SDavid van Moolenbroek 		isc_socket_t *sock;
3823*00b67f09SDavid van Moolenbroek 		sock = dns_dispatch_getsocket(dset->dispatches[i]);
3824*00b67f09SDavid van Moolenbroek 		isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
3825*00b67f09SDavid van Moolenbroek 	}
3826*00b67f09SDavid van Moolenbroek }
3827*00b67f09SDavid van Moolenbroek 
3828*00b67f09SDavid van Moolenbroek void
dns_dispatchset_destroy(dns_dispatchset_t ** dsetp)3829*00b67f09SDavid van Moolenbroek dns_dispatchset_destroy(dns_dispatchset_t **dsetp) {
3830*00b67f09SDavid van Moolenbroek 	dns_dispatchset_t *dset;
3831*00b67f09SDavid van Moolenbroek 	int i;
3832*00b67f09SDavid van Moolenbroek 
3833*00b67f09SDavid van Moolenbroek 	REQUIRE(dsetp != NULL && *dsetp != NULL);
3834*00b67f09SDavid van Moolenbroek 
3835*00b67f09SDavid van Moolenbroek 	dset = *dsetp;
3836*00b67f09SDavid van Moolenbroek 	for (i = 0; i < dset->ndisp; i++)
3837*00b67f09SDavid van Moolenbroek 		dns_dispatch_detach(&(dset->dispatches[i]));
3838*00b67f09SDavid van Moolenbroek 	isc_mem_put(dset->mctx, dset->dispatches,
3839*00b67f09SDavid van Moolenbroek 		    sizeof(dns_dispatch_t *) * dset->ndisp);
3840*00b67f09SDavid van Moolenbroek 	DESTROYLOCK(&dset->lock);
3841*00b67f09SDavid van Moolenbroek 	isc_mem_putanddetach(&dset->mctx, dset, sizeof(dns_dispatchset_t));
3842*00b67f09SDavid van Moolenbroek 
3843*00b67f09SDavid van Moolenbroek 	*dsetp = NULL;
3844*00b67f09SDavid van Moolenbroek }
3845*00b67f09SDavid van Moolenbroek 
3846*00b67f09SDavid van Moolenbroek void
dns_dispatch_setdscp(dns_dispatch_t * disp,isc_dscp_t dscp)3847*00b67f09SDavid van Moolenbroek dns_dispatch_setdscp(dns_dispatch_t *disp, isc_dscp_t dscp) {
3848*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3849*00b67f09SDavid van Moolenbroek 	disp->dscp = dscp;
3850*00b67f09SDavid van Moolenbroek }
3851*00b67f09SDavid van Moolenbroek 
3852*00b67f09SDavid van Moolenbroek isc_dscp_t
dns_dispatch_getdscp(dns_dispatch_t * disp)3853*00b67f09SDavid van Moolenbroek dns_dispatch_getdscp(dns_dispatch_t *disp) {
3854*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_DISPATCH(disp));
3855*00b67f09SDavid van Moolenbroek 	return (disp->dscp);
3856*00b67f09SDavid van Moolenbroek }
3857*00b67f09SDavid van Moolenbroek 
3858*00b67f09SDavid van Moolenbroek #if 0
3859*00b67f09SDavid van Moolenbroek void
3860*00b67f09SDavid van Moolenbroek dns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) {
3861*00b67f09SDavid van Moolenbroek 	dns_dispatch_t *disp;
3862*00b67f09SDavid van Moolenbroek 	char foo[1024];
3863*00b67f09SDavid van Moolenbroek 
3864*00b67f09SDavid van Moolenbroek 	disp = ISC_LIST_HEAD(mgr->list);
3865*00b67f09SDavid van Moolenbroek 	while (disp != NULL) {
3866*00b67f09SDavid van Moolenbroek 		isc_sockaddr_format(&disp->local, foo, sizeof(foo));
3867*00b67f09SDavid van Moolenbroek 		printf("\tdispatch %p, addr %s\n", disp, foo);
3868*00b67f09SDavid van Moolenbroek 		disp = ISC_LIST_NEXT(disp, link);
3869*00b67f09SDavid van Moolenbroek 	}
3870*00b67f09SDavid van Moolenbroek }
3871*00b67f09SDavid van Moolenbroek #endif
3872