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