1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24254402Serwin#include <isc/log.h>
25193149Sdougb#include <isc/platform.h>
26135446Strhodes#include <isc/print.h>
27135446Strhodes#include <isc/string.h>
28193149Sdougb#include <isc/random.h>
29254897Serwin#include <isc/socket.h>
30254897Serwin#include <isc/stats.h>
31135446Strhodes#include <isc/task.h>
32135446Strhodes#include <isc/timer.h>
33135446Strhodes#include <isc/util.h>
34135446Strhodes
35135446Strhodes#include <dns/acl.h>
36135446Strhodes#include <dns/adb.h>
37165071Sdougb#include <dns/cache.h>
38135446Strhodes#include <dns/db.h>
39135446Strhodes#include <dns/dispatch.h>
40165071Sdougb#include <dns/ds.h>
41135446Strhodes#include <dns/events.h>
42135446Strhodes#include <dns/forward.h>
43135446Strhodes#include <dns/keytable.h>
44135446Strhodes#include <dns/log.h>
45135446Strhodes#include <dns/message.h>
46135446Strhodes#include <dns/ncache.h>
47254402Serwin#include <dns/nsec.h>
48254402Serwin#include <dns/nsec3.h>
49135446Strhodes#include <dns/opcode.h>
50135446Strhodes#include <dns/peer.h>
51135446Strhodes#include <dns/rbt.h>
52135446Strhodes#include <dns/rcode.h>
53135446Strhodes#include <dns/rdata.h>
54135446Strhodes#include <dns/rdataclass.h>
55135446Strhodes#include <dns/rdatalist.h>
56135446Strhodes#include <dns/rdataset.h>
57135446Strhodes#include <dns/rdatastruct.h>
58135446Strhodes#include <dns/rdatatype.h>
59135446Strhodes#include <dns/resolver.h>
60135446Strhodes#include <dns/result.h>
61165071Sdougb#include <dns/rootns.h>
62193149Sdougb#include <dns/stats.h>
63135446Strhodes#include <dns/tsig.h>
64135446Strhodes#include <dns/validator.h>
65135446Strhodes
66135446Strhodes#define DNS_RESOLVER_TRACE
67135446Strhodes#ifdef DNS_RESOLVER_TRACE
68193149Sdougb#define RTRACE(m)       isc_log_write(dns_lctx, \
69135446Strhodes				      DNS_LOGCATEGORY_RESOLVER, \
70135446Strhodes				      DNS_LOGMODULE_RESOLVER, \
71135446Strhodes				      ISC_LOG_DEBUG(3), \
72135446Strhodes				      "res %p: %s", res, (m))
73193149Sdougb#define RRTRACE(r, m)   isc_log_write(dns_lctx, \
74135446Strhodes				      DNS_LOGCATEGORY_RESOLVER, \
75135446Strhodes				      DNS_LOGMODULE_RESOLVER, \
76135446Strhodes				      ISC_LOG_DEBUG(3), \
77135446Strhodes				      "res %p: %s", (r), (m))
78193149Sdougb#define FCTXTRACE(m)    isc_log_write(dns_lctx, \
79135446Strhodes				      DNS_LOGCATEGORY_RESOLVER, \
80135446Strhodes				      DNS_LOGMODULE_RESOLVER, \
81135446Strhodes				      ISC_LOG_DEBUG(3), \
82254402Serwin				      "fctx %p(%s): %s", fctx, fctx->info, (m))
83135446Strhodes#define FCTXTRACE2(m1, m2) \
84135446Strhodes			isc_log_write(dns_lctx, \
85135446Strhodes				      DNS_LOGCATEGORY_RESOLVER, \
86135446Strhodes				      DNS_LOGMODULE_RESOLVER, \
87135446Strhodes				      ISC_LOG_DEBUG(3), \
88135446Strhodes				      "fctx %p(%s): %s %s", \
89135446Strhodes				      fctx, fctx->info, (m1), (m2))
90193149Sdougb#define FTRACE(m)       isc_log_write(dns_lctx, \
91135446Strhodes				      DNS_LOGCATEGORY_RESOLVER, \
92135446Strhodes				      DNS_LOGMODULE_RESOLVER, \
93135446Strhodes				      ISC_LOG_DEBUG(3), \
94135446Strhodes				      "fetch %p (fctx %p(%s)): %s", \
95135446Strhodes				      fetch, fetch->private, \
96135446Strhodes				      fetch->private->info, (m))
97193149Sdougb#define QTRACE(m)       isc_log_write(dns_lctx, \
98135446Strhodes				      DNS_LOGCATEGORY_RESOLVER, \
99135446Strhodes				      DNS_LOGMODULE_RESOLVER, \
100135446Strhodes				      ISC_LOG_DEBUG(3), \
101135446Strhodes				      "resquery %p (fctx %p(%s)): %s", \
102135446Strhodes				      query, query->fctx, \
103135446Strhodes				      query->fctx->info, (m))
104135446Strhodes#else
105135446Strhodes#define RTRACE(m)
106135446Strhodes#define RRTRACE(r, m)
107135446Strhodes#define FCTXTRACE(m)
108262706Serwin#define FCTXTRACE2(m1, m2)
109135446Strhodes#define FTRACE(m)
110135446Strhodes#define QTRACE(m)
111135446Strhodes#endif
112135446Strhodes
113245163Serwin#define US_PER_SEC 1000000U
114245163Serwin/*
115245163Serwin * The maximum time we will wait for a single query.
116245163Serwin */
117245163Serwin#define MAX_SINGLE_QUERY_TIMEOUT 9U
118245163Serwin#define MAX_SINGLE_QUERY_TIMEOUT_US (MAX_SINGLE_QUERY_TIMEOUT*US_PER_SEC)
119245163Serwin
120245163Serwin/*
121245163Serwin * We need to allow a individual query time to complete / timeout.
122245163Serwin */
123245163Serwin#define MINIMUM_QUERY_TIMEOUT (MAX_SINGLE_QUERY_TIMEOUT + 1U)
124245163Serwin
125245163Serwin/* The default time in seconds for the whole query to live. */
126224092Sdougb#ifndef DEFAULT_QUERY_TIMEOUT
127245163Serwin#define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT
128224092Sdougb#endif
129224092Sdougb
130224092Sdougb#ifndef MAXIMUM_QUERY_TIMEOUT
131224092Sdougb#define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */
132224092Sdougb#endif
133224092Sdougb
134275672Sdelphij/* The default maximum number of recursions to follow before giving up. */
135275672Sdelphij#ifndef DEFAULT_RECURSION_DEPTH
136275672Sdelphij#define DEFAULT_RECURSION_DEPTH 7
137275672Sdelphij#endif
138275672Sdelphij
139275672Sdelphij/* The default maximum number of iterative queries to allow before giving up. */
140275672Sdelphij#ifndef DEFAULT_MAX_QUERIES
141275672Sdelphij#define DEFAULT_MAX_QUERIES 50
142275672Sdelphij#endif
143275672Sdelphij
144170222Sdougb/*%
145135446Strhodes * Maximum EDNS0 input packet size.
146135446Strhodes */
147193149Sdougb#define RECV_BUFFER_SIZE                4096            /* XXXRTH  Constant. */
148254402Serwin#define EDNSOPTS			2
149135446Strhodes
150170222Sdougb/*%
151135446Strhodes * This defines the maximum number of timeouts we will permit before we
152135446Strhodes * disable EDNS0 on the query.
153135446Strhodes */
154193149Sdougb#define MAX_EDNS0_TIMEOUTS      3
155135446Strhodes
156135446Strhodestypedef struct fetchctx fetchctx_t;
157135446Strhodes
158135446Strhodestypedef struct query {
159135446Strhodes	/* Locked by task event serialization. */
160135446Strhodes	unsigned int			magic;
161135446Strhodes	fetchctx_t *			fctx;
162135446Strhodes	isc_mem_t *			mctx;
163135446Strhodes	dns_dispatchmgr_t *		dispatchmgr;
164135446Strhodes	dns_dispatch_t *		dispatch;
165186462Sdougb	isc_boolean_t			exclusivesocket;
166135446Strhodes	dns_adbaddrinfo_t *		addrinfo;
167135446Strhodes	isc_socket_t *			tcpsocket;
168135446Strhodes	isc_time_t			start;
169135446Strhodes	dns_messageid_t			id;
170135446Strhodes	dns_dispentry_t *		dispentry;
171135446Strhodes	ISC_LINK(struct query)		link;
172135446Strhodes	isc_buffer_t			buffer;
173135446Strhodes	isc_buffer_t			*tsig;
174135446Strhodes	dns_tsigkey_t			*tsigkey;
175254897Serwin	isc_socketevent_t		sendevent;
176135446Strhodes	unsigned int			options;
177135446Strhodes	unsigned int			attributes;
178135446Strhodes	unsigned int			sends;
179135446Strhodes	unsigned int			connects;
180135446Strhodes	unsigned char			data[512];
181135446Strhodes} resquery_t;
182135446Strhodes
183135446Strhodes#define QUERY_MAGIC			ISC_MAGIC('Q', '!', '!', '!')
184135446Strhodes#define VALID_QUERY(query)		ISC_MAGIC_VALID(query, QUERY_MAGIC)
185135446Strhodes
186193149Sdougb#define RESQUERY_ATTR_CANCELED          0x02
187135446Strhodes
188193149Sdougb#define RESQUERY_CONNECTING(q)          ((q)->connects > 0)
189193149Sdougb#define RESQUERY_CANCELED(q)            (((q)->attributes & \
190135446Strhodes					  RESQUERY_ATTR_CANCELED) != 0)
191193149Sdougb#define RESQUERY_SENDING(q)             ((q)->sends > 0)
192135446Strhodes
193135446Strhodestypedef enum {
194193149Sdougb	fetchstate_init = 0,            /*%< Start event has not run yet. */
195135446Strhodes	fetchstate_active,
196193149Sdougb	fetchstate_done                 /*%< FETCHDONE events posted. */
197135446Strhodes} fetchstate;
198135446Strhodes
199193149Sdougbtypedef enum {
200193149Sdougb	badns_unreachable = 0,
201193149Sdougb	badns_response,
202193149Sdougb	badns_validation
203193149Sdougb} badnstype_t;
204193149Sdougb
205135446Strhodesstruct fetchctx {
206170222Sdougb	/*% Not locked. */
207135446Strhodes	unsigned int			magic;
208135446Strhodes	dns_resolver_t *		res;
209135446Strhodes	dns_name_t			name;
210135446Strhodes	dns_rdatatype_t			type;
211135446Strhodes	unsigned int			options;
212135446Strhodes	unsigned int			bucketnum;
213236374Sdougb	char *				info;
214236374Sdougb	isc_mem_t *			mctx;
215236374Sdougb
216170222Sdougb	/*% Locked by appropriate bucket lock. */
217135446Strhodes	fetchstate			state;
218135446Strhodes	isc_boolean_t			want_shutdown;
219135446Strhodes	isc_boolean_t			cloned;
220170222Sdougb	isc_boolean_t			spilled;
221135446Strhodes	unsigned int			references;
222135446Strhodes	isc_event_t			control_event;
223193149Sdougb	ISC_LINK(struct fetchctx)       link;
224193149Sdougb	ISC_LIST(dns_fetchevent_t)      events;
225170222Sdougb	/*% Locked by task event serialization. */
226135446Strhodes	dns_name_t			domain;
227135446Strhodes	dns_rdataset_t			nameservers;
228135446Strhodes	unsigned int			attributes;
229135446Strhodes	isc_timer_t *			timer;
230135446Strhodes	isc_time_t			expires;
231135446Strhodes	isc_interval_t			interval;
232135446Strhodes	dns_message_t *			qmessage;
233135446Strhodes	dns_message_t *			rmessage;
234135446Strhodes	ISC_LIST(resquery_t)		queries;
235135446Strhodes	dns_adbfindlist_t		finds;
236135446Strhodes	dns_adbfind_t *			find;
237135446Strhodes	dns_adbfindlist_t		altfinds;
238135446Strhodes	dns_adbfind_t *			altfind;
239135446Strhodes	dns_adbaddrinfolist_t		forwaddrs;
240135446Strhodes	dns_adbaddrinfolist_t		altaddrs;
241135446Strhodes	isc_sockaddrlist_t		forwarders;
242135446Strhodes	dns_fwdpolicy_t			fwdpolicy;
243135446Strhodes	isc_sockaddrlist_t		bad;
244170222Sdougb	isc_sockaddrlist_t		edns;
245170222Sdougb	isc_sockaddrlist_t		edns512;
246218384Sdougb	isc_sockaddrlist_t		bad_edns;
247174187Sdougb	dns_validator_t			*validator;
248193149Sdougb	ISC_LIST(dns_validator_t)       validators;
249135446Strhodes	dns_db_t *			cache;
250135446Strhodes	dns_adb_t *			adb;
251234010Sdougb	isc_boolean_t			ns_ttl_ok;
252234010Sdougb	isc_uint32_t			ns_ttl;
253135446Strhodes
254170222Sdougb	/*%
255135446Strhodes	 * The number of events we're waiting for.
256135446Strhodes	 */
257135446Strhodes	unsigned int			pending;
258135446Strhodes
259170222Sdougb	/*%
260135446Strhodes	 * The number of times we've "restarted" the current
261135446Strhodes	 * nameserver set.  This acts as a failsafe to prevent
262135446Strhodes	 * us from pounding constantly on a particular set of
263135446Strhodes	 * servers that, for whatever reason, are not giving
264135446Strhodes	 * us useful responses, but are responding in such a
265135446Strhodes	 * way that they are not marked "bad".
266135446Strhodes	 */
267135446Strhodes	unsigned int			restarts;
268135446Strhodes
269170222Sdougb	/*%
270186462Sdougb	 * The number of timeouts that have occurred since we
271135446Strhodes	 * last successfully received a response packet.  This
272135446Strhodes	 * is used for EDNS0 black hole detection.
273135446Strhodes	 */
274135446Strhodes	unsigned int			timeouts;
275193149Sdougb
276170222Sdougb	/*%
277135446Strhodes	 * Look aside state for DS lookups.
278135446Strhodes	 */
279186462Sdougb	dns_name_t 			nsname;
280135446Strhodes	dns_fetch_t *			nsfetch;
281135446Strhodes	dns_rdataset_t			nsrrset;
282166332Sdougb
283166332Sdougb	/*%
284166332Sdougb	 * Number of queries that reference this context.
285166332Sdougb	 */
286166332Sdougb	unsigned int			nqueries;
287193149Sdougb
288193149Sdougb	/*%
289193149Sdougb	 * The reason to print when logging a successful
290193149Sdougb	 * response to a query.
291193149Sdougb	 */
292193149Sdougb	const char *			reason;
293193149Sdougb
294193149Sdougb	/*%
295193149Sdougb	 * Random numbers to use for mixing up server addresses.
296193149Sdougb	 */
297193149Sdougb	isc_uint32_t                    rand_buf;
298193149Sdougb	isc_uint32_t                    rand_bits;
299193149Sdougb
300193149Sdougb	/*%
301193149Sdougb	 * Fetch-local statistics for detailed logging.
302193149Sdougb	 */
303193149Sdougb	isc_result_t			result; /*%< fetch result  */
304193149Sdougb	isc_result_t			vresult; /*%< validation result  */
305193149Sdougb	int				exitline;
306193149Sdougb	isc_time_t			start;
307193149Sdougb	isc_uint64_t			duration;
308193149Sdougb	isc_boolean_t			logged;
309193149Sdougb	unsigned int			querysent;
310275672Sdelphij	unsigned int			totalqueries;
311193149Sdougb	unsigned int			referrals;
312193149Sdougb	unsigned int			lamecount;
313193149Sdougb	unsigned int			neterr;
314193149Sdougb	unsigned int			badresp;
315193149Sdougb	unsigned int			adberr;
316193149Sdougb	unsigned int			findfail;
317193149Sdougb	unsigned int			valfail;
318193149Sdougb	isc_boolean_t			timeout;
319224092Sdougb	dns_adbaddrinfo_t 		*addrinfo;
320224092Sdougb	isc_sockaddr_t			*client;
321275672Sdelphij	unsigned int			depth;
322135446Strhodes};
323135446Strhodes
324135446Strhodes#define FCTX_MAGIC			ISC_MAGIC('F', '!', '!', '!')
325135446Strhodes#define VALID_FCTX(fctx)		ISC_MAGIC_VALID(fctx, FCTX_MAGIC)
326135446Strhodes
327193149Sdougb#define FCTX_ATTR_HAVEANSWER            0x0001
328193149Sdougb#define FCTX_ATTR_GLUING                0x0002
329193149Sdougb#define FCTX_ATTR_ADDRWAIT              0x0004
330193149Sdougb#define FCTX_ATTR_SHUTTINGDOWN          0x0008
331193149Sdougb#define FCTX_ATTR_WANTCACHE             0x0010
332193149Sdougb#define FCTX_ATTR_WANTNCACHE            0x0020
333193149Sdougb#define FCTX_ATTR_NEEDEDNS0             0x0040
334193149Sdougb#define FCTX_ATTR_TRIEDFIND             0x0080
335193149Sdougb#define FCTX_ATTR_TRIEDALT              0x0100
336135446Strhodes
337193149Sdougb#define HAVE_ANSWER(f)          (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \
338135446Strhodes				 0)
339193149Sdougb#define GLUING(f)               (((f)->attributes & FCTX_ATTR_GLUING) != \
340135446Strhodes				 0)
341193149Sdougb#define ADDRWAIT(f)             (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \
342135446Strhodes				 0)
343193149Sdougb#define SHUTTINGDOWN(f)         (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \
344174187Sdougb				 != 0)
345193149Sdougb#define WANTCACHE(f)            (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0)
346193149Sdougb#define WANTNCACHE(f)           (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)
347193149Sdougb#define NEEDEDNS0(f)            (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0)
348193149Sdougb#define TRIEDFIND(f)            (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0)
349193149Sdougb#define TRIEDALT(f)             (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0)
350135446Strhodes
351153816Sdougbtypedef struct {
352153816Sdougb	dns_adbaddrinfo_t *		addrinfo;
353153816Sdougb	fetchctx_t *			fctx;
354153816Sdougb} dns_valarg_t;
355153816Sdougb
356135446Strhodesstruct dns_fetch {
357135446Strhodes	unsigned int			magic;
358135446Strhodes	fetchctx_t *			private;
359135446Strhodes};
360135446Strhodes
361135446Strhodes#define DNS_FETCH_MAGIC			ISC_MAGIC('F', 't', 'c', 'h')
362135446Strhodes#define DNS_FETCH_VALID(fetch)		ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC)
363135446Strhodes
364135446Strhodestypedef struct fctxbucket {
365135446Strhodes	isc_task_t *			task;
366135446Strhodes	isc_mutex_t			lock;
367135446Strhodes	ISC_LIST(fetchctx_t)		fctxs;
368135446Strhodes	isc_boolean_t			exiting;
369170222Sdougb	isc_mem_t *			mctx;
370135446Strhodes} fctxbucket_t;
371135446Strhodes
372135446Strhodestypedef struct alternate {
373135446Strhodes	isc_boolean_t			isaddress;
374193149Sdougb	union   {
375135446Strhodes		isc_sockaddr_t		addr;
376135446Strhodes		struct {
377193149Sdougb			dns_name_t      name;
378193149Sdougb			in_port_t       port;
379135446Strhodes		} _n;
380135446Strhodes	} _u;
381193149Sdougb	ISC_LINK(struct alternate)      link;
382135446Strhodes} alternate_t;
383135446Strhodes
384205292Sdougbtypedef struct dns_badcache dns_badcache_t;
385205292Sdougbstruct dns_badcache {
386205292Sdougb	dns_badcache_t *	next;
387205292Sdougb	dns_rdatatype_t 	type;
388205292Sdougb	isc_time_t		expire;
389205292Sdougb	unsigned int		hashval;
390205292Sdougb	dns_name_t		name;
391205292Sdougb};
392205292Sdougb#define DNS_BADCACHE_SIZE 1021
393205292Sdougb#define DNS_BADCACHE_TTL(fctx) \
394205292Sdougb	(((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30)
395205292Sdougb
396135446Strhodesstruct dns_resolver {
397135446Strhodes	/* Unlocked. */
398135446Strhodes	unsigned int			magic;
399135446Strhodes	isc_mem_t *			mctx;
400135446Strhodes	isc_mutex_t			lock;
401186462Sdougb	isc_mutex_t			nlock;
402186462Sdougb	isc_mutex_t			primelock;
403135446Strhodes	dns_rdataclass_t		rdclass;
404135446Strhodes	isc_socketmgr_t *		socketmgr;
405135446Strhodes	isc_timermgr_t *		timermgr;
406135446Strhodes	isc_taskmgr_t *			taskmgr;
407135446Strhodes	dns_view_t *			view;
408135446Strhodes	isc_boolean_t			frozen;
409135446Strhodes	unsigned int			options;
410135446Strhodes	dns_dispatchmgr_t *		dispatchmgr;
411254897Serwin	dns_dispatchset_t *		dispatches4;
412186462Sdougb	isc_boolean_t			exclusivev4;
413254897Serwin	dns_dispatchset_t *		dispatches6;
414186462Sdougb	isc_boolean_t			exclusivev6;
415135446Strhodes	unsigned int			nbuckets;
416135446Strhodes	fctxbucket_t *			buckets;
417135446Strhodes	isc_uint32_t			lame_ttl;
418135446Strhodes	ISC_LIST(alternate_t)		alternates;
419135446Strhodes	isc_uint16_t			udpsize;
420135446Strhodes#if USE_ALGLOCK
421135446Strhodes	isc_rwlock_t			alglock;
422135446Strhodes#endif
423135446Strhodes	dns_rbt_t *			algorithms;
424135446Strhodes#if USE_MBSLOCK
425135446Strhodes	isc_rwlock_t			mbslock;
426135446Strhodes#endif
427135446Strhodes	dns_rbt_t *			mustbesecure;
428170222Sdougb	unsigned int			spillatmax;
429170222Sdougb	unsigned int			spillatmin;
430170222Sdougb	isc_timer_t *			spillattimer;
431170222Sdougb	isc_boolean_t			zero_no_soa_ttl;
432224092Sdougb	unsigned int			query_timeout;
433275672Sdelphij	unsigned int			maxdepth;
434193149Sdougb
435135446Strhodes	/* Locked by lock. */
436135446Strhodes	unsigned int			references;
437135446Strhodes	isc_boolean_t			exiting;
438135446Strhodes	isc_eventlist_t			whenshutdown;
439135446Strhodes	unsigned int			activebuckets;
440135446Strhodes	isc_boolean_t			priming;
441186462Sdougb	unsigned int			spillat;	/* clients-per-query */
442205292Sdougb
443205292Sdougb	/* Bad cache. */
444205292Sdougb	dns_badcache_t  ** 		badcache;
445205292Sdougb	unsigned int 			badcount;
446205292Sdougb	unsigned int 			badhash;
447205292Sdougb	unsigned int 			badsweep;
448205292Sdougb
449135446Strhodes	/* Locked by primelock. */
450135446Strhodes	dns_fetch_t *			primefetch;
451135446Strhodes	/* Locked by nlock. */
452135446Strhodes	unsigned int			nfctx;
453135446Strhodes};
454135446Strhodes
455135446Strhodes#define RES_MAGIC			ISC_MAGIC('R', 'e', 's', '!')
456135446Strhodes#define VALID_RESOLVER(res)		ISC_MAGIC_VALID(res, RES_MAGIC)
457135446Strhodes
458170222Sdougb/*%
459135446Strhodes * Private addrinfo flags.  These must not conflict with DNS_FETCHOPT_NOEDNS0,
460135446Strhodes * which we also use as an addrinfo flag.
461135446Strhodes */
462193149Sdougb#define FCTX_ADDRINFO_MARK              0x0001
463193149Sdougb#define FCTX_ADDRINFO_FORWARDER         0x1000
464193149Sdougb#define FCTX_ADDRINFO_TRIED             0x2000
465193149Sdougb#define UNMARKED(a)                     (((a)->flags & FCTX_ADDRINFO_MARK) \
466135446Strhodes					 == 0)
467193149Sdougb#define ISFORWARDER(a)                  (((a)->flags & \
468135446Strhodes					 FCTX_ADDRINFO_FORWARDER) != 0)
469193149Sdougb#define TRIED(a)                        (((a)->flags & \
470193149Sdougb					 FCTX_ADDRINFO_TRIED) != 0)
471135446Strhodes
472135446Strhodes#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
473223812Sdougb#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
474135446Strhodes
475135446Strhodesstatic void destroy(dns_resolver_t *res);
476135446Strhodesstatic void empty_bucket(dns_resolver_t *res);
477135446Strhodesstatic isc_result_t resquery_send(resquery_t *query);
478135446Strhodesstatic void resquery_response(isc_task_t *task, isc_event_t *event);
479135446Strhodesstatic void resquery_connected(isc_task_t *task, isc_event_t *event);
480205292Sdougbstatic void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying,
481205292Sdougb		     isc_boolean_t badcache);
482236374Sdougbstatic void fctx_destroy(fetchctx_t *fctx);
483236374Sdougbstatic isc_boolean_t fctx_unlink(fetchctx_t *fctx);
484135446Strhodesstatic isc_result_t ncache_adderesult(dns_message_t *message,
485135446Strhodes				      dns_db_t *cache, dns_dbnode_t *node,
486135446Strhodes				      dns_rdatatype_t covers,
487135446Strhodes				      isc_stdtime_t now, dns_ttl_t maxttl,
488193149Sdougb				      isc_boolean_t optout,
489254402Serwin				      isc_boolean_t secure,
490135446Strhodes				      dns_rdataset_t *ardataset,
491135446Strhodes				      isc_result_t *eresultp);
492186462Sdougbstatic void validated(isc_task_t *task, isc_event_t *event);
493234010Sdougbstatic isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked);
494186462Sdougbstatic void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
495193149Sdougb		    isc_result_t reason, badnstype_t badtype);
496254402Serwinstatic inline isc_result_t findnoqname(fetchctx_t *fctx, dns_name_t *name,
497254402Serwin				       dns_rdatatype_t type,
498254402Serwin				       dns_name_t **noqname);
499135446Strhodes
500193149Sdougb/*%
501193149Sdougb * Increment resolver-related statistics counters.
502193149Sdougb */
503193149Sdougbstatic inline void
504193149Sdougbinc_stats(dns_resolver_t *res, isc_statscounter_t counter) {
505193149Sdougb	if (res->view->resstats != NULL)
506193149Sdougb		isc_stats_increment(res->view->resstats, counter);
507193149Sdougb}
508193149Sdougb
509153816Sdougbstatic isc_result_t
510153816Sdougbvalcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
511153816Sdougb	  dns_rdatatype_t type, dns_rdataset_t *rdataset,
512153816Sdougb	  dns_rdataset_t *sigrdataset, unsigned int valoptions,
513153816Sdougb	  isc_task_t *task)
514153816Sdougb{
515153816Sdougb	dns_validator_t *validator = NULL;
516153816Sdougb	dns_valarg_t *valarg;
517153816Sdougb	isc_result_t result;
518153816Sdougb
519236374Sdougb	valarg = isc_mem_get(fctx->mctx, sizeof(*valarg));
520153816Sdougb	if (valarg == NULL)
521153816Sdougb		return (ISC_R_NOMEMORY);
522153816Sdougb
523153816Sdougb	valarg->fctx = fctx;
524153816Sdougb	valarg->addrinfo = addrinfo;
525153816Sdougb
526166332Sdougb	if (!ISC_LIST_EMPTY(fctx->validators))
527308205Sdelphij		valoptions |= DNS_VALIDATOR_DEFER;
528308205Sdelphij	else
529308205Sdelphij		valoptions &= ~DNS_VALIDATOR_DEFER;
530166332Sdougb
531153816Sdougb	result = dns_validator_create(fctx->res->view, name, type, rdataset,
532153816Sdougb				      sigrdataset, fctx->rmessage,
533153816Sdougb				      valoptions, task, validated, valarg,
534153816Sdougb				      &validator);
535174187Sdougb	if (result == ISC_R_SUCCESS) {
536193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_val);
537174187Sdougb		if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
538174187Sdougb			INSIST(fctx->validator == NULL);
539218384Sdougb			fctx->validator = validator;
540174187Sdougb		}
541153816Sdougb		ISC_LIST_APPEND(fctx->validators, validator, link);
542174187Sdougb	} else
543236374Sdougb		isc_mem_put(fctx->mctx, valarg, sizeof(*valarg));
544153816Sdougb	return (result);
545153816Sdougb}
546153816Sdougb
547135446Strhodesstatic isc_boolean_t
548194995Sdougbrrsig_fromchildzone(fetchctx_t *fctx, dns_rdataset_t *rdataset) {
549194995Sdougb	dns_namereln_t namereln;
550194995Sdougb	dns_rdata_rrsig_t rrsig;
551194995Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
552194995Sdougb	int order;
553194995Sdougb	isc_result_t result;
554194995Sdougb	unsigned int labels;
555194995Sdougb
556194995Sdougb	for (result = dns_rdataset_first(rdataset);
557194995Sdougb	     result == ISC_R_SUCCESS;
558194995Sdougb	     result = dns_rdataset_next(rdataset)) {
559194995Sdougb		dns_rdataset_current(rdataset, &rdata);
560194995Sdougb		result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
561194995Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
562194995Sdougb		namereln = dns_name_fullcompare(&rrsig.signer, &fctx->domain,
563194995Sdougb						&order, &labels);
564194995Sdougb		if (namereln == dns_namereln_subdomain)
565194995Sdougb			return (ISC_TRUE);
566194995Sdougb		dns_rdata_reset(&rdata);
567194995Sdougb	}
568194995Sdougb	return (ISC_FALSE);
569194995Sdougb}
570194995Sdougb
571194995Sdougbstatic isc_boolean_t
572135446Strhodesfix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
573135446Strhodes	dns_name_t *name;
574135446Strhodes	dns_name_t *domain = &fctx->domain;
575135446Strhodes	dns_rdataset_t *rdataset;
576135446Strhodes	dns_rdatatype_t type;
577135446Strhodes	isc_result_t result;
578135446Strhodes	isc_boolean_t keep_auth = ISC_FALSE;
579135446Strhodes
580135446Strhodes	if (message->rcode == dns_rcode_nxdomain)
581135446Strhodes		return (ISC_FALSE);
582135446Strhodes
583135446Strhodes	/*
584194995Sdougb	 * A DS RRset can appear anywhere in a zone, even for a delegation-only
585194995Sdougb	 * zone.  So a response to an explicit query for this type should be
586194995Sdougb	 * excluded from delegation-only fixup.
587194995Sdougb	 *
588194995Sdougb	 * SOA, NS, and DNSKEY can only exist at a zone apex, so a postive
589194995Sdougb	 * response to a query for these types can never violate the
590194995Sdougb	 * delegation-only assumption: if the query name is below a
591194995Sdougb	 * zone cut, the response should normally be a referral, which should
592194995Sdougb	 * be accepted; if the query name is below a zone cut but the server
593194995Sdougb	 * happens to have authority for the zone of the query name, the
594194995Sdougb	 * response is a (non-referral) answer.  But this does not violate
595194995Sdougb	 * delegation-only because the query name must be in a different zone
596194995Sdougb	 * due to the "apex-only" nature of these types.  Note that if the
597194995Sdougb	 * remote server happens to have authority for a child zone of a
598194995Sdougb	 * delegation-only zone, we may still incorrectly "fix" the response
599194995Sdougb	 * with NXDOMAIN for queries for other types.  Unfortunately it's
600194995Sdougb	 * generally impossible to differentiate this case from violation of
601194995Sdougb	 * the delegation-only assumption.  Once the resolver learns the
602194995Sdougb	 * correct zone cut, possibly via a separate query for an "apex-only"
603194995Sdougb	 * type, queries for other types will be resolved correctly.
604194995Sdougb	 *
605194995Sdougb	 * A query for type ANY will be accepted if it hits an exceptional
606194995Sdougb	 * type above in the answer section as it should be from a child
607194995Sdougb	 * zone.
608194995Sdougb	 *
609194995Sdougb	 * Also accept answers with RRSIG records from the child zone.
610194995Sdougb	 * Direct queries for RRSIG records should not be answered from
611194995Sdougb	 * the parent zone.
612135446Strhodes	 */
613194995Sdougb
614135446Strhodes	if (message->counts[DNS_SECTION_ANSWER] != 0 &&
615135446Strhodes	    (fctx->type == dns_rdatatype_ns ||
616194995Sdougb	     fctx->type == dns_rdatatype_ds ||
617194995Sdougb	     fctx->type == dns_rdatatype_soa ||
618194995Sdougb	     fctx->type == dns_rdatatype_any ||
619194995Sdougb	     fctx->type == dns_rdatatype_rrsig ||
620194995Sdougb	     fctx->type == dns_rdatatype_dnskey)) {
621135446Strhodes		result = dns_message_firstname(message, DNS_SECTION_ANSWER);
622135446Strhodes		while (result == ISC_R_SUCCESS) {
623135446Strhodes			name = NULL;
624135446Strhodes			dns_message_currentname(message, DNS_SECTION_ANSWER,
625135446Strhodes						&name);
626135446Strhodes			for (rdataset = ISC_LIST_HEAD(name->list);
627135446Strhodes			     rdataset != NULL;
628135446Strhodes			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
629194995Sdougb				if (!dns_name_equal(name, &fctx->name))
630194995Sdougb					continue;
631135446Strhodes				type = rdataset->type;
632194995Sdougb				/*
633194995Sdougb				 * RRsig from child?
634194995Sdougb				 */
635194995Sdougb				if (type == dns_rdatatype_rrsig &&
636194995Sdougb				    rrsig_fromchildzone(fctx, rdataset))
637135446Strhodes					return (ISC_FALSE);
638194995Sdougb				/*
639194995Sdougb				 * Direct query for apex records or DS.
640194995Sdougb				 */
641194995Sdougb				if (fctx->type == type &&
642194995Sdougb				    (type == dns_rdatatype_ds ||
643194995Sdougb				     type == dns_rdatatype_ns ||
644194995Sdougb				     type == dns_rdatatype_soa ||
645194995Sdougb				     type == dns_rdatatype_dnskey))
646194995Sdougb					return (ISC_FALSE);
647194995Sdougb				/*
648194995Sdougb				 * Indirect query for apex records or DS.
649194995Sdougb				 */
650194995Sdougb				if (fctx->type == dns_rdatatype_any &&
651194995Sdougb				    (type == dns_rdatatype_ns ||
652194995Sdougb				     type == dns_rdatatype_ds ||
653194995Sdougb				     type == dns_rdatatype_soa ||
654194995Sdougb				     type == dns_rdatatype_dnskey))
655194995Sdougb					return (ISC_FALSE);
656135446Strhodes			}
657135446Strhodes			result = dns_message_nextname(message,
658135446Strhodes						      DNS_SECTION_ANSWER);
659135446Strhodes		}
660135446Strhodes	}
661135446Strhodes
662194995Sdougb	/*
663194995Sdougb	 * A NODATA response to a DS query?
664194995Sdougb	 */
665194995Sdougb	if (fctx->type == dns_rdatatype_ds &&
666194995Sdougb	    message->counts[DNS_SECTION_ANSWER] == 0)
667194995Sdougb		return (ISC_FALSE);
668194995Sdougb
669194995Sdougb	/* Look for referral or indication of answer from child zone? */
670135446Strhodes	if (message->counts[DNS_SECTION_AUTHORITY] == 0)
671135446Strhodes		goto munge;
672135446Strhodes
673135446Strhodes	result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
674135446Strhodes	while (result == ISC_R_SUCCESS) {
675135446Strhodes		name = NULL;
676135446Strhodes		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
677135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
678135446Strhodes		     rdataset != NULL;
679135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
680135446Strhodes			type = rdataset->type;
681135446Strhodes			if (type == dns_rdatatype_soa &&
682135446Strhodes			    dns_name_equal(name, domain))
683135446Strhodes				keep_auth = ISC_TRUE;
684194995Sdougb
685135446Strhodes			if (type != dns_rdatatype_ns &&
686194995Sdougb			    type != dns_rdatatype_soa &&
687194995Sdougb			    type != dns_rdatatype_rrsig)
688135446Strhodes				continue;
689194995Sdougb
690194995Sdougb			if (type == dns_rdatatype_rrsig) {
691194995Sdougb				if (rrsig_fromchildzone(fctx, rdataset))
692194995Sdougb					return (ISC_FALSE);
693194995Sdougb				else
694194995Sdougb					continue;
695194995Sdougb			}
696194995Sdougb
697194995Sdougb			/* NS or SOA records. */
698194995Sdougb			if (dns_name_equal(name, domain)) {
699194995Sdougb				/*
700194995Sdougb				 * If a query for ANY causes a negative
701194995Sdougb				 * response, we can be sure that this is
702194995Sdougb				 * an empty node.  For other type of queries
703194995Sdougb				 * we cannot differentiate an empty node
704194995Sdougb				 * from a node that just doesn't have that
705194995Sdougb				 * type of record.  We only accept the former
706194995Sdougb				 * case.
707194995Sdougb				 */
708194995Sdougb				if (message->counts[DNS_SECTION_ANSWER] == 0 &&
709194995Sdougb				    fctx->type == dns_rdatatype_any)
710194995Sdougb					return (ISC_FALSE);
711194995Sdougb			} else if (dns_name_issubdomain(name, domain)) {
712194995Sdougb				/* Referral or answer from child zone. */
713135446Strhodes				return (ISC_FALSE);
714194995Sdougb			}
715135446Strhodes		}
716135446Strhodes		result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
717135446Strhodes	}
718135446Strhodes
719135446Strhodes munge:
720135446Strhodes	message->rcode = dns_rcode_nxdomain;
721135446Strhodes	message->counts[DNS_SECTION_ANSWER] = 0;
722135446Strhodes	if (!keep_auth)
723135446Strhodes		message->counts[DNS_SECTION_AUTHORITY] = 0;
724135446Strhodes	message->counts[DNS_SECTION_ADDITIONAL] = 0;
725135446Strhodes	return (ISC_TRUE);
726135446Strhodes}
727135446Strhodes
728135446Strhodesstatic inline isc_result_t
729135446Strhodesfctx_starttimer(fetchctx_t *fctx) {
730135446Strhodes	/*
731135446Strhodes	 * Start the lifetime timer for fctx.
732135446Strhodes	 *
733135446Strhodes	 * This is also used for stopping the idle timer; in that
734135446Strhodes	 * case we must purge events already posted to ensure that
735135446Strhodes	 * no further idle events are delivered.
736135446Strhodes	 */
737135446Strhodes	return (isc_timer_reset(fctx->timer, isc_timertype_once,
738165071Sdougb				&fctx->expires, NULL, ISC_TRUE));
739135446Strhodes}
740135446Strhodes
741135446Strhodesstatic inline void
742135446Strhodesfctx_stoptimer(fetchctx_t *fctx) {
743135446Strhodes	isc_result_t result;
744135446Strhodes
745135446Strhodes	/*
746135446Strhodes	 * We don't return a result if resetting the timer to inactive fails
747135446Strhodes	 * since there's nothing to be done about it.  Resetting to inactive
748135446Strhodes	 * should never fail anyway, since the code as currently written
749135446Strhodes	 * cannot fail in that case.
750135446Strhodes	 */
751135446Strhodes	result = isc_timer_reset(fctx->timer, isc_timertype_inactive,
752135446Strhodes				  NULL, NULL, ISC_TRUE);
753135446Strhodes	if (result != ISC_R_SUCCESS) {
754135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
755135446Strhodes				 "isc_timer_reset(): %s",
756135446Strhodes				 isc_result_totext(result));
757135446Strhodes	}
758135446Strhodes}
759135446Strhodes
760135446Strhodes
761135446Strhodesstatic inline isc_result_t
762193149Sdougbfctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) {
763135446Strhodes	/*
764135446Strhodes	 * Start the idle timer for fctx.  The lifetime timer continues
765135446Strhodes	 * to be in effect.
766135446Strhodes	 */
767135446Strhodes	return (isc_timer_reset(fctx->timer, isc_timertype_once,
768193149Sdougb				&fctx->expires, interval, ISC_FALSE));
769135446Strhodes}
770135446Strhodes
771135446Strhodes/*
772135446Strhodes * Stopping the idle timer is equivalent to calling fctx_starttimer(), but
773135446Strhodes * we use fctx_stopidletimer for readability in the code below.
774135446Strhodes */
775193149Sdougb#define fctx_stopidletimer      fctx_starttimer
776135446Strhodes
777135446Strhodes
778135446Strhodesstatic inline void
779135446Strhodesresquery_destroy(resquery_t **queryp) {
780135446Strhodes	resquery_t *query;
781135446Strhodes
782135446Strhodes	REQUIRE(queryp != NULL);
783135446Strhodes	query = *queryp;
784135446Strhodes	REQUIRE(!ISC_LINK_LINKED(query, link));
785135446Strhodes
786135446Strhodes	INSIST(query->tcpsocket == NULL);
787135446Strhodes
788166332Sdougb	query->fctx->nqueries--;
789234010Sdougb	if (SHUTTINGDOWN(query->fctx)) {
790234010Sdougb		dns_resolver_t *res = query->fctx->res;
791234010Sdougb		if (maybe_destroy(query->fctx, ISC_FALSE))
792234010Sdougb			empty_bucket(res);
793234010Sdougb	}
794135446Strhodes	query->magic = 0;
795135446Strhodes	isc_mem_put(query->mctx, query, sizeof(*query));
796135446Strhodes	*queryp = NULL;
797135446Strhodes}
798135446Strhodes
799135446Strhodesstatic void
800135446Strhodesfctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
801135446Strhodes		 isc_time_t *finish, isc_boolean_t no_response)
802135446Strhodes{
803135446Strhodes	fetchctx_t *fctx;
804135446Strhodes	resquery_t *query;
805193149Sdougb	unsigned int rtt, rttms;
806135446Strhodes	unsigned int factor;
807135446Strhodes	dns_adbfind_t *find;
808135446Strhodes	dns_adbaddrinfo_t *addrinfo;
809186462Sdougb	isc_socket_t *socket;
810135446Strhodes
811135446Strhodes	query = *queryp;
812135446Strhodes	fctx = query->fctx;
813135446Strhodes
814135446Strhodes	FCTXTRACE("cancelquery");
815135446Strhodes
816135446Strhodes	REQUIRE(!RESQUERY_CANCELED(query));
817135446Strhodes
818135446Strhodes	query->attributes |= RESQUERY_ATTR_CANCELED;
819135446Strhodes
820135446Strhodes	/*
821135446Strhodes	 * Should we update the RTT?
822135446Strhodes	 */
823135446Strhodes	if (finish != NULL || no_response) {
824135446Strhodes		if (finish != NULL) {
825135446Strhodes			/*
826135446Strhodes			 * We have both the start and finish times for this
827135446Strhodes			 * packet, so we can compute a real RTT.
828135446Strhodes			 */
829135446Strhodes			rtt = (unsigned int)isc_time_microdiff(finish,
830135446Strhodes							       &query->start);
831135446Strhodes			factor = DNS_ADB_RTTADJDEFAULT;
832193149Sdougb
833193149Sdougb			rttms = rtt / 1000;
834193149Sdougb			if (rttms < DNS_RESOLVER_QRYRTTCLASS0) {
835193149Sdougb				inc_stats(fctx->res,
836193149Sdougb					  dns_resstatscounter_queryrtt0);
837193149Sdougb			} else if (rttms < DNS_RESOLVER_QRYRTTCLASS1) {
838193149Sdougb				inc_stats(fctx->res,
839193149Sdougb					  dns_resstatscounter_queryrtt1);
840193149Sdougb			} else if (rttms < DNS_RESOLVER_QRYRTTCLASS2) {
841193149Sdougb				inc_stats(fctx->res,
842193149Sdougb					  dns_resstatscounter_queryrtt2);
843193149Sdougb			} else if (rttms < DNS_RESOLVER_QRYRTTCLASS3) {
844193149Sdougb				inc_stats(fctx->res,
845193149Sdougb					  dns_resstatscounter_queryrtt3);
846193149Sdougb			} else if (rttms < DNS_RESOLVER_QRYRTTCLASS4) {
847193149Sdougb				inc_stats(fctx->res,
848193149Sdougb					  dns_resstatscounter_queryrtt4);
849193149Sdougb			} else {
850193149Sdougb				inc_stats(fctx->res,
851193149Sdougb					  dns_resstatscounter_queryrtt5);
852193149Sdougb			}
853135446Strhodes		} else {
854135446Strhodes			/*
855135446Strhodes			 * We don't have an RTT for this query.  Maybe the
856135446Strhodes			 * packet was lost, or maybe this server is very
857135446Strhodes			 * slow.  We don't know.  Increase the RTT.
858135446Strhodes			 */
859135446Strhodes			INSIST(no_response);
860170222Sdougb			rtt = query->addrinfo->srtt + 200000;
861245163Serwin			if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US)
862245163Serwin				rtt = MAX_SINGLE_QUERY_TIMEOUT_US;
863135446Strhodes			/*
864135446Strhodes			 * Replace the current RTT with our value.
865135446Strhodes			 */
866135446Strhodes			factor = DNS_ADB_RTTADJREPLACE;
867135446Strhodes		}
868135446Strhodes		dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
869135446Strhodes	}
870135446Strhodes
871193149Sdougb	/* Remember that the server has been tried. */
872193149Sdougb	if (!TRIED(query->addrinfo)) {
873193149Sdougb		dns_adb_changeflags(fctx->adb, query->addrinfo,
874193149Sdougb				    FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED);
875193149Sdougb	}
876193149Sdougb
877135446Strhodes	/*
878135446Strhodes	 * Age RTTs of servers not tried.
879135446Strhodes	 */
880135446Strhodes	factor = DNS_ADB_RTTADJAGE;
881135446Strhodes	if (finish != NULL)
882135446Strhodes		for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
883135446Strhodes		     addrinfo != NULL;
884135446Strhodes		     addrinfo = ISC_LIST_NEXT(addrinfo, publink))
885135446Strhodes			if (UNMARKED(addrinfo))
886135446Strhodes				dns_adb_adjustsrtt(fctx->adb, addrinfo,
887135446Strhodes						   0, factor);
888135446Strhodes
889135446Strhodes	if (finish != NULL && TRIEDFIND(fctx))
890135446Strhodes		for (find = ISC_LIST_HEAD(fctx->finds);
891135446Strhodes		     find != NULL;
892135446Strhodes		     find = ISC_LIST_NEXT(find, publink))
893135446Strhodes			for (addrinfo = ISC_LIST_HEAD(find->list);
894135446Strhodes			     addrinfo != NULL;
895135446Strhodes			     addrinfo = ISC_LIST_NEXT(addrinfo, publink))
896135446Strhodes				if (UNMARKED(addrinfo))
897135446Strhodes					dns_adb_adjustsrtt(fctx->adb, addrinfo,
898135446Strhodes							   0, factor);
899135446Strhodes
900135446Strhodes	if (finish != NULL && TRIEDALT(fctx)) {
901135446Strhodes		for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
902135446Strhodes		     addrinfo != NULL;
903135446Strhodes		     addrinfo = ISC_LIST_NEXT(addrinfo, publink))
904135446Strhodes			if (UNMARKED(addrinfo))
905135446Strhodes				dns_adb_adjustsrtt(fctx->adb, addrinfo,
906135446Strhodes						   0, factor);
907135446Strhodes		for (find = ISC_LIST_HEAD(fctx->altfinds);
908135446Strhodes		     find != NULL;
909135446Strhodes		     find = ISC_LIST_NEXT(find, publink))
910135446Strhodes			for (addrinfo = ISC_LIST_HEAD(find->list);
911135446Strhodes			     addrinfo != NULL;
912135446Strhodes			     addrinfo = ISC_LIST_NEXT(addrinfo, publink))
913135446Strhodes				if (UNMARKED(addrinfo))
914135446Strhodes					dns_adb_adjustsrtt(fctx->adb, addrinfo,
915135446Strhodes							   0, factor);
916135446Strhodes	}
917135446Strhodes
918135446Strhodes	/*
919135446Strhodes	 * Check for any outstanding socket events.  If they exist, cancel
920135446Strhodes	 * them and let the event handlers finish the cleanup.  The resolver
921135446Strhodes	 * only needs to worry about managing the connect and send events;
922135446Strhodes	 * the dispatcher manages the recv events.
923135446Strhodes	 */
924186462Sdougb	if (RESQUERY_CONNECTING(query)) {
925135446Strhodes		/*
926135446Strhodes		 * Cancel the connect.
927135446Strhodes		 */
928186462Sdougb		if (query->tcpsocket != NULL) {
929186462Sdougb			isc_socket_cancel(query->tcpsocket, NULL,
930186462Sdougb					  ISC_SOCKCANCEL_CONNECT);
931186462Sdougb		} else if (query->dispentry != NULL) {
932186462Sdougb			INSIST(query->exclusivesocket);
933186462Sdougb			socket = dns_dispatch_getentrysocket(query->dispentry);
934186462Sdougb			if (socket != NULL)
935186462Sdougb				isc_socket_cancel(socket, NULL,
936186462Sdougb						  ISC_SOCKCANCEL_CONNECT);
937186462Sdougb		}
938186462Sdougb	} else if (RESQUERY_SENDING(query)) {
939135446Strhodes		/*
940135446Strhodes		 * Cancel the pending send.
941135446Strhodes		 */
942186462Sdougb		if (query->exclusivesocket && query->dispentry != NULL)
943186462Sdougb			socket = dns_dispatch_getentrysocket(query->dispentry);
944186462Sdougb		else
945186462Sdougb			socket = dns_dispatch_getsocket(query->dispatch);
946186462Sdougb		if (socket != NULL)
947186462Sdougb			isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND);
948186462Sdougb	}
949135446Strhodes
950186462Sdougb	if (query->dispentry != NULL)
951186462Sdougb		dns_dispatch_removeresponse(&query->dispentry, deventp);
952186462Sdougb
953186462Sdougb	ISC_LIST_UNLINK(fctx->queries, query, link);
954186462Sdougb
955186462Sdougb	if (query->tsig != NULL)
956186462Sdougb		isc_buffer_free(&query->tsig);
957186462Sdougb
958186462Sdougb	if (query->tsigkey != NULL)
959186462Sdougb		dns_tsigkey_detach(&query->tsigkey);
960186462Sdougb
961135446Strhodes	if (query->dispatch != NULL)
962135446Strhodes		dns_dispatch_detach(&query->dispatch);
963135446Strhodes
964135446Strhodes	if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query)))
965135446Strhodes		/*
966135446Strhodes		 * It's safe to destroy the query now.
967135446Strhodes		 */
968135446Strhodes		resquery_destroy(&query);
969135446Strhodes}
970135446Strhodes
971135446Strhodesstatic void
972135446Strhodesfctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) {
973135446Strhodes	resquery_t *query, *next_query;
974135446Strhodes
975135446Strhodes	FCTXTRACE("cancelqueries");
976135446Strhodes
977135446Strhodes	for (query = ISC_LIST_HEAD(fctx->queries);
978135446Strhodes	     query != NULL;
979135446Strhodes	     query = next_query) {
980135446Strhodes		next_query = ISC_LIST_NEXT(query, link);
981135446Strhodes		fctx_cancelquery(&query, NULL, NULL, no_response);
982135446Strhodes	}
983135446Strhodes}
984135446Strhodes
985135446Strhodesstatic void
986135446Strhodesfctx_cleanupfinds(fetchctx_t *fctx) {
987135446Strhodes	dns_adbfind_t *find, *next_find;
988135446Strhodes
989135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
990135446Strhodes
991135446Strhodes	for (find = ISC_LIST_HEAD(fctx->finds);
992135446Strhodes	     find != NULL;
993135446Strhodes	     find = next_find) {
994135446Strhodes		next_find = ISC_LIST_NEXT(find, publink);
995135446Strhodes		ISC_LIST_UNLINK(fctx->finds, find, publink);
996135446Strhodes		dns_adb_destroyfind(&find);
997135446Strhodes	}
998135446Strhodes	fctx->find = NULL;
999135446Strhodes}
1000135446Strhodes
1001135446Strhodesstatic void
1002135446Strhodesfctx_cleanupaltfinds(fetchctx_t *fctx) {
1003135446Strhodes	dns_adbfind_t *find, *next_find;
1004135446Strhodes
1005135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
1006135446Strhodes
1007135446Strhodes	for (find = ISC_LIST_HEAD(fctx->altfinds);
1008135446Strhodes	     find != NULL;
1009135446Strhodes	     find = next_find) {
1010135446Strhodes		next_find = ISC_LIST_NEXT(find, publink);
1011135446Strhodes		ISC_LIST_UNLINK(fctx->altfinds, find, publink);
1012135446Strhodes		dns_adb_destroyfind(&find);
1013135446Strhodes	}
1014135446Strhodes	fctx->altfind = NULL;
1015135446Strhodes}
1016135446Strhodes
1017135446Strhodesstatic void
1018135446Strhodesfctx_cleanupforwaddrs(fetchctx_t *fctx) {
1019135446Strhodes	dns_adbaddrinfo_t *addr, *next_addr;
1020135446Strhodes
1021135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
1022135446Strhodes
1023135446Strhodes	for (addr = ISC_LIST_HEAD(fctx->forwaddrs);
1024135446Strhodes	     addr != NULL;
1025135446Strhodes	     addr = next_addr) {
1026135446Strhodes		next_addr = ISC_LIST_NEXT(addr, publink);
1027135446Strhodes		ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink);
1028135446Strhodes		dns_adb_freeaddrinfo(fctx->adb, &addr);
1029135446Strhodes	}
1030135446Strhodes}
1031135446Strhodes
1032135446Strhodesstatic void
1033135446Strhodesfctx_cleanupaltaddrs(fetchctx_t *fctx) {
1034135446Strhodes	dns_adbaddrinfo_t *addr, *next_addr;
1035135446Strhodes
1036135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
1037135446Strhodes
1038135446Strhodes	for (addr = ISC_LIST_HEAD(fctx->altaddrs);
1039135446Strhodes	     addr != NULL;
1040135446Strhodes	     addr = next_addr) {
1041135446Strhodes		next_addr = ISC_LIST_NEXT(addr, publink);
1042135446Strhodes		ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
1043135446Strhodes		dns_adb_freeaddrinfo(fctx->adb, &addr);
1044135446Strhodes	}
1045135446Strhodes}
1046135446Strhodes
1047135446Strhodesstatic inline void
1048135446Strhodesfctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
1049135446Strhodes	FCTXTRACE("stopeverything");
1050135446Strhodes	fctx_cancelqueries(fctx, no_response);
1051135446Strhodes	fctx_cleanupfinds(fctx);
1052135446Strhodes	fctx_cleanupaltfinds(fctx);
1053135446Strhodes	fctx_cleanupforwaddrs(fctx);
1054135446Strhodes	fctx_cleanupaltaddrs(fctx);
1055135446Strhodes	fctx_stoptimer(fctx);
1056135446Strhodes}
1057135446Strhodes
1058135446Strhodesstatic inline void
1059193149Sdougbfctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) {
1060135446Strhodes	dns_fetchevent_t *event, *next_event;
1061135446Strhodes	isc_task_t *task;
1062170222Sdougb	unsigned int count = 0;
1063170222Sdougb	isc_interval_t i;
1064170222Sdougb	isc_boolean_t logit = ISC_FALSE;
1065193149Sdougb	isc_time_t now;
1066186462Sdougb	unsigned int old_spillat;
1067193149Sdougb	unsigned int new_spillat = 0;	/* initialized to silence
1068193149Sdougb					   compiler warnings */
1069135446Strhodes
1070135446Strhodes	/*
1071135446Strhodes	 * Caller must be holding the appropriate bucket lock.
1072135446Strhodes	 */
1073135446Strhodes	REQUIRE(fctx->state == fetchstate_done);
1074135446Strhodes
1075135446Strhodes	FCTXTRACE("sendevents");
1076135446Strhodes
1077193149Sdougb	/*
1078193149Sdougb	 * Keep some record of fetch result for logging later (if required).
1079193149Sdougb	 */
1080193149Sdougb	fctx->result = result;
1081193149Sdougb	fctx->exitline = line;
1082193149Sdougb	TIME_NOW(&now);
1083193149Sdougb	fctx->duration = isc_time_microdiff(&now, &fctx->start);
1084193149Sdougb
1085135446Strhodes	for (event = ISC_LIST_HEAD(fctx->events);
1086135446Strhodes	     event != NULL;
1087135446Strhodes	     event = next_event) {
1088135446Strhodes		next_event = ISC_LIST_NEXT(event, ev_link);
1089135446Strhodes		ISC_LIST_UNLINK(fctx->events, event, ev_link);
1090135446Strhodes		task = event->ev_sender;
1091135446Strhodes		event->ev_sender = fctx;
1092224092Sdougb		event->vresult = fctx->vresult;
1093135446Strhodes		if (!HAVE_ANSWER(fctx))
1094135446Strhodes			event->result = result;
1095135446Strhodes
1096135446Strhodes		INSIST(result != ISC_R_SUCCESS ||
1097135446Strhodes		       dns_rdataset_isassociated(event->rdataset) ||
1098135446Strhodes		       fctx->type == dns_rdatatype_any ||
1099162079Sdougb		       fctx->type == dns_rdatatype_rrsig ||
1100162079Sdougb		       fctx->type == dns_rdatatype_sig);
1101186462Sdougb
1102174187Sdougb		/*
1103174187Sdougb		 * Negative results must be indicated in event->result.
1104174187Sdougb		 */
1105174187Sdougb		if (dns_rdataset_isassociated(event->rdataset) &&
1106223812Sdougb		    NEGATIVE(event->rdataset)) {
1107174187Sdougb			INSIST(event->result == DNS_R_NCACHENXDOMAIN ||
1108174187Sdougb			       event->result == DNS_R_NCACHENXRRSET);
1109174187Sdougb		}
1110135446Strhodes
1111275672Sdelphij		event->qtotal = fctx->totalqueries;
1112135446Strhodes		isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
1113170222Sdougb		count++;
1114135446Strhodes	}
1115170222Sdougb
1116170222Sdougb	if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 &&
1117170222Sdougb	    fctx->spilled &&
1118170222Sdougb	    (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) {
1119170222Sdougb		LOCK(&fctx->res->lock);
1120174187Sdougb		if (count == fctx->res->spillat && !fctx->res->exiting) {
1121186462Sdougb			old_spillat = fctx->res->spillat;
1122170222Sdougb			fctx->res->spillat += 5;
1123170222Sdougb			if (fctx->res->spillat > fctx->res->spillatmax &&
1124170222Sdougb			    fctx->res->spillatmax != 0)
1125170222Sdougb				fctx->res->spillat = fctx->res->spillatmax;
1126186462Sdougb			new_spillat = fctx->res->spillat;
1127186462Sdougb			if (new_spillat != old_spillat) {
1128186462Sdougb				logit = ISC_TRUE;
1129186462Sdougb			}
1130170222Sdougb			isc_interval_set(&i, 20 * 60, 0);
1131170222Sdougb			result = isc_timer_reset(fctx->res->spillattimer,
1132170222Sdougb						 isc_timertype_ticker, NULL,
1133170222Sdougb						 &i, ISC_TRUE);
1134170222Sdougb			RUNTIME_CHECK(result == ISC_R_SUCCESS);
1135170222Sdougb		}
1136170222Sdougb		UNLOCK(&fctx->res->lock);
1137170222Sdougb		if (logit)
1138170222Sdougb			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
1139170222Sdougb				      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
1140170222Sdougb				      "clients-per-query increased to %u",
1141186462Sdougb				      new_spillat);
1142170222Sdougb	}
1143135446Strhodes}
1144135446Strhodes
1145193149Sdougbstatic inline void
1146193149Sdougblog_edns(fetchctx_t *fctx) {
1147193149Sdougb	char domainbuf[DNS_NAME_FORMATSIZE];
1148193149Sdougb
1149193149Sdougb	if (fctx->reason == NULL)
1150193149Sdougb		return;
1151193149Sdougb
1152262706Serwin	/*
1153262706Serwin	 * We do not know if fctx->domain is the actual domain the record
1154262706Serwin	 * lives in or a parent domain so we have a '?' after it.
1155262706Serwin	 */
1156193149Sdougb	dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
1157193149Sdougb	isc_log_write(dns_lctx, DNS_LOGCATEGORY_EDNS_DISABLED,
1158193149Sdougb		      DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
1159193149Sdougb		      "success resolving '%s' (in '%s'?) after %s",
1160193149Sdougb		      fctx->info, domainbuf, fctx->reason);
1161193149Sdougb
1162193149Sdougb	fctx->reason = NULL;
1163193149Sdougb}
1164193149Sdougb
1165135446Strhodesstatic void
1166193149Sdougbfctx_done(fetchctx_t *fctx, isc_result_t result, int line) {
1167135446Strhodes	dns_resolver_t *res;
1168135446Strhodes	isc_boolean_t no_response;
1169135446Strhodes
1170193149Sdougb	REQUIRE(line >= 0);
1171193149Sdougb
1172135446Strhodes	FCTXTRACE("done");
1173135446Strhodes
1174135446Strhodes	res = fctx->res;
1175135446Strhodes
1176193149Sdougb	if (result == ISC_R_SUCCESS) {
1177193149Sdougb		/*%
1178193149Sdougb		 * Log any deferred EDNS timeout messages.
1179193149Sdougb		 */
1180193149Sdougb		log_edns(fctx);
1181135446Strhodes		no_response = ISC_TRUE;
1182193149Sdougb	 } else
1183135446Strhodes		no_response = ISC_FALSE;
1184193149Sdougb
1185193149Sdougb	fctx->reason = NULL;
1186135446Strhodes	fctx_stopeverything(fctx, no_response);
1187135446Strhodes
1188135446Strhodes	LOCK(&res->buckets[fctx->bucketnum].lock);
1189135446Strhodes
1190135446Strhodes	fctx->state = fetchstate_done;
1191135446Strhodes	fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
1192193149Sdougb	fctx_sendevents(fctx, result, line);
1193135446Strhodes
1194135446Strhodes	UNLOCK(&res->buckets[fctx->bucketnum].lock);
1195135446Strhodes}
1196135446Strhodes
1197135446Strhodesstatic void
1198186462Sdougbprocess_sendevent(resquery_t *query, isc_event_t *event) {
1199135446Strhodes	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
1200143731Sdougb	isc_boolean_t retry = ISC_FALSE;
1201143731Sdougb	isc_result_t result;
1202143731Sdougb	fetchctx_t *fctx;
1203135446Strhodes
1204143731Sdougb	fctx = query->fctx;
1205135446Strhodes
1206135446Strhodes	if (RESQUERY_CANCELED(query)) {
1207186462Sdougb		if (query->sends == 0 && query->connects == 0) {
1208135446Strhodes			/*
1209135446Strhodes			 * This query was canceled while the
1210186462Sdougb			 * isc_socket_sendto/connect() was in progress.
1211135446Strhodes			 */
1212135446Strhodes			if (query->tcpsocket != NULL)
1213135446Strhodes				isc_socket_detach(&query->tcpsocket);
1214135446Strhodes			resquery_destroy(&query);
1215135446Strhodes		}
1216186462Sdougb	} else {
1217143731Sdougb		switch (sevent->result) {
1218143731Sdougb		case ISC_R_SUCCESS:
1219143731Sdougb			break;
1220135446Strhodes
1221143731Sdougb		case ISC_R_HOSTUNREACH:
1222143731Sdougb		case ISC_R_NETUNREACH:
1223143731Sdougb		case ISC_R_NOPERM:
1224143731Sdougb		case ISC_R_ADDRNOTAVAIL:
1225143731Sdougb		case ISC_R_CONNREFUSED:
1226143731Sdougb
1227143731Sdougb			/*
1228143731Sdougb			 * No route to remote.
1229143731Sdougb			 */
1230193149Sdougb			add_bad(fctx, query->addrinfo, sevent->result,
1231193149Sdougb				badns_unreachable);
1232143731Sdougb			fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
1233143731Sdougb			retry = ISC_TRUE;
1234143731Sdougb			break;
1235143731Sdougb
1236143731Sdougb		default:
1237143731Sdougb			fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
1238143731Sdougb			break;
1239143731Sdougb		}
1240186462Sdougb	}
1241143731Sdougb
1242254897Serwin	if (event->ev_type == ISC_SOCKEVENT_CONNECT)
1243254897Serwin		isc_event_free(&event);
1244143731Sdougb
1245143731Sdougb	if (retry) {
1246143731Sdougb		/*
1247143731Sdougb		 * Behave as if the idle timer has expired.  For TCP
1248143731Sdougb		 * this may not actually reflect the latest timer.
1249143731Sdougb		 */
1250143731Sdougb		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
1251143731Sdougb		result = fctx_stopidletimer(fctx);
1252143731Sdougb		if (result != ISC_R_SUCCESS)
1253193149Sdougb			fctx_done(fctx, result, __LINE__);
1254143731Sdougb		else
1255205292Sdougb			fctx_try(fctx, ISC_TRUE, ISC_FALSE);
1256143731Sdougb	}
1257135446Strhodes}
1258135446Strhodes
1259186462Sdougbstatic void
1260186462Sdougbresquery_udpconnected(isc_task_t *task, isc_event_t *event) {
1261186462Sdougb	resquery_t *query = event->ev_arg;
1262186462Sdougb
1263186462Sdougb	REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
1264186462Sdougb
1265186462Sdougb	QTRACE("udpconnected");
1266186462Sdougb
1267186462Sdougb	UNUSED(task);
1268186462Sdougb
1269186462Sdougb	INSIST(RESQUERY_CONNECTING(query));
1270186462Sdougb
1271186462Sdougb	query->connects--;
1272186462Sdougb
1273186462Sdougb	process_sendevent(query, event);
1274186462Sdougb}
1275186462Sdougb
1276186462Sdougbstatic void
1277186462Sdougbresquery_senddone(isc_task_t *task, isc_event_t *event) {
1278186462Sdougb	resquery_t *query = event->ev_arg;
1279186462Sdougb
1280186462Sdougb	REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE);
1281186462Sdougb
1282186462Sdougb	QTRACE("senddone");
1283186462Sdougb
1284186462Sdougb	/*
1285186462Sdougb	 * XXXRTH
1286186462Sdougb	 *
1287186462Sdougb	 * Currently we don't wait for the senddone event before retrying
1288186462Sdougb	 * a query.  This means that if we get really behind, we may end
1289186462Sdougb	 * up doing extra work!
1290186462Sdougb	 */
1291186462Sdougb
1292186462Sdougb	UNUSED(task);
1293186462Sdougb
1294186462Sdougb	INSIST(RESQUERY_SENDING(query));
1295186462Sdougb
1296186462Sdougb	query->sends--;
1297186462Sdougb
1298186462Sdougb	process_sendevent(query, event);
1299186462Sdougb}
1300186462Sdougb
1301135446Strhodesstatic inline isc_result_t
1302193149Sdougbfctx_addopt(dns_message_t *message, unsigned int version,
1303254402Serwin	    isc_uint16_t udpsize, dns_ednsopt_t *ednsopts, size_t count)
1304186462Sdougb{
1305254402Serwin	dns_rdataset_t *rdataset = NULL;
1306135446Strhodes	isc_result_t result;
1307135446Strhodes
1308254402Serwin	result = dns_message_buildopt(message, &rdataset, version, udpsize,
1309254402Serwin				      DNS_MESSAGEEXTFLAG_DO, ednsopts, count);
1310135446Strhodes	if (result != ISC_R_SUCCESS)
1311135446Strhodes		return (result);
1312135446Strhodes	return (dns_message_setopt(message, rdataset));
1313135446Strhodes}
1314135446Strhodes
1315135446Strhodesstatic inline void
1316135446Strhodesfctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
1317135446Strhodes	unsigned int seconds;
1318170222Sdougb	unsigned int us;
1319135446Strhodes
1320135446Strhodes	/*
1321193149Sdougb	 * We retry every .8 seconds the first two times through the address
1322135446Strhodes	 * list, and then we do exponential back-off.
1323135446Strhodes	 */
1324135446Strhodes	if (fctx->restarts < 3)
1325186462Sdougb		us = 800000;
1326135446Strhodes	else
1327186462Sdougb		us = (800000 << (fctx->restarts - 2));
1328135446Strhodes
1329135446Strhodes	/*
1330245163Serwin	 * Add a fudge factor to the expected rtt based on the current
1331245163Serwin	 * estimate.
1332135446Strhodes	 */
1333245163Serwin	if (rtt < 50000)
1334245163Serwin		rtt += 50000;
1335245163Serwin	else if (rtt < 100000)
1336245163Serwin		rtt += 100000;
1337245163Serwin	else
1338245163Serwin		rtt += 200000;
1339135446Strhodes
1340135446Strhodes	/*
1341245163Serwin	 * Always wait for at least the expected rtt.
1342135446Strhodes	 */
1343170222Sdougb	if (us < rtt)
1344170222Sdougb		us = rtt;
1345135446Strhodes
1346135446Strhodes	/*
1347170222Sdougb	 * But don't ever wait for more than 10 seconds.
1348135446Strhodes	 */
1349245163Serwin	if (us > MAX_SINGLE_QUERY_TIMEOUT_US)
1350245163Serwin		us = MAX_SINGLE_QUERY_TIMEOUT_US;
1351135446Strhodes
1352245163Serwin	seconds = us / US_PER_SEC;
1353245163Serwin	us -= seconds * US_PER_SEC;
1354170222Sdougb	isc_interval_set(&fctx->interval, seconds, us * 1000);
1355135446Strhodes}
1356135446Strhodes
1357135446Strhodesstatic isc_result_t
1358135446Strhodesfctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
1359135446Strhodes	   unsigned int options)
1360135446Strhodes{
1361135446Strhodes	dns_resolver_t *res;
1362135446Strhodes	isc_task_t *task;
1363135446Strhodes	isc_result_t result;
1364135446Strhodes	resquery_t *query;
1365170222Sdougb	isc_sockaddr_t addr;
1366170222Sdougb	isc_boolean_t have_addr = ISC_FALSE;
1367193149Sdougb	unsigned int srtt;
1368135446Strhodes
1369135446Strhodes	FCTXTRACE("query");
1370135446Strhodes
1371135446Strhodes	res = fctx->res;
1372135446Strhodes	task = res->buckets[fctx->bucketnum].task;
1373135446Strhodes
1374193149Sdougb	srtt = addrinfo->srtt;
1375245163Serwin
1376245163Serwin	/*
1377245163Serwin	 * A forwarder needs to make multiple queries. Give it at least
1378245163Serwin	 * a second to do these in.
1379245163Serwin	 */
1380193149Sdougb	if (ISFORWARDER(addrinfo) && srtt < 1000000)
1381193149Sdougb		srtt = 1000000;
1382193149Sdougb
1383193149Sdougb	fctx_setretryinterval(fctx, srtt);
1384193149Sdougb	result = fctx_startidletimer(fctx, &fctx->interval);
1385135446Strhodes	if (result != ISC_R_SUCCESS)
1386135446Strhodes		return (result);
1387135446Strhodes
1388166332Sdougb	INSIST(ISC_LIST_EMPTY(fctx->validators));
1389166332Sdougb
1390135446Strhodes	dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
1391135446Strhodes
1392236374Sdougb	query = isc_mem_get(fctx->mctx, sizeof(*query));
1393135446Strhodes	if (query == NULL) {
1394135446Strhodes		result = ISC_R_NOMEMORY;
1395135446Strhodes		goto stop_idle_timer;
1396135446Strhodes	}
1397236374Sdougb	query->mctx = fctx->mctx;
1398135446Strhodes	query->options = options;
1399135446Strhodes	query->attributes = 0;
1400135446Strhodes	query->sends = 0;
1401135446Strhodes	query->connects = 0;
1402135446Strhodes	/*
1403135446Strhodes	 * Note that the caller MUST guarantee that 'addrinfo' will remain
1404135446Strhodes	 * valid until this query is canceled.
1405135446Strhodes	 */
1406135446Strhodes	query->addrinfo = addrinfo;
1407135446Strhodes	TIME_NOW(&query->start);
1408135446Strhodes
1409135446Strhodes	/*
1410135446Strhodes	 * If this is a TCP query, then we need to make a socket and
1411135446Strhodes	 * a dispatch for it here.  Otherwise we use the resolver's
1412135446Strhodes	 * shared dispatch.
1413135446Strhodes	 */
1414135446Strhodes	query->dispatchmgr = res->dispatchmgr;
1415135446Strhodes	query->dispatch = NULL;
1416186462Sdougb	query->exclusivesocket = ISC_FALSE;
1417135446Strhodes	query->tcpsocket = NULL;
1418170222Sdougb	if (res->view->peers != NULL) {
1419170222Sdougb		dns_peer_t *peer = NULL;
1420170222Sdougb		isc_netaddr_t dstip;
1421170222Sdougb		isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr);
1422170222Sdougb		result = dns_peerlist_peerbyaddr(res->view->peers,
1423174187Sdougb						 &dstip, &peer);
1424170222Sdougb		if (result == ISC_R_SUCCESS) {
1425170222Sdougb			result = dns_peer_getquerysource(peer, &addr);
1426170222Sdougb			if (result == ISC_R_SUCCESS)
1427170222Sdougb				have_addr = ISC_TRUE;
1428170222Sdougb		}
1429170222Sdougb	}
1430170222Sdougb
1431135446Strhodes	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
1432135446Strhodes		int pf;
1433135446Strhodes
1434135446Strhodes		pf = isc_sockaddr_pf(&addrinfo->sockaddr);
1435170222Sdougb		if (!have_addr) {
1436170222Sdougb			switch (pf) {
1437170222Sdougb			case PF_INET:
1438254897Serwin				result = dns_dispatch_getlocaladdress(
1439254897Serwin					      res->dispatches4->dispatches[0],
1440254897Serwin					      &addr);
1441170222Sdougb				break;
1442170222Sdougb			case PF_INET6:
1443254897Serwin				result = dns_dispatch_getlocaladdress(
1444254897Serwin					      res->dispatches6->dispatches[0],
1445254897Serwin					      &addr);
1446170222Sdougb				break;
1447170222Sdougb			default:
1448170222Sdougb				result = ISC_R_NOTIMPLEMENTED;
1449170222Sdougb				break;
1450170222Sdougb			}
1451170222Sdougb			if (result != ISC_R_SUCCESS)
1452170222Sdougb				goto cleanup_query;
1453135446Strhodes		}
1454135446Strhodes		isc_sockaddr_setport(&addr, 0);
1455135446Strhodes
1456135446Strhodes		result = isc_socket_create(res->socketmgr, pf,
1457135446Strhodes					   isc_sockettype_tcp,
1458135446Strhodes					   &query->tcpsocket);
1459135446Strhodes		if (result != ISC_R_SUCCESS)
1460135446Strhodes			goto cleanup_query;
1461135446Strhodes
1462165071Sdougb#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
1463182645Sdougb		result = isc_socket_bind(query->tcpsocket, &addr, 0);
1464135446Strhodes		if (result != ISC_R_SUCCESS)
1465135446Strhodes			goto cleanup_socket;
1466165071Sdougb#endif
1467135446Strhodes
1468135446Strhodes		/*
1469135446Strhodes		 * A dispatch will be created once the connect succeeds.
1470135446Strhodes		 */
1471135446Strhodes	} else {
1472170222Sdougb		if (have_addr) {
1473170222Sdougb			unsigned int attrs, attrmask;
1474170222Sdougb			attrs = DNS_DISPATCHATTR_UDP;
1475170222Sdougb			switch (isc_sockaddr_pf(&addr)) {
1476170222Sdougb			case AF_INET:
1477170222Sdougb				attrs |= DNS_DISPATCHATTR_IPV4;
1478170222Sdougb				break;
1479170222Sdougb			case AF_INET6:
1480170222Sdougb				attrs |= DNS_DISPATCHATTR_IPV6;
1481170222Sdougb				break;
1482170222Sdougb			default:
1483170222Sdougb				result = ISC_R_NOTIMPLEMENTED;
1484170222Sdougb				goto cleanup_query;
1485170222Sdougb			}
1486170222Sdougb			attrmask = DNS_DISPATCHATTR_UDP;
1487170222Sdougb			attrmask |= DNS_DISPATCHATTR_TCP;
1488170222Sdougb			attrmask |= DNS_DISPATCHATTR_IPV4;
1489170222Sdougb			attrmask |= DNS_DISPATCHATTR_IPV6;
1490170222Sdougb			result = dns_dispatch_getudp(res->dispatchmgr,
1491170222Sdougb						     res->socketmgr,
1492170222Sdougb						     res->taskmgr, &addr,
1493170222Sdougb						     4096, 1000, 32768, 16411,
1494170222Sdougb						     16433, attrs, attrmask,
1495170222Sdougb						     &query->dispatch);
1496170222Sdougb			if (result != ISC_R_SUCCESS)
1497170222Sdougb				goto cleanup_query;
1498170222Sdougb		} else {
1499170222Sdougb			switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
1500186462Sdougb			case PF_INET:
1501254897Serwin				dns_dispatch_attach(
1502254897Serwin				    dns_resolver_dispatchv4(res),
1503254897Serwin				    &query->dispatch);
1504186462Sdougb				query->exclusivesocket = res->exclusivev4;
1505170222Sdougb				break;
1506186462Sdougb			case PF_INET6:
1507254897Serwin				dns_dispatch_attach(
1508254897Serwin				    dns_resolver_dispatchv6(res),
1509254897Serwin				    &query->dispatch);
1510186462Sdougb				query->exclusivesocket = res->exclusivev6;
1511170222Sdougb				break;
1512170222Sdougb			default:
1513170222Sdougb				result = ISC_R_NOTIMPLEMENTED;
1514170222Sdougb				goto cleanup_query;
1515170222Sdougb			}
1516135446Strhodes		}
1517135446Strhodes		/*
1518135446Strhodes		 * We should always have a valid dispatcher here.  If we
1519135446Strhodes		 * don't support a protocol family, then its dispatcher
1520135446Strhodes		 * will be NULL, but we shouldn't be finding addresses for
1521135446Strhodes		 * protocol types we don't support, so the dispatcher
1522135446Strhodes		 * we found should never be NULL.
1523135446Strhodes		 */
1524135446Strhodes		INSIST(query->dispatch != NULL);
1525135446Strhodes	}
1526135446Strhodes
1527135446Strhodes	query->dispentry = NULL;
1528135446Strhodes	query->fctx = fctx;
1529135446Strhodes	query->tsig = NULL;
1530135446Strhodes	query->tsigkey = NULL;
1531135446Strhodes	ISC_LINK_INIT(query, link);
1532135446Strhodes	query->magic = QUERY_MAGIC;
1533135446Strhodes
1534135446Strhodes	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
1535135446Strhodes		/*
1536135446Strhodes		 * Connect to the remote server.
1537135446Strhodes		 *
1538135446Strhodes		 * XXXRTH  Should we attach to the socket?
1539135446Strhodes		 */
1540135446Strhodes		result = isc_socket_connect(query->tcpsocket,
1541135446Strhodes					    &addrinfo->sockaddr, task,
1542135446Strhodes					    resquery_connected, query);
1543135446Strhodes		if (result != ISC_R_SUCCESS)
1544135446Strhodes			goto cleanup_socket;
1545135446Strhodes		query->connects++;
1546135446Strhodes		QTRACE("connecting via TCP");
1547135446Strhodes	} else {
1548135446Strhodes		result = resquery_send(query);
1549135446Strhodes		if (result != ISC_R_SUCCESS)
1550135446Strhodes			goto cleanup_dispatch;
1551135446Strhodes	}
1552275672Sdelphij
1553193149Sdougb	fctx->querysent++;
1554275672Sdelphij	fctx->totalqueries++;
1555135446Strhodes
1556135446Strhodes	ISC_LIST_APPEND(fctx->queries, query, link);
1557166332Sdougb	query->fctx->nqueries++;
1558193149Sdougb	if (isc_sockaddr_pf(&addrinfo->sockaddr) == PF_INET)
1559193149Sdougb		inc_stats(res, dns_resstatscounter_queryv4);
1560193149Sdougb	else
1561193149Sdougb		inc_stats(res, dns_resstatscounter_queryv6);
1562193149Sdougb	if (res->view->resquerystats != NULL)
1563193149Sdougb		dns_rdatatypestats_increment(res->view->resquerystats,
1564193149Sdougb					     fctx->type);
1565135446Strhodes
1566135446Strhodes	return (ISC_R_SUCCESS);
1567135446Strhodes
1568135446Strhodes cleanup_socket:
1569135446Strhodes	isc_socket_detach(&query->tcpsocket);
1570135446Strhodes
1571135446Strhodes cleanup_dispatch:
1572135446Strhodes	if (query->dispatch != NULL)
1573135446Strhodes		dns_dispatch_detach(&query->dispatch);
1574135446Strhodes
1575135446Strhodes cleanup_query:
1576234010Sdougb	if (query->connects == 0) {
1577234010Sdougb		query->magic = 0;
1578236374Sdougb		isc_mem_put(fctx->mctx, query, sizeof(*query));
1579234010Sdougb	}
1580135446Strhodes
1581135446Strhodes stop_idle_timer:
1582135446Strhodes	RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS);
1583135446Strhodes
1584135446Strhodes	return (result);
1585135446Strhodes}
1586135446Strhodes
1587170222Sdougbstatic isc_boolean_t
1588218384Sdougbbad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
1589218384Sdougb	isc_sockaddr_t *sa;
1590218384Sdougb
1591218384Sdougb	for (sa = ISC_LIST_HEAD(fctx->bad_edns);
1592218384Sdougb	     sa != NULL;
1593218384Sdougb	     sa = ISC_LIST_NEXT(sa, link)) {
1594218384Sdougb		if (isc_sockaddr_equal(sa, address))
1595218384Sdougb			return (ISC_TRUE);
1596218384Sdougb	}
1597218384Sdougb
1598218384Sdougb	return (ISC_FALSE);
1599218384Sdougb}
1600218384Sdougb
1601218384Sdougbstatic void
1602218384Sdougbadd_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
1603218384Sdougb	isc_sockaddr_t *sa;
1604218384Sdougb
1605218384Sdougb	if (bad_edns(fctx, address))
1606218384Sdougb		return;
1607218384Sdougb
1608236374Sdougb	sa = isc_mem_get(fctx->mctx, sizeof(*sa));
1609218384Sdougb	if (sa == NULL)
1610218384Sdougb		return;
1611218384Sdougb
1612218384Sdougb	*sa = *address;
1613218384Sdougb	ISC_LIST_INITANDAPPEND(fctx->bad_edns, sa, link);
1614218384Sdougb}
1615218384Sdougb
1616218384Sdougbstatic isc_boolean_t
1617170222Sdougbtriededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
1618170222Sdougb	isc_sockaddr_t *sa;
1619170222Sdougb
1620170222Sdougb	for (sa = ISC_LIST_HEAD(fctx->edns);
1621170222Sdougb	     sa != NULL;
1622170222Sdougb	     sa = ISC_LIST_NEXT(sa, link)) {
1623170222Sdougb		if (isc_sockaddr_equal(sa, address))
1624170222Sdougb			return (ISC_TRUE);
1625170222Sdougb	}
1626170222Sdougb
1627170222Sdougb	return (ISC_FALSE);
1628170222Sdougb}
1629170222Sdougb
1630170222Sdougbstatic void
1631170222Sdougbadd_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
1632170222Sdougb	isc_sockaddr_t *sa;
1633170222Sdougb
1634170222Sdougb	if (triededns(fctx, address))
1635170222Sdougb		return;
1636170222Sdougb
1637236374Sdougb	sa = isc_mem_get(fctx->mctx, sizeof(*sa));
1638170222Sdougb	if (sa == NULL)
1639170222Sdougb		return;
1640170222Sdougb
1641170222Sdougb	*sa = *address;
1642170222Sdougb	ISC_LIST_INITANDAPPEND(fctx->edns, sa, link);
1643170222Sdougb}
1644170222Sdougb
1645170222Sdougbstatic isc_boolean_t
1646170222Sdougbtriededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
1647170222Sdougb	isc_sockaddr_t *sa;
1648170222Sdougb
1649170222Sdougb	for (sa = ISC_LIST_HEAD(fctx->edns512);
1650170222Sdougb	     sa != NULL;
1651170222Sdougb	     sa = ISC_LIST_NEXT(sa, link)) {
1652170222Sdougb		if (isc_sockaddr_equal(sa, address))
1653170222Sdougb			return (ISC_TRUE);
1654170222Sdougb	}
1655170222Sdougb
1656170222Sdougb	return (ISC_FALSE);
1657170222Sdougb}
1658170222Sdougb
1659170222Sdougbstatic void
1660170222Sdougbadd_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
1661170222Sdougb	isc_sockaddr_t *sa;
1662170222Sdougb
1663170222Sdougb	if (triededns512(fctx, address))
1664170222Sdougb		return;
1665170222Sdougb
1666236374Sdougb	sa = isc_mem_get(fctx->mctx, sizeof(*sa));
1667170222Sdougb	if (sa == NULL)
1668170222Sdougb		return;
1669170222Sdougb
1670170222Sdougb	*sa = *address;
1671170222Sdougb	ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link);
1672170222Sdougb}
1673170222Sdougb
1674135446Strhodesstatic isc_result_t
1675135446Strhodesresquery_send(resquery_t *query) {
1676135446Strhodes	fetchctx_t *fctx;
1677135446Strhodes	isc_result_t result;
1678135446Strhodes	dns_name_t *qname = NULL;
1679135446Strhodes	dns_rdataset_t *qrdataset = NULL;
1680135446Strhodes	isc_region_t r;
1681135446Strhodes	dns_resolver_t *res;
1682135446Strhodes	isc_task_t *task;
1683135446Strhodes	isc_socket_t *socket;
1684135446Strhodes	isc_buffer_t tcpbuffer;
1685135446Strhodes	isc_sockaddr_t *address;
1686135446Strhodes	isc_buffer_t *buffer;
1687135446Strhodes	isc_netaddr_t ipaddr;
1688135446Strhodes	dns_tsigkey_t *tsigkey = NULL;
1689135446Strhodes	dns_peer_t *peer = NULL;
1690135446Strhodes	isc_boolean_t useedns;
1691135446Strhodes	dns_compress_t cctx;
1692135446Strhodes	isc_boolean_t cleanup_cctx = ISC_FALSE;
1693135446Strhodes	isc_boolean_t secure_domain;
1694234010Sdougb	isc_boolean_t connecting = ISC_FALSE;
1695254402Serwin	dns_ednsopt_t ednsopts[EDNSOPTS];
1696254402Serwin	unsigned ednsopt = 0;
1697135446Strhodes
1698135446Strhodes	fctx = query->fctx;
1699135446Strhodes	QTRACE("send");
1700135446Strhodes
1701135446Strhodes	res = fctx->res;
1702135446Strhodes	task = res->buckets[fctx->bucketnum].task;
1703135446Strhodes	address = NULL;
1704135446Strhodes
1705135446Strhodes	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
1706135446Strhodes		/*
1707135446Strhodes		 * Reserve space for the TCP message length.
1708135446Strhodes		 */
1709135446Strhodes		isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data));
1710135446Strhodes		isc_buffer_init(&query->buffer, query->data + 2,
1711135446Strhodes				sizeof(query->data) - 2);
1712135446Strhodes		buffer = &tcpbuffer;
1713135446Strhodes	} else {
1714135446Strhodes		isc_buffer_init(&query->buffer, query->data,
1715135446Strhodes				sizeof(query->data));
1716135446Strhodes		buffer = &query->buffer;
1717135446Strhodes	}
1718135446Strhodes
1719135446Strhodes	result = dns_message_gettempname(fctx->qmessage, &qname);
1720135446Strhodes	if (result != ISC_R_SUCCESS)
1721135446Strhodes		goto cleanup_temps;
1722135446Strhodes	result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset);
1723135446Strhodes	if (result != ISC_R_SUCCESS)
1724135446Strhodes		goto cleanup_temps;
1725135446Strhodes
1726135446Strhodes	/*
1727135446Strhodes	 * Get a query id from the dispatch.
1728135446Strhodes	 */
1729186462Sdougb	result = dns_dispatch_addresponse2(query->dispatch,
1730186462Sdougb					   &query->addrinfo->sockaddr,
1731186462Sdougb					   task,
1732186462Sdougb					   resquery_response,
1733186462Sdougb					   query,
1734186462Sdougb					   &query->id,
1735186462Sdougb					   &query->dispentry,
1736186462Sdougb					   res->socketmgr);
1737135446Strhodes	if (result != ISC_R_SUCCESS)
1738135446Strhodes		goto cleanup_temps;
1739135446Strhodes
1740135446Strhodes	fctx->qmessage->opcode = dns_opcode_query;
1741135446Strhodes
1742135446Strhodes	/*
1743135446Strhodes	 * Set up question.
1744135446Strhodes	 */
1745135446Strhodes	dns_name_init(qname, NULL);
1746135446Strhodes	dns_name_clone(&fctx->name, qname);
1747135446Strhodes	dns_rdataset_init(qrdataset);
1748135446Strhodes	dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type);
1749135446Strhodes	ISC_LIST_APPEND(qname->list, qrdataset, link);
1750135446Strhodes	dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION);
1751135446Strhodes	qname = NULL;
1752135446Strhodes	qrdataset = NULL;
1753135446Strhodes
1754135446Strhodes	/*
1755135446Strhodes	 * Set RD if the client has requested that we do a recursive query,
1756135446Strhodes	 * or if we're sending to a forwarder.
1757135446Strhodes	 */
1758135446Strhodes	if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 ||
1759135446Strhodes	    ISFORWARDER(query->addrinfo))
1760135446Strhodes		fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
1761135446Strhodes
1762135446Strhodes	/*
1763135446Strhodes	 * Set CD if the client says don't validate or the question is
1764135446Strhodes	 * under a secure entry point.
1765135446Strhodes	 */
1766170222Sdougb	if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
1767170222Sdougb		fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
1768170222Sdougb	} else if (res->view->enablevalidation) {
1769224092Sdougb		result = dns_view_issecuredomain(res->view, &fctx->name,
1770224092Sdougb						 &secure_domain);
1771135446Strhodes		if (result != ISC_R_SUCCESS)
1772135446Strhodes			secure_domain = ISC_FALSE;
1773135446Strhodes		if (res->view->dlv != NULL)
1774135446Strhodes			secure_domain = ISC_TRUE;
1775135446Strhodes		if (secure_domain)
1776135446Strhodes			fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
1777170222Sdougb	}
1778135446Strhodes
1779135446Strhodes	/*
1780135446Strhodes	 * We don't have to set opcode because it defaults to query.
1781135446Strhodes	 */
1782135446Strhodes	fctx->qmessage->id = query->id;
1783135446Strhodes
1784135446Strhodes	/*
1785135446Strhodes	 * Convert the question to wire format.
1786135446Strhodes	 */
1787135446Strhodes	result = dns_compress_init(&cctx, -1, fctx->res->mctx);
1788135446Strhodes	if (result != ISC_R_SUCCESS)
1789135446Strhodes		goto cleanup_message;
1790135446Strhodes	cleanup_cctx = ISC_TRUE;
1791135446Strhodes
1792135446Strhodes	result = dns_message_renderbegin(fctx->qmessage, &cctx,
1793135446Strhodes					 &query->buffer);
1794135446Strhodes	if (result != ISC_R_SUCCESS)
1795135446Strhodes		goto cleanup_message;
1796135446Strhodes
1797135446Strhodes	result = dns_message_rendersection(fctx->qmessage,
1798135446Strhodes					   DNS_SECTION_QUESTION, 0);
1799135446Strhodes	if (result != ISC_R_SUCCESS)
1800135446Strhodes		goto cleanup_message;
1801135446Strhodes
1802135446Strhodes	peer = NULL;
1803135446Strhodes	isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr);
1804135446Strhodes	(void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer);
1805135446Strhodes
1806135446Strhodes	/*
1807135446Strhodes	 * The ADB does not know about servers with "edns no".  Check this,
1808135446Strhodes	 * and then inform the ADB for future use.
1809135446Strhodes	 */
1810135446Strhodes	if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 &&
1811135446Strhodes	    peer != NULL &&
1812135446Strhodes	    dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS &&
1813135446Strhodes	    !useedns)
1814135446Strhodes	{
1815135446Strhodes		query->options |= DNS_FETCHOPT_NOEDNS0;
1816193149Sdougb		dns_adb_changeflags(fctx->adb, query->addrinfo,
1817135446Strhodes				    DNS_FETCHOPT_NOEDNS0,
1818135446Strhodes				    DNS_FETCHOPT_NOEDNS0);
1819135446Strhodes	}
1820135446Strhodes
1821193149Sdougb	/* Sync NOEDNS0 flag in addrinfo->flags and options now. */
1822193149Sdougb	if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0)
1823193149Sdougb		query->options |= DNS_FETCHOPT_NOEDNS0;
1824193149Sdougb
1825135446Strhodes	/*
1826193149Sdougb	 * Handle timeouts by reducing the UDP response size to 512 bytes
1827193149Sdougb	 * then if that doesn't work disabling EDNS (includes DO) and CD.
1828193149Sdougb	 *
1829193149Sdougb	 * These timeout can be due to:
1830193149Sdougb	 *	* broken nameservers that don't respond to EDNS queries.
1831193149Sdougb	 *	* broken/misconfigured firewalls and NAT implementations
1832193149Sdougb	 *	  that don't handle IP fragmentation.
1833193149Sdougb	 *	* broken/misconfigured firewalls that don't handle responses
1834193149Sdougb	 *	  greater than 512 bytes.
1835193149Sdougb	 *	* broken/misconfigured firewalls that don't handle EDNS, DO
1836193149Sdougb	 *	  or CD.
1837193149Sdougb	 *	* packet loss / link outage.
1838193149Sdougb	 */
1839193149Sdougb	if (fctx->timeout) {
1840193149Sdougb		if ((triededns512(fctx, &query->addrinfo->sockaddr) ||
1841193149Sdougb		     fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) &&
1842193149Sdougb		    (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
1843193149Sdougb			query->options |= DNS_FETCHOPT_NOEDNS0;
1844193149Sdougb			fctx->reason = "disabling EDNS";
1845193149Sdougb		} else if ((triededns(fctx, &query->addrinfo->sockaddr) ||
1846193149Sdougb			    fctx->timeouts >= MAX_EDNS0_TIMEOUTS) &&
1847193149Sdougb			   (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
1848193149Sdougb			query->options |= DNS_FETCHOPT_EDNS512;
1849193149Sdougb			fctx->reason = "reducing the advertised EDNS UDP "
1850193149Sdougb				       "packet size to 512 octets";
1851193149Sdougb		}
1852193149Sdougb		fctx->timeout = ISC_FALSE;
1853193149Sdougb	}
1854193149Sdougb
1855193149Sdougb	/*
1856135446Strhodes	 * Use EDNS0, unless the caller doesn't want it, or we know that
1857135446Strhodes	 * the remote server doesn't like it.
1858135446Strhodes	 */
1859135446Strhodes	if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
1860135446Strhodes		if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
1861193149Sdougb			unsigned int version = 0;       /* Default version. */
1862170222Sdougb			unsigned int flags;
1863170222Sdougb			isc_uint16_t udpsize = res->udpsize;
1864193149Sdougb			isc_boolean_t reqnsid = res->view->requestnsid;
1865170222Sdougb
1866170222Sdougb			flags = query->addrinfo->flags;
1867170222Sdougb			if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) {
1868170222Sdougb				version = flags & DNS_FETCHOPT_EDNSVERSIONMASK;
1869170222Sdougb				version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT;
1870170222Sdougb			}
1871170222Sdougb			if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
1872170222Sdougb				udpsize = 512;
1873170222Sdougb			else if (peer != NULL)
1874170222Sdougb				(void)dns_peer_getudpsize(peer, &udpsize);
1875193149Sdougb
1876193149Sdougb			/* request NSID for current view or peer? */
1877193149Sdougb			if (peer != NULL)
1878193149Sdougb				(void) dns_peer_getrequestnsid(peer, &reqnsid);
1879254402Serwin			if (reqnsid) {
1880254402Serwin				INSIST(ednsopt < EDNSOPTS);
1881254402Serwin				ednsopts[ednsopt].code = DNS_OPT_NSID;
1882254402Serwin				ednsopts[ednsopt].length = 0;
1883254402Serwin				ednsopts[ednsopt].value = NULL;
1884254402Serwin				ednsopt++;
1885254402Serwin			}
1886193149Sdougb			result = fctx_addopt(fctx->qmessage, version,
1887254402Serwin					     udpsize, ednsopts, ednsopt);
1888193149Sdougb			if (reqnsid && result == ISC_R_SUCCESS) {
1889193149Sdougb				query->options |= DNS_FETCHOPT_WANTNSID;
1890193149Sdougb			} else if (result != ISC_R_SUCCESS) {
1891135446Strhodes				/*
1892135446Strhodes				 * We couldn't add the OPT, but we'll press on.
1893135446Strhodes				 * We're not using EDNS0, so set the NOEDNS0
1894135446Strhodes				 * bit.
1895135446Strhodes				 */
1896135446Strhodes				query->options |= DNS_FETCHOPT_NOEDNS0;
1897135446Strhodes			}
1898135446Strhodes		} else {
1899135446Strhodes			/*
1900135446Strhodes			 * We know this server doesn't like EDNS0, so we
1901135446Strhodes			 * won't use it.  Set the NOEDNS0 bit since we're
1902135446Strhodes			 * not using EDNS0.
1903135446Strhodes			 */
1904135446Strhodes			query->options |= DNS_FETCHOPT_NOEDNS0;
1905135446Strhodes		}
1906135446Strhodes	}
1907135446Strhodes
1908135446Strhodes	/*
1909135446Strhodes	 * If we need EDNS0 to do this query and aren't using it, we lose.
1910135446Strhodes	 */
1911135446Strhodes	if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) {
1912135446Strhodes		result = DNS_R_SERVFAIL;
1913135446Strhodes		goto cleanup_message;
1914135446Strhodes	}
1915135446Strhodes
1916170222Sdougb	if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
1917170222Sdougb		add_triededns(fctx, &query->addrinfo->sockaddr);
1918170222Sdougb
1919170222Sdougb	if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
1920170222Sdougb		add_triededns512(fctx, &query->addrinfo->sockaddr);
1921170222Sdougb
1922135446Strhodes	/*
1923165071Sdougb	 * Clear CD if EDNS is not in use.
1924165071Sdougb	 */
1925165071Sdougb	if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0)
1926165071Sdougb		fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD;
1927165071Sdougb
1928165071Sdougb	/*
1929135446Strhodes	 * Add TSIG record tailored to the current recipient.
1930135446Strhodes	 */
1931135446Strhodes	result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey);
1932135446Strhodes	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
1933135446Strhodes		goto cleanup_message;
1934135446Strhodes
1935135446Strhodes	if (tsigkey != NULL) {
1936135446Strhodes		result = dns_message_settsigkey(fctx->qmessage, tsigkey);
1937135446Strhodes		dns_tsigkey_detach(&tsigkey);
1938135446Strhodes		if (result != ISC_R_SUCCESS)
1939135446Strhodes			goto cleanup_message;
1940135446Strhodes	}
1941135446Strhodes
1942135446Strhodes	result = dns_message_rendersection(fctx->qmessage,
1943135446Strhodes					   DNS_SECTION_ADDITIONAL, 0);
1944135446Strhodes	if (result != ISC_R_SUCCESS)
1945135446Strhodes		goto cleanup_message;
1946135446Strhodes
1947135446Strhodes	result = dns_message_renderend(fctx->qmessage);
1948135446Strhodes	if (result != ISC_R_SUCCESS)
1949135446Strhodes		goto cleanup_message;
1950135446Strhodes
1951135446Strhodes	dns_compress_invalidate(&cctx);
1952135446Strhodes	cleanup_cctx = ISC_FALSE;
1953135446Strhodes
1954135446Strhodes	if (dns_message_gettsigkey(fctx->qmessage) != NULL) {
1955135446Strhodes		dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage),
1956135446Strhodes				   &query->tsigkey);
1957135446Strhodes		result = dns_message_getquerytsig(fctx->qmessage,
1958135446Strhodes						  fctx->res->mctx,
1959135446Strhodes						  &query->tsig);
1960135446Strhodes		if (result != ISC_R_SUCCESS)
1961135446Strhodes			goto cleanup_message;
1962135446Strhodes	}
1963135446Strhodes
1964135446Strhodes	/*
1965135446Strhodes	 * If using TCP, write the length of the message at the beginning
1966135446Strhodes	 * of the buffer.
1967135446Strhodes	 */
1968135446Strhodes	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
1969135446Strhodes		isc_buffer_usedregion(&query->buffer, &r);
1970135446Strhodes		isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length);
1971135446Strhodes		isc_buffer_add(&tcpbuffer, r.length);
1972135446Strhodes	}
1973135446Strhodes
1974135446Strhodes	/*
1975135446Strhodes	 * We're now done with the query message.
1976135446Strhodes	 */
1977135446Strhodes	dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
1978135446Strhodes
1979186462Sdougb	if (query->exclusivesocket)
1980186462Sdougb		socket = dns_dispatch_getentrysocket(query->dispentry);
1981186462Sdougb	else
1982186462Sdougb		socket = dns_dispatch_getsocket(query->dispatch);
1983135446Strhodes	/*
1984135446Strhodes	 * Send the query!
1985135446Strhodes	 */
1986186462Sdougb	if ((query->options & DNS_FETCHOPT_TCP) == 0) {
1987135446Strhodes		address = &query->addrinfo->sockaddr;
1988186462Sdougb		if (query->exclusivesocket) {
1989186462Sdougb			result = isc_socket_connect(socket, address, task,
1990186462Sdougb						    resquery_udpconnected,
1991186462Sdougb						    query);
1992186462Sdougb			if (result != ISC_R_SUCCESS)
1993186462Sdougb				goto cleanup_message;
1994234010Sdougb			connecting = ISC_TRUE;
1995186462Sdougb			query->connects++;
1996186462Sdougb		}
1997186462Sdougb	}
1998135446Strhodes	isc_buffer_usedregion(buffer, &r);
1999135446Strhodes
2000135446Strhodes	/*
2001135446Strhodes	 * XXXRTH  Make sure we don't send to ourselves!  We should probably
2002193149Sdougb	 *		prune out these addresses when we get them from the ADB.
2003135446Strhodes	 */
2004254897Serwin	ISC_EVENT_INIT(&query->sendevent, sizeof(query->sendevent), 0, NULL,
2005254897Serwin		       ISC_SOCKEVENT_SENDDONE, resquery_senddone, query,
2006254897Serwin		       NULL, NULL, NULL);
2007254897Serwin	result = isc_socket_sendto2(socket, &r, task, address, NULL,
2008254897Serwin				    &query->sendevent, 0);
2009234010Sdougb	if (result != ISC_R_SUCCESS) {
2010234010Sdougb		if (connecting) {
2011234010Sdougb			/*
2012234010Sdougb			 * This query is still connecting.
2013234010Sdougb			 * Mark it as canceled so that it will just be
2014234010Sdougb			 * cleaned up when the connected event is received.
2015234010Sdougb			 * Keep fctx around until the event is processed.
2016234010Sdougb			 */
2017234010Sdougb			query->fctx->nqueries++;
2018234010Sdougb			query->attributes |= RESQUERY_ATTR_CANCELED;
2019234010Sdougb		}
2020135446Strhodes		goto cleanup_message;
2021234010Sdougb	}
2022193149Sdougb
2023135446Strhodes	query->sends++;
2024193149Sdougb
2025135446Strhodes	QTRACE("sent");
2026135446Strhodes
2027135446Strhodes	return (ISC_R_SUCCESS);
2028135446Strhodes
2029135446Strhodes cleanup_message:
2030135446Strhodes	if (cleanup_cctx)
2031135446Strhodes		dns_compress_invalidate(&cctx);
2032135446Strhodes
2033135446Strhodes	dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
2034135446Strhodes
2035135446Strhodes	/*
2036135446Strhodes	 * Stop the dispatcher from listening.
2037135446Strhodes	 */
2038135446Strhodes	dns_dispatch_removeresponse(&query->dispentry, NULL);
2039135446Strhodes
2040135446Strhodes cleanup_temps:
2041135446Strhodes	if (qname != NULL)
2042135446Strhodes		dns_message_puttempname(fctx->qmessage, &qname);
2043135446Strhodes	if (qrdataset != NULL)
2044135446Strhodes		dns_message_puttemprdataset(fctx->qmessage, &qrdataset);
2045135446Strhodes
2046135446Strhodes	return (result);
2047135446Strhodes}
2048135446Strhodes
2049135446Strhodesstatic void
2050135446Strhodesresquery_connected(isc_task_t *task, isc_event_t *event) {
2051135446Strhodes	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
2052135446Strhodes	resquery_t *query = event->ev_arg;
2053143731Sdougb	isc_boolean_t retry = ISC_FALSE;
2054193149Sdougb	isc_interval_t interval;
2055135446Strhodes	isc_result_t result;
2056143731Sdougb	unsigned int attrs;
2057143731Sdougb	fetchctx_t *fctx;
2058135446Strhodes
2059135446Strhodes	REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
2060135446Strhodes	REQUIRE(VALID_QUERY(query));
2061135446Strhodes
2062135446Strhodes	QTRACE("connected");
2063135446Strhodes
2064135446Strhodes	UNUSED(task);
2065135446Strhodes
2066135446Strhodes	/*
2067135446Strhodes	 * XXXRTH
2068135446Strhodes	 *
2069135446Strhodes	 * Currently we don't wait for the connect event before retrying
2070135446Strhodes	 * a query.  This means that if we get really behind, we may end
2071135446Strhodes	 * up doing extra work!
2072135446Strhodes	 */
2073135446Strhodes
2074135446Strhodes	query->connects--;
2075143731Sdougb	fctx = query->fctx;
2076135446Strhodes
2077135446Strhodes	if (RESQUERY_CANCELED(query)) {
2078135446Strhodes		/*
2079135446Strhodes		 * This query was canceled while the connect() was in
2080135446Strhodes		 * progress.
2081135446Strhodes		 */
2082135446Strhodes		isc_socket_detach(&query->tcpsocket);
2083135446Strhodes		resquery_destroy(&query);
2084135446Strhodes	} else {
2085143731Sdougb		switch (sevent->result) {
2086143731Sdougb		case ISC_R_SUCCESS:
2087193149Sdougb
2088135446Strhodes			/*
2089193149Sdougb			 * Extend the idle timer for TCP.  20 seconds
2090193149Sdougb			 * should be long enough for a TCP connection to be
2091193149Sdougb			 * established, a single DNS request to be sent,
2092193149Sdougb			 * and the response received.
2093193149Sdougb			 */
2094193149Sdougb			isc_interval_set(&interval, 20, 0);
2095193149Sdougb			result = fctx_startidletimer(query->fctx, &interval);
2096193149Sdougb			if (result != ISC_R_SUCCESS) {
2097193149Sdougb				fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
2098193149Sdougb				fctx_done(fctx, result, __LINE__);
2099193149Sdougb				break;
2100193149Sdougb			}
2101193149Sdougb			/*
2102135446Strhodes			 * We are connected.  Create a dispatcher and
2103135446Strhodes			 * send the query.
2104135446Strhodes			 */
2105135446Strhodes			attrs = 0;
2106135446Strhodes			attrs |= DNS_DISPATCHATTR_TCP;
2107135446Strhodes			attrs |= DNS_DISPATCHATTR_PRIVATE;
2108135446Strhodes			attrs |= DNS_DISPATCHATTR_CONNECTED;
2109135446Strhodes			if (isc_sockaddr_pf(&query->addrinfo->sockaddr) ==
2110135446Strhodes			    AF_INET)
2111135446Strhodes				attrs |= DNS_DISPATCHATTR_IPV4;
2112135446Strhodes			else
2113135446Strhodes				attrs |= DNS_DISPATCHATTR_IPV6;
2114135446Strhodes			attrs |= DNS_DISPATCHATTR_MAKEQUERY;
2115135446Strhodes
2116135446Strhodes			result = dns_dispatch_createtcp(query->dispatchmgr,
2117135446Strhodes						     query->tcpsocket,
2118135446Strhodes						     query->fctx->res->taskmgr,
2119135446Strhodes						     4096, 2, 1, 1, 3, attrs,
2120135446Strhodes						     &query->dispatch);
2121135446Strhodes
2122135446Strhodes			/*
2123135446Strhodes			 * Regardless of whether dns_dispatch_create()
2124135446Strhodes			 * succeeded or not, we don't need our reference
2125135446Strhodes			 * to the socket anymore.
2126135446Strhodes			 */
2127135446Strhodes			isc_socket_detach(&query->tcpsocket);
2128135446Strhodes
2129135446Strhodes			if (result == ISC_R_SUCCESS)
2130135446Strhodes				result = resquery_send(query);
2131135446Strhodes
2132135446Strhodes			if (result != ISC_R_SUCCESS) {
2133193149Sdougb				fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
2134193149Sdougb				fctx_done(fctx, result, __LINE__);
2135135446Strhodes			}
2136143731Sdougb			break;
2137143731Sdougb
2138143731Sdougb		case ISC_R_NETUNREACH:
2139143731Sdougb		case ISC_R_HOSTUNREACH:
2140143731Sdougb		case ISC_R_CONNREFUSED:
2141143731Sdougb		case ISC_R_NOPERM:
2142143731Sdougb		case ISC_R_ADDRNOTAVAIL:
2143153816Sdougb		case ISC_R_CONNECTIONRESET:
2144143731Sdougb			/*
2145143731Sdougb			 * No route to remote.
2146143731Sdougb			 */
2147135446Strhodes			isc_socket_detach(&query->tcpsocket);
2148143731Sdougb			fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
2149143731Sdougb			retry = ISC_TRUE;
2150143731Sdougb			break;
2151143731Sdougb
2152143731Sdougb		default:
2153143731Sdougb			isc_socket_detach(&query->tcpsocket);
2154135446Strhodes			fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
2155143731Sdougb			break;
2156135446Strhodes		}
2157135446Strhodes	}
2158135446Strhodes
2159135446Strhodes	isc_event_free(&event);
2160186462Sdougb
2161143731Sdougb	if (retry) {
2162143731Sdougb		/*
2163143731Sdougb		 * Behave as if the idle timer has expired.  For TCP
2164143731Sdougb		 * connections this may not actually reflect the latest timer.
2165143731Sdougb		 */
2166143731Sdougb		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
2167143731Sdougb		result = fctx_stopidletimer(fctx);
2168143731Sdougb		if (result != ISC_R_SUCCESS)
2169193149Sdougb			fctx_done(fctx, result, __LINE__);
2170143731Sdougb		else
2171205292Sdougb			fctx_try(fctx, ISC_TRUE, ISC_FALSE);
2172143731Sdougb	}
2173135446Strhodes}
2174135446Strhodes
2175135446Strhodesstatic void
2176135446Strhodesfctx_finddone(isc_task_t *task, isc_event_t *event) {
2177135446Strhodes	fetchctx_t *fctx;
2178135446Strhodes	dns_adbfind_t *find;
2179135446Strhodes	dns_resolver_t *res;
2180135446Strhodes	isc_boolean_t want_try = ISC_FALSE;
2181135446Strhodes	isc_boolean_t want_done = ISC_FALSE;
2182135446Strhodes	isc_boolean_t bucket_empty = ISC_FALSE;
2183236374Sdougb	unsigned int bucketnum;
2184234010Sdougb	isc_boolean_t destroy = ISC_FALSE;
2185135446Strhodes
2186135446Strhodes	find = event->ev_sender;
2187135446Strhodes	fctx = event->ev_arg;
2188135446Strhodes	REQUIRE(VALID_FCTX(fctx));
2189135446Strhodes	res = fctx->res;
2190135446Strhodes
2191135446Strhodes	UNUSED(task);
2192135446Strhodes
2193135446Strhodes	FCTXTRACE("finddone");
2194135446Strhodes
2195234010Sdougb	bucketnum = fctx->bucketnum;
2196234010Sdougb	LOCK(&res->buckets[bucketnum].lock);
2197234010Sdougb
2198135446Strhodes	INSIST(fctx->pending > 0);
2199135446Strhodes	fctx->pending--;
2200135446Strhodes
2201135446Strhodes	if (ADDRWAIT(fctx)) {
2202135446Strhodes		/*
2203135446Strhodes		 * The fetch is waiting for a name to be found.
2204135446Strhodes		 */
2205135446Strhodes		INSIST(!SHUTTINGDOWN(fctx));
2206135446Strhodes		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
2207275672Sdelphij		if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) {
2208135446Strhodes			want_try = ISC_TRUE;
2209275672Sdelphij			fctx->totalqueries += find->qtotal;
2210275672Sdelphij		} else {
2211193149Sdougb			fctx->findfail++;
2212193149Sdougb			if (fctx->pending == 0) {
2213193149Sdougb				/*
2214193149Sdougb				 * We've got nothing else to wait for and don't
2215193149Sdougb				 * know the answer.  There's nothing to do but
2216193149Sdougb				 * fail the fctx.
2217193149Sdougb				 */
2218193149Sdougb				want_done = ISC_TRUE;
2219193149Sdougb			}
2220135446Strhodes		}
2221135446Strhodes	} else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 &&
2222166332Sdougb		   fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) {
2223236374Sdougb
2224236374Sdougb		if (fctx->references == 0) {
2225236374Sdougb			bucket_empty = fctx_unlink(fctx);
2226234010Sdougb			destroy = ISC_TRUE;
2227236374Sdougb		}
2228135446Strhodes	}
2229234010Sdougb	UNLOCK(&res->buckets[bucketnum].lock);
2230135446Strhodes
2231135446Strhodes	isc_event_free(&event);
2232135446Strhodes	dns_adb_destroyfind(&find);
2233135446Strhodes
2234135446Strhodes	if (want_try)
2235205292Sdougb		fctx_try(fctx, ISC_TRUE, ISC_FALSE);
2236135446Strhodes	else if (want_done)
2237193149Sdougb		fctx_done(fctx, ISC_R_FAILURE, __LINE__);
2238236374Sdougb	else if (destroy) {
2239236374Sdougb			fctx_destroy(fctx);
2240236374Sdougb		if (bucket_empty)
2241236374Sdougb			empty_bucket(res);
2242236374Sdougb	}
2243135446Strhodes}
2244135446Strhodes
2245135446Strhodes
2246135446Strhodesstatic inline isc_boolean_t
2247135446Strhodesbad_server(fetchctx_t *fctx, isc_sockaddr_t *address) {
2248135446Strhodes	isc_sockaddr_t *sa;
2249135446Strhodes
2250135446Strhodes	for (sa = ISC_LIST_HEAD(fctx->bad);
2251135446Strhodes	     sa != NULL;
2252135446Strhodes	     sa = ISC_LIST_NEXT(sa, link)) {
2253135446Strhodes		if (isc_sockaddr_equal(sa, address))
2254135446Strhodes			return (ISC_TRUE);
2255135446Strhodes	}
2256135446Strhodes
2257135446Strhodes	return (ISC_FALSE);
2258135446Strhodes}
2259135446Strhodes
2260135446Strhodesstatic inline isc_boolean_t
2261135446Strhodesmark_bad(fetchctx_t *fctx) {
2262135446Strhodes	dns_adbfind_t *curr;
2263135446Strhodes	dns_adbaddrinfo_t *addrinfo;
2264135446Strhodes	isc_boolean_t all_bad = ISC_TRUE;
2265135446Strhodes
2266135446Strhodes	/*
2267135446Strhodes	 * Mark all known bad servers, so we don't try to talk to them
2268135446Strhodes	 * again.
2269135446Strhodes	 */
2270135446Strhodes
2271135446Strhodes	/*
2272135446Strhodes	 * Mark any bad nameservers.
2273135446Strhodes	 */
2274135446Strhodes	for (curr = ISC_LIST_HEAD(fctx->finds);
2275135446Strhodes	     curr != NULL;
2276135446Strhodes	     curr = ISC_LIST_NEXT(curr, publink)) {
2277135446Strhodes		for (addrinfo = ISC_LIST_HEAD(curr->list);
2278135446Strhodes		     addrinfo != NULL;
2279135446Strhodes		     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2280135446Strhodes			if (bad_server(fctx, &addrinfo->sockaddr))
2281135446Strhodes				addrinfo->flags |= FCTX_ADDRINFO_MARK;
2282135446Strhodes			else
2283135446Strhodes				all_bad = ISC_FALSE;
2284135446Strhodes		}
2285135446Strhodes	}
2286135446Strhodes
2287135446Strhodes	/*
2288135446Strhodes	 * Mark any bad forwarders.
2289135446Strhodes	 */
2290135446Strhodes	for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
2291135446Strhodes	     addrinfo != NULL;
2292135446Strhodes	     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2293135446Strhodes		if (bad_server(fctx, &addrinfo->sockaddr))
2294135446Strhodes			addrinfo->flags |= FCTX_ADDRINFO_MARK;
2295135446Strhodes		else
2296135446Strhodes			all_bad = ISC_FALSE;
2297135446Strhodes	}
2298135446Strhodes
2299135446Strhodes	/*
2300135446Strhodes	 * Mark any bad alternates.
2301135446Strhodes	 */
2302135446Strhodes	for (curr = ISC_LIST_HEAD(fctx->altfinds);
2303135446Strhodes	     curr != NULL;
2304135446Strhodes	     curr = ISC_LIST_NEXT(curr, publink)) {
2305135446Strhodes		for (addrinfo = ISC_LIST_HEAD(curr->list);
2306135446Strhodes		     addrinfo != NULL;
2307135446Strhodes		     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2308135446Strhodes			if (bad_server(fctx, &addrinfo->sockaddr))
2309135446Strhodes				addrinfo->flags |= FCTX_ADDRINFO_MARK;
2310135446Strhodes			else
2311135446Strhodes				all_bad = ISC_FALSE;
2312135446Strhodes		}
2313135446Strhodes	}
2314135446Strhodes
2315135446Strhodes	for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
2316135446Strhodes	     addrinfo != NULL;
2317135446Strhodes	     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2318135446Strhodes		if (bad_server(fctx, &addrinfo->sockaddr))
2319135446Strhodes			addrinfo->flags |= FCTX_ADDRINFO_MARK;
2320135446Strhodes		else
2321135446Strhodes			all_bad = ISC_FALSE;
2322135446Strhodes	}
2323135446Strhodes
2324135446Strhodes	return (all_bad);
2325135446Strhodes}
2326135446Strhodes
2327135446Strhodesstatic void
2328193149Sdougbadd_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason,
2329193149Sdougb	badnstype_t badtype)
2330193149Sdougb{
2331135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
2332135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
2333135446Strhodes	char classbuf[64];
2334135446Strhodes	char typebuf[64];
2335135446Strhodes	char code[64];
2336135446Strhodes	isc_buffer_t b;
2337135446Strhodes	isc_sockaddr_t *sa;
2338224092Sdougb	const char *spc = "";
2339174187Sdougb	isc_sockaddr_t *address = &addrinfo->sockaddr;
2340135446Strhodes
2341193149Sdougb	if (reason == DNS_R_LAME)
2342193149Sdougb		fctx->lamecount++;
2343193149Sdougb	else {
2344193149Sdougb		switch (badtype) {
2345193149Sdougb		case badns_unreachable:
2346193149Sdougb			fctx->neterr++;
2347193149Sdougb			break;
2348193149Sdougb		case badns_response:
2349193149Sdougb			fctx->badresp++;
2350193149Sdougb			break;
2351193149Sdougb		case badns_validation:
2352193149Sdougb			break;	/* counted as 'valfail' */
2353193149Sdougb		}
2354193149Sdougb	}
2355193149Sdougb
2356135446Strhodes	if (bad_server(fctx, address)) {
2357135446Strhodes		/*
2358135446Strhodes		 * We already know this server is bad.
2359135446Strhodes		 */
2360135446Strhodes		return;
2361135446Strhodes	}
2362135446Strhodes
2363135446Strhodes	FCTXTRACE("add_bad");
2364135446Strhodes
2365236374Sdougb	sa = isc_mem_get(fctx->mctx, sizeof(*sa));
2366135446Strhodes	if (sa == NULL)
2367135446Strhodes		return;
2368135446Strhodes	*sa = *address;
2369135446Strhodes	ISC_LIST_INITANDAPPEND(fctx->bad, sa, link);
2370135446Strhodes
2371193149Sdougb	if (reason == DNS_R_LAME)       /* already logged */
2372135446Strhodes		return;
2373135446Strhodes
2374186462Sdougb	if (reason == DNS_R_UNEXPECTEDRCODE &&
2375186462Sdougb	    fctx->rmessage->rcode == dns_rcode_servfail &&
2376174187Sdougb	    ISFORWARDER(addrinfo))
2377174187Sdougb		return;
2378174187Sdougb
2379135446Strhodes	if (reason == DNS_R_UNEXPECTEDRCODE) {
2380135446Strhodes		isc_buffer_init(&b, code, sizeof(code) - 1);
2381135446Strhodes		dns_rcode_totext(fctx->rmessage->rcode, &b);
2382135446Strhodes		code[isc_buffer_usedlength(&b)] = '\0';
2383224092Sdougb		spc = " ";
2384135446Strhodes	} else if (reason == DNS_R_UNEXPECTEDOPCODE) {
2385135446Strhodes		isc_buffer_init(&b, code, sizeof(code) - 1);
2386135446Strhodes		dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b);
2387135446Strhodes		code[isc_buffer_usedlength(&b)] = '\0';
2388224092Sdougb		spc = " ";
2389135446Strhodes	} else {
2390135446Strhodes		code[0] = '\0';
2391135446Strhodes	}
2392135446Strhodes	dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
2393135446Strhodes	dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf));
2394135446Strhodes	dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf));
2395135446Strhodes	isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
2396135446Strhodes	isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS,
2397135446Strhodes		      DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
2398224092Sdougb		      "error (%s%s%s) resolving '%s/%s/%s': %s",
2399224092Sdougb		      dns_result_totext(reason), spc, code,
2400135446Strhodes		      namebuf, typebuf, classbuf, addrbuf);
2401135446Strhodes}
2402135446Strhodes
2403193149Sdougb/*
2404224092Sdougb * Sort addrinfo list by RTT.
2405193149Sdougb */
2406135446Strhodesstatic void
2407224092Sdougbsort_adbfind(dns_adbfind_t *find) {
2408135446Strhodes	dns_adbaddrinfo_t *best, *curr;
2409135446Strhodes	dns_adbaddrinfolist_t sorted;
2410135446Strhodes
2411193149Sdougb	/* Lame N^2 bubble sort. */
2412135446Strhodes	ISC_LIST_INIT(sorted);
2413135446Strhodes	while (!ISC_LIST_EMPTY(find->list)) {
2414135446Strhodes		best = ISC_LIST_HEAD(find->list);
2415135446Strhodes		curr = ISC_LIST_NEXT(best, publink);
2416135446Strhodes		while (curr != NULL) {
2417135446Strhodes			if (curr->srtt < best->srtt)
2418135446Strhodes				best = curr;
2419135446Strhodes			curr = ISC_LIST_NEXT(curr, publink);
2420135446Strhodes		}
2421135446Strhodes		ISC_LIST_UNLINK(find->list, best, publink);
2422135446Strhodes		ISC_LIST_APPEND(sorted, best, publink);
2423135446Strhodes	}
2424135446Strhodes	find->list = sorted;
2425135446Strhodes}
2426135446Strhodes
2427193149Sdougb/*
2428224092Sdougb * Sort a list of finds by server RTT.
2429193149Sdougb */
2430135446Strhodesstatic void
2431224092Sdougbsort_finds(dns_adbfindlist_t *findlist) {
2432135446Strhodes	dns_adbfind_t *best, *curr;
2433135446Strhodes	dns_adbfindlist_t sorted;
2434135446Strhodes	dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;
2435135446Strhodes
2436224092Sdougb	/* Sort each find's addrinfo list by SRTT. */
2437193149Sdougb	for (curr = ISC_LIST_HEAD(*findlist);
2438193149Sdougb	     curr != NULL;
2439193149Sdougb	     curr = ISC_LIST_NEXT(curr, publink))
2440224092Sdougb		sort_adbfind(curr);
2441135446Strhodes
2442193149Sdougb	/* Lame N^2 bubble sort. */
2443135446Strhodes	ISC_LIST_INIT(sorted);
2444193149Sdougb	while (!ISC_LIST_EMPTY(*findlist)) {
2445193149Sdougb		best = ISC_LIST_HEAD(*findlist);
2446135446Strhodes		bestaddrinfo = ISC_LIST_HEAD(best->list);
2447135446Strhodes		INSIST(bestaddrinfo != NULL);
2448135446Strhodes		curr = ISC_LIST_NEXT(best, publink);
2449135446Strhodes		while (curr != NULL) {
2450135446Strhodes			addrinfo = ISC_LIST_HEAD(curr->list);
2451135446Strhodes			INSIST(addrinfo != NULL);
2452135446Strhodes			if (addrinfo->srtt < bestaddrinfo->srtt) {
2453135446Strhodes				best = curr;
2454135446Strhodes				bestaddrinfo = addrinfo;
2455135446Strhodes			}
2456135446Strhodes			curr = ISC_LIST_NEXT(curr, publink);
2457135446Strhodes		}
2458193149Sdougb		ISC_LIST_UNLINK(*findlist, best, publink);
2459135446Strhodes		ISC_LIST_APPEND(sorted, best, publink);
2460135446Strhodes	}
2461193149Sdougb	*findlist = sorted;
2462135446Strhodes}
2463135446Strhodes
2464135446Strhodesstatic void
2465135446Strhodesfindname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
2466135446Strhodes	 unsigned int options, unsigned int flags, isc_stdtime_t now,
2467170222Sdougb	 isc_boolean_t *need_alternate)
2468135446Strhodes{
2469135446Strhodes	dns_adbaddrinfo_t *ai;
2470135446Strhodes	dns_adbfind_t *find;
2471135446Strhodes	dns_resolver_t *res;
2472135446Strhodes	isc_boolean_t unshared;
2473135446Strhodes	isc_result_t result;
2474135446Strhodes
2475135446Strhodes	res = fctx->res;
2476254402Serwin	unshared = ISC_TF((fctx->options & DNS_FETCHOPT_UNSHARED) != 0);
2477135446Strhodes	/*
2478135446Strhodes	 * If this name is a subdomain of the query domain, tell
2479135446Strhodes	 * the ADB to start looking using zone/hint data. This keeps us
2480135446Strhodes	 * from getting stuck if the nameserver is beneath the zone cut
2481135446Strhodes	 * and we don't know its address (e.g. because the A record has
2482135446Strhodes	 * expired).
2483135446Strhodes	 */
2484135446Strhodes	if (dns_name_issubdomain(name, &fctx->domain))
2485135446Strhodes		options |= DNS_ADBFIND_STARTATZONE;
2486135446Strhodes	options |= DNS_ADBFIND_GLUEOK;
2487135446Strhodes	options |= DNS_ADBFIND_HINTOK;
2488135446Strhodes
2489135446Strhodes	/*
2490135446Strhodes	 * See what we know about this address.
2491135446Strhodes	 */
2492135446Strhodes	find = NULL;
2493275672Sdelphij	result = dns_adb_createfind2(fctx->adb,
2494275672Sdelphij				     res->buckets[fctx->bucketnum].task,
2495275672Sdelphij				     fctx_finddone, fctx, name,
2496275672Sdelphij				     &fctx->name, fctx->type,
2497275672Sdelphij				     options, now, NULL,
2498275672Sdelphij				     res->view->dstport,
2499275672Sdelphij				     fctx->depth + 1, &find);
2500135446Strhodes	if (result != ISC_R_SUCCESS) {
2501135446Strhodes		if (result == DNS_R_ALIAS) {
2502135446Strhodes			/*
2503135446Strhodes			 * XXXRTH  Follow the CNAME/DNAME chain?
2504135446Strhodes			 */
2505135446Strhodes			dns_adb_destroyfind(&find);
2506193149Sdougb			fctx->adberr++;
2507135446Strhodes		}
2508135446Strhodes	} else if (!ISC_LIST_EMPTY(find->list)) {
2509135446Strhodes		/*
2510135446Strhodes		 * We have at least some of the addresses for the
2511135446Strhodes		 * name.
2512135446Strhodes		 */
2513135446Strhodes		INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
2514135446Strhodes		if (flags != 0 || port != 0) {
2515135446Strhodes			for (ai = ISC_LIST_HEAD(find->list);
2516135446Strhodes			     ai != NULL;
2517135446Strhodes			     ai = ISC_LIST_NEXT(ai, publink)) {
2518135446Strhodes				ai->flags |= flags;
2519135446Strhodes				if (port != 0)
2520135446Strhodes					isc_sockaddr_setport(&ai->sockaddr,
2521135446Strhodes							     port);
2522135446Strhodes			}
2523135446Strhodes		}
2524135446Strhodes		if ((flags & FCTX_ADDRINFO_FORWARDER) != 0)
2525135446Strhodes			ISC_LIST_APPEND(fctx->altfinds, find, publink);
2526135446Strhodes		else
2527135446Strhodes			ISC_LIST_APPEND(fctx->finds, find, publink);
2528135446Strhodes	} else {
2529135446Strhodes		/*
2530135446Strhodes		 * We don't know any of the addresses for this
2531135446Strhodes		 * name.
2532135446Strhodes		 */
2533135446Strhodes		if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
2534135446Strhodes			/*
2535135446Strhodes			 * We're looking for them and will get an
2536135446Strhodes			 * event about it later.
2537135446Strhodes			 */
2538135446Strhodes			fctx->pending++;
2539135446Strhodes			/*
2540135446Strhodes			 * Bootstrap.
2541135446Strhodes			 */
2542135446Strhodes			if (need_alternate != NULL &&
2543135446Strhodes			    !*need_alternate && unshared &&
2544254897Serwin			    ((res->dispatches4 == NULL &&
2545135446Strhodes			      find->result_v6 != DNS_R_NXDOMAIN) ||
2546254897Serwin			     (res->dispatches6 == NULL &&
2547135446Strhodes			      find->result_v4 != DNS_R_NXDOMAIN)))
2548135446Strhodes				*need_alternate = ISC_TRUE;
2549135446Strhodes		} else {
2550193149Sdougb			if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0)
2551193149Sdougb				fctx->lamecount++; /* cached lame server */
2552193149Sdougb			else
2553193149Sdougb				fctx->adberr++; /* unreachable server, etc. */
2554193149Sdougb
2555135446Strhodes			/*
2556135446Strhodes			 * If we know there are no addresses for
2557135446Strhodes			 * the family we are using then try to add
2558135446Strhodes			 * an alternative server.
2559135446Strhodes			 */
2560135446Strhodes			if (need_alternate != NULL && !*need_alternate &&
2561254897Serwin			    ((res->dispatches4 == NULL &&
2562135446Strhodes			      find->result_v6 == DNS_R_NXRRSET) ||
2563254897Serwin			     (res->dispatches6 == NULL &&
2564135446Strhodes			      find->result_v4 == DNS_R_NXRRSET)))
2565135446Strhodes				*need_alternate = ISC_TRUE;
2566135446Strhodes			dns_adb_destroyfind(&find);
2567135446Strhodes		}
2568135446Strhodes	}
2569135446Strhodes}
2570135446Strhodes
2571204619Sdougbstatic isc_boolean_t
2572204619Sdougbisstrictsubdomain(dns_name_t *name1, dns_name_t *name2) {
2573204619Sdougb	int order;
2574204619Sdougb	unsigned int nlabels;
2575204619Sdougb	dns_namereln_t namereln;
2576204619Sdougb
2577204619Sdougb	namereln = dns_name_fullcompare(name1, name2, &order, &nlabels);
2578204619Sdougb	return (ISC_TF(namereln == dns_namereln_subdomain));
2579204619Sdougb}
2580204619Sdougb
2581135446Strhodesstatic isc_result_t
2582205292Sdougbfctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
2583135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2584135446Strhodes	isc_result_t result;
2585135446Strhodes	dns_resolver_t *res;
2586135446Strhodes	isc_stdtime_t now;
2587225361Sdougb	unsigned int stdoptions = 0;
2588135446Strhodes	isc_sockaddr_t *sa;
2589135446Strhodes	dns_adbaddrinfo_t *ai;
2590170222Sdougb	isc_boolean_t all_bad;
2591135446Strhodes	dns_rdata_ns_t ns;
2592135446Strhodes	isc_boolean_t need_alternate = ISC_FALSE;
2593135446Strhodes
2594135446Strhodes	FCTXTRACE("getaddresses");
2595135446Strhodes
2596135446Strhodes	/*
2597135446Strhodes	 * Don't pound on remote servers.  (Failsafe!)
2598135446Strhodes	 */
2599135446Strhodes	fctx->restarts++;
2600135446Strhodes	if (fctx->restarts > 10) {
2601135446Strhodes		FCTXTRACE("too many restarts");
2602135446Strhodes		return (DNS_R_SERVFAIL);
2603135446Strhodes	}
2604135446Strhodes
2605135446Strhodes	res = fctx->res;
2606135446Strhodes
2607275672Sdelphij	if (fctx->depth > res->maxdepth) {
2608275672Sdelphij		FCTXTRACE("too much NS indirection");
2609275672Sdelphij		return (DNS_R_SERVFAIL);
2610275672Sdelphij	}
2611275672Sdelphij
2612135446Strhodes	/*
2613135446Strhodes	 * Forwarders.
2614135446Strhodes	 */
2615135446Strhodes
2616135446Strhodes	INSIST(ISC_LIST_EMPTY(fctx->forwaddrs));
2617135446Strhodes	INSIST(ISC_LIST_EMPTY(fctx->altaddrs));
2618135446Strhodes
2619135446Strhodes	/*
2620135446Strhodes	 * If this fctx has forwarders, use them; otherwise use any
2621135446Strhodes	 * selective forwarders specified in the view; otherwise use the
2622135446Strhodes	 * resolver's forwarders (if any).
2623135446Strhodes	 */
2624135446Strhodes	sa = ISC_LIST_HEAD(fctx->forwarders);
2625135446Strhodes	if (sa == NULL) {
2626135446Strhodes		dns_forwarders_t *forwarders = NULL;
2627135446Strhodes		dns_name_t *name = &fctx->name;
2628135446Strhodes		dns_name_t suffix;
2629135446Strhodes		unsigned int labels;
2630204619Sdougb		dns_fixedname_t fixed;
2631204619Sdougb		dns_name_t *domain;
2632135446Strhodes
2633135446Strhodes		/*
2634135446Strhodes		 * DS records are found in the parent server.
2635135446Strhodes		 * Strip label to get the correct forwarder (if any).
2636135446Strhodes		 */
2637204619Sdougb		if (dns_rdatatype_atparent(fctx->type) &&
2638135446Strhodes		    dns_name_countlabels(name) > 1) {
2639135446Strhodes			dns_name_init(&suffix, NULL);
2640135446Strhodes			labels = dns_name_countlabels(name);
2641135446Strhodes			dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
2642135446Strhodes			name = &suffix;
2643135446Strhodes		}
2644204619Sdougb
2645204619Sdougb		dns_fixedname_init(&fixed);
2646204619Sdougb		domain = dns_fixedname_name(&fixed);
2647204619Sdougb		result = dns_fwdtable_find2(fctx->res->view->fwdtable, name,
2648204619Sdougb					    domain, &forwarders);
2649135446Strhodes		if (result == ISC_R_SUCCESS) {
2650135446Strhodes			sa = ISC_LIST_HEAD(forwarders->addrs);
2651135446Strhodes			fctx->fwdpolicy = forwarders->fwdpolicy;
2652204619Sdougb			if (fctx->fwdpolicy == dns_fwdpolicy_only &&
2653204619Sdougb			    isstrictsubdomain(domain, &fctx->domain)) {
2654236374Sdougb				dns_name_free(&fctx->domain, fctx->mctx);
2655204619Sdougb				dns_name_init(&fctx->domain, NULL);
2656236374Sdougb				result = dns_name_dup(domain, fctx->mctx,
2657204619Sdougb						      &fctx->domain);
2658204619Sdougb				if (result != ISC_R_SUCCESS)
2659204619Sdougb					return (result);
2660204619Sdougb			}
2661135446Strhodes		}
2662135446Strhodes	}
2663135446Strhodes
2664135446Strhodes	while (sa != NULL) {
2665186462Sdougb		if ((isc_sockaddr_pf(sa) == AF_INET &&
2666254897Serwin			 fctx->res->dispatches4 == NULL) ||
2667186462Sdougb		    (isc_sockaddr_pf(sa) == AF_INET6 &&
2668254897Serwin			fctx->res->dispatches6 == NULL)) {
2669186462Sdougb				sa = ISC_LIST_NEXT(sa, link);
2670186462Sdougb				continue;
2671186462Sdougb		}
2672135446Strhodes		ai = NULL;
2673135446Strhodes		result = dns_adb_findaddrinfo(fctx->adb,
2674135446Strhodes					      sa, &ai, 0);  /* XXXMLG */
2675135446Strhodes		if (result == ISC_R_SUCCESS) {
2676135446Strhodes			dns_adbaddrinfo_t *cur;
2677135446Strhodes			ai->flags |= FCTX_ADDRINFO_FORWARDER;
2678135446Strhodes			cur = ISC_LIST_HEAD(fctx->forwaddrs);
2679135446Strhodes			while (cur != NULL && cur->srtt < ai->srtt)
2680135446Strhodes				cur = ISC_LIST_NEXT(cur, publink);
2681135446Strhodes			if (cur != NULL)
2682135446Strhodes				ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur,
2683135446Strhodes						      ai, publink);
2684135446Strhodes			else
2685135446Strhodes				ISC_LIST_APPEND(fctx->forwaddrs, ai, publink);
2686135446Strhodes		}
2687135446Strhodes		sa = ISC_LIST_NEXT(sa, link);
2688135446Strhodes	}
2689135446Strhodes
2690135446Strhodes	/*
2691135446Strhodes	 * If the forwarding policy is "only", we don't need the addresses
2692135446Strhodes	 * of the nameservers.
2693135446Strhodes	 */
2694135446Strhodes	if (fctx->fwdpolicy == dns_fwdpolicy_only)
2695135446Strhodes		goto out;
2696135446Strhodes
2697135446Strhodes	/*
2698135446Strhodes	 * Normal nameservers.
2699135446Strhodes	 */
2700135446Strhodes
2701135446Strhodes	stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT;
2702135446Strhodes	if (fctx->restarts == 1) {
2703135446Strhodes		/*
2704135446Strhodes		 * To avoid sending out a flood of queries likely to
2705135446Strhodes		 * result in NXRRSET, we suppress fetches for address
2706135446Strhodes		 * families we don't have the first time through,
2707135446Strhodes		 * provided that we have addresses in some family we
2708135446Strhodes		 * can use.
2709135446Strhodes		 *
2710135446Strhodes		 * We don't want to set this option all the time, since
2711135446Strhodes		 * if fctx->restarts > 1, we've clearly been having trouble
2712135446Strhodes		 * with the addresses we had, so getting more could help.
2713135446Strhodes		 */
2714135446Strhodes		stdoptions |= DNS_ADBFIND_AVOIDFETCHES;
2715135446Strhodes	}
2716254897Serwin	if (res->dispatches4 != NULL)
2717135446Strhodes		stdoptions |= DNS_ADBFIND_INET;
2718254897Serwin	if (res->dispatches6 != NULL)
2719135446Strhodes		stdoptions |= DNS_ADBFIND_INET6;
2720135446Strhodes	isc_stdtime_get(&now);
2721135446Strhodes
2722135446Strhodes	INSIST(ISC_LIST_EMPTY(fctx->finds));
2723135446Strhodes	INSIST(ISC_LIST_EMPTY(fctx->altfinds));
2724135446Strhodes
2725135446Strhodes	for (result = dns_rdataset_first(&fctx->nameservers);
2726135446Strhodes	     result == ISC_R_SUCCESS;
2727135446Strhodes	     result = dns_rdataset_next(&fctx->nameservers))
2728135446Strhodes	{
2729135446Strhodes		dns_rdataset_current(&fctx->nameservers, &rdata);
2730135446Strhodes		/*
2731135446Strhodes		 * Extract the name from the NS record.
2732135446Strhodes		 */
2733135446Strhodes		result = dns_rdata_tostruct(&rdata, &ns, NULL);
2734135446Strhodes		if (result != ISC_R_SUCCESS)
2735135446Strhodes			continue;
2736135446Strhodes
2737135446Strhodes		findname(fctx, &ns.name, 0, stdoptions, 0, now,
2738170222Sdougb			 &need_alternate);
2739135446Strhodes		dns_rdata_reset(&rdata);
2740135446Strhodes		dns_rdata_freestruct(&ns);
2741135446Strhodes	}
2742135446Strhodes	if (result != ISC_R_NOMORE)
2743135446Strhodes		return (result);
2744135446Strhodes
2745135446Strhodes	/*
2746135446Strhodes	 * Do we need to use 6 to 4?
2747135446Strhodes	 */
2748135446Strhodes	if (need_alternate) {
2749135446Strhodes		int family;
2750135446Strhodes		alternate_t *a;
2751254897Serwin		family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET;
2752135446Strhodes		for (a = ISC_LIST_HEAD(fctx->res->alternates);
2753135446Strhodes		     a != NULL;
2754135446Strhodes		     a = ISC_LIST_NEXT(a, link)) {
2755135446Strhodes			if (!a->isaddress) {
2756135446Strhodes				findname(fctx, &a->_u._n.name, a->_u._n.port,
2757135446Strhodes					 stdoptions, FCTX_ADDRINFO_FORWARDER,
2758170222Sdougb					 now, NULL);
2759135446Strhodes				continue;
2760135446Strhodes			}
2761135446Strhodes			if (isc_sockaddr_pf(&a->_u.addr) != family)
2762135446Strhodes				continue;
2763135446Strhodes			ai = NULL;
2764135446Strhodes			result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr,
2765135446Strhodes						      &ai, 0);
2766135446Strhodes			if (result == ISC_R_SUCCESS) {
2767135446Strhodes				dns_adbaddrinfo_t *cur;
2768135446Strhodes				ai->flags |= FCTX_ADDRINFO_FORWARDER;
2769135446Strhodes				cur = ISC_LIST_HEAD(fctx->altaddrs);
2770135446Strhodes				while (cur != NULL && cur->srtt < ai->srtt)
2771135446Strhodes					cur = ISC_LIST_NEXT(cur, publink);
2772135446Strhodes				if (cur != NULL)
2773135446Strhodes					ISC_LIST_INSERTBEFORE(fctx->altaddrs,
2774135446Strhodes							      cur, ai, publink);
2775135446Strhodes				else
2776135446Strhodes					ISC_LIST_APPEND(fctx->altaddrs, ai,
2777135446Strhodes							publink);
2778135446Strhodes			}
2779135446Strhodes		}
2780135446Strhodes	}
2781135446Strhodes
2782135446Strhodes out:
2783135446Strhodes	/*
2784135446Strhodes	 * Mark all known bad servers.
2785135446Strhodes	 */
2786135446Strhodes	all_bad = mark_bad(fctx);
2787135446Strhodes
2788135446Strhodes	/*
2789135446Strhodes	 * How are we doing?
2790135446Strhodes	 */
2791135446Strhodes	if (all_bad) {
2792135446Strhodes		/*
2793135446Strhodes		 * We've got no addresses.
2794135446Strhodes		 */
2795135446Strhodes		if (fctx->pending > 0) {
2796135446Strhodes			/*
2797135446Strhodes			 * We're fetching the addresses, but don't have any
2798135446Strhodes			 * yet.   Tell the caller to wait for an answer.
2799135446Strhodes			 */
2800135446Strhodes			result = DNS_R_WAIT;
2801135446Strhodes		} else {
2802205292Sdougb			isc_time_t expire;
2803205292Sdougb			isc_interval_t i;
2804135446Strhodes			/*
2805135446Strhodes			 * We've lost completely.  We don't know any
2806135446Strhodes			 * addresses, and the ADB has told us it can't get
2807135446Strhodes			 * them.
2808135446Strhodes			 */
2809135446Strhodes			FCTXTRACE("no addresses");
2810205292Sdougb			isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0);
2811205292Sdougb			result = isc_time_nowplusinterval(&expire, &i);
2812205292Sdougb			if (badcache &&
2813205292Sdougb			    (fctx->type == dns_rdatatype_dnskey ||
2814205292Sdougb			     fctx->type == dns_rdatatype_dlv ||
2815205292Sdougb			     fctx->type == dns_rdatatype_ds) &&
2816205292Sdougb			     result == ISC_R_SUCCESS)
2817205292Sdougb				dns_resolver_addbadcache(fctx->res,
2818205292Sdougb							 &fctx->name,
2819205292Sdougb							 fctx->type, &expire);
2820135446Strhodes			result = ISC_R_FAILURE;
2821135446Strhodes		}
2822135446Strhodes	} else {
2823135446Strhodes		/*
2824135446Strhodes		 * We've found some addresses.  We might still be looking
2825135446Strhodes		 * for more addresses.
2826135446Strhodes		 */
2827224092Sdougb		sort_finds(&fctx->finds);
2828224092Sdougb		sort_finds(&fctx->altfinds);
2829135446Strhodes		result = ISC_R_SUCCESS;
2830135446Strhodes	}
2831135446Strhodes
2832135446Strhodes	return (result);
2833135446Strhodes}
2834135446Strhodes
2835135446Strhodesstatic inline void
2836135446Strhodespossibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr)
2837135446Strhodes{
2838135446Strhodes	isc_netaddr_t na;
2839135446Strhodes	char buf[ISC_NETADDR_FORMATSIZE];
2840135446Strhodes	isc_sockaddr_t *sa;
2841135446Strhodes	isc_boolean_t aborted = ISC_FALSE;
2842135446Strhodes	isc_boolean_t bogus;
2843135446Strhodes	dns_acl_t *blackhole;
2844135446Strhodes	isc_netaddr_t ipaddr;
2845135446Strhodes	dns_peer_t *peer = NULL;
2846135446Strhodes	dns_resolver_t *res;
2847135446Strhodes	const char *msg = NULL;
2848135446Strhodes
2849135446Strhodes	sa = &addr->sockaddr;
2850135446Strhodes
2851135446Strhodes	res = fctx->res;
2852135446Strhodes	isc_netaddr_fromsockaddr(&ipaddr, sa);
2853135446Strhodes	blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr);
2854135446Strhodes	(void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer);
2855186462Sdougb
2856135446Strhodes	if (blackhole != NULL) {
2857135446Strhodes		int match;
2858135446Strhodes
2859135446Strhodes		if (dns_acl_match(&ipaddr, NULL, blackhole,
2860135446Strhodes				  &res->view->aclenv,
2861135446Strhodes				  &match, NULL) == ISC_R_SUCCESS &&
2862135446Strhodes		    match > 0)
2863135446Strhodes			aborted = ISC_TRUE;
2864135446Strhodes	}
2865135446Strhodes
2866135446Strhodes	if (peer != NULL &&
2867135446Strhodes	    dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS &&
2868135446Strhodes	    bogus)
2869135446Strhodes		aborted = ISC_TRUE;
2870135446Strhodes
2871135446Strhodes	if (aborted) {
2872135446Strhodes		addr->flags |= FCTX_ADDRINFO_MARK;
2873135446Strhodes		msg = "ignoring blackholed / bogus server: ";
2874135446Strhodes	} else if (isc_sockaddr_ismulticast(sa)) {
2875135446Strhodes		addr->flags |= FCTX_ADDRINFO_MARK;
2876135446Strhodes		msg = "ignoring multicast address: ";
2877135446Strhodes	} else if (isc_sockaddr_isexperimental(sa)) {
2878135446Strhodes		addr->flags |= FCTX_ADDRINFO_MARK;
2879135446Strhodes		msg = "ignoring experimental address: ";
2880135446Strhodes	} else if (sa->type.sa.sa_family != AF_INET6) {
2881135446Strhodes		return;
2882135446Strhodes	} else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) {
2883135446Strhodes		addr->flags |= FCTX_ADDRINFO_MARK;
2884135446Strhodes		msg = "ignoring IPv6 mapped IPV4 address: ";
2885135446Strhodes	} else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) {
2886135446Strhodes		addr->flags |= FCTX_ADDRINFO_MARK;
2887135446Strhodes		msg = "ignoring IPv6 compatibility IPV4 address: ";
2888135446Strhodes	} else
2889135446Strhodes		return;
2890135446Strhodes
2891135446Strhodes	if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3)))
2892135446Strhodes		return;
2893135446Strhodes
2894135446Strhodes	isc_netaddr_fromsockaddr(&na, sa);
2895135446Strhodes	isc_netaddr_format(&na, buf, sizeof(buf));
2896135446Strhodes	FCTXTRACE2(msg, buf);
2897135446Strhodes}
2898135446Strhodes
2899135446Strhodesstatic inline dns_adbaddrinfo_t *
2900135446Strhodesfctx_nextaddress(fetchctx_t *fctx) {
2901135446Strhodes	dns_adbfind_t *find, *start;
2902135446Strhodes	dns_adbaddrinfo_t *addrinfo;
2903135446Strhodes	dns_adbaddrinfo_t *faddrinfo;
2904135446Strhodes
2905135446Strhodes	/*
2906135446Strhodes	 * Return the next untried address, if any.
2907135446Strhodes	 */
2908135446Strhodes
2909135446Strhodes	/*
2910135446Strhodes	 * Find the first unmarked forwarder (if any).
2911135446Strhodes	 */
2912135446Strhodes	for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
2913135446Strhodes	     addrinfo != NULL;
2914135446Strhodes	     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2915135446Strhodes		if (!UNMARKED(addrinfo))
2916135446Strhodes			continue;
2917135446Strhodes		possibly_mark(fctx, addrinfo);
2918135446Strhodes		if (UNMARKED(addrinfo)) {
2919135446Strhodes			addrinfo->flags |= FCTX_ADDRINFO_MARK;
2920135446Strhodes			fctx->find = NULL;
2921135446Strhodes			return (addrinfo);
2922135446Strhodes		}
2923135446Strhodes	}
2924135446Strhodes
2925135446Strhodes	/*
2926135446Strhodes	 * No forwarders.  Move to the next find.
2927135446Strhodes	 */
2928135446Strhodes
2929135446Strhodes	fctx->attributes |= FCTX_ATTR_TRIEDFIND;
2930135446Strhodes
2931135446Strhodes	find = fctx->find;
2932135446Strhodes	if (find == NULL)
2933135446Strhodes		find = ISC_LIST_HEAD(fctx->finds);
2934135446Strhodes	else {
2935135446Strhodes		find = ISC_LIST_NEXT(find, publink);
2936135446Strhodes		if (find == NULL)
2937135446Strhodes			find = ISC_LIST_HEAD(fctx->finds);
2938135446Strhodes	}
2939135446Strhodes
2940135446Strhodes	/*
2941135446Strhodes	 * Find the first unmarked addrinfo.
2942135446Strhodes	 */
2943135446Strhodes	addrinfo = NULL;
2944135446Strhodes	if (find != NULL) {
2945135446Strhodes		start = find;
2946135446Strhodes		do {
2947135446Strhodes			for (addrinfo = ISC_LIST_HEAD(find->list);
2948135446Strhodes			     addrinfo != NULL;
2949135446Strhodes			     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2950135446Strhodes				if (!UNMARKED(addrinfo))
2951135446Strhodes					continue;
2952135446Strhodes				possibly_mark(fctx, addrinfo);
2953135446Strhodes				if (UNMARKED(addrinfo)) {
2954135446Strhodes					addrinfo->flags |= FCTX_ADDRINFO_MARK;
2955135446Strhodes					break;
2956135446Strhodes				}
2957135446Strhodes			}
2958135446Strhodes			if (addrinfo != NULL)
2959135446Strhodes				break;
2960135446Strhodes			find = ISC_LIST_NEXT(find, publink);
2961135446Strhodes			if (find == NULL)
2962135446Strhodes				find = ISC_LIST_HEAD(fctx->finds);
2963135446Strhodes		} while (find != start);
2964135446Strhodes	}
2965135446Strhodes
2966135446Strhodes	fctx->find = find;
2967135446Strhodes	if (addrinfo != NULL)
2968135446Strhodes		return (addrinfo);
2969135446Strhodes
2970135446Strhodes	/*
2971135446Strhodes	 * No nameservers left.  Try alternates.
2972135446Strhodes	 */
2973135446Strhodes
2974135446Strhodes	fctx->attributes |= FCTX_ATTR_TRIEDALT;
2975135446Strhodes
2976135446Strhodes	find = fctx->altfind;
2977135446Strhodes	if (find == NULL)
2978135446Strhodes		find = ISC_LIST_HEAD(fctx->altfinds);
2979135446Strhodes	else {
2980135446Strhodes		find = ISC_LIST_NEXT(find, publink);
2981135446Strhodes		if (find == NULL)
2982135446Strhodes			find = ISC_LIST_HEAD(fctx->altfinds);
2983135446Strhodes	}
2984135446Strhodes
2985135446Strhodes	/*
2986135446Strhodes	 * Find the first unmarked addrinfo.
2987135446Strhodes	 */
2988135446Strhodes	addrinfo = NULL;
2989135446Strhodes	if (find != NULL) {
2990135446Strhodes		start = find;
2991135446Strhodes		do {
2992135446Strhodes			for (addrinfo = ISC_LIST_HEAD(find->list);
2993135446Strhodes			     addrinfo != NULL;
2994135446Strhodes			     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
2995135446Strhodes				if (!UNMARKED(addrinfo))
2996135446Strhodes					continue;
2997135446Strhodes				possibly_mark(fctx, addrinfo);
2998135446Strhodes				if (UNMARKED(addrinfo)) {
2999135446Strhodes					addrinfo->flags |= FCTX_ADDRINFO_MARK;
3000135446Strhodes					break;
3001135446Strhodes				}
3002135446Strhodes			}
3003135446Strhodes			if (addrinfo != NULL)
3004135446Strhodes				break;
3005135446Strhodes			find = ISC_LIST_NEXT(find, publink);
3006135446Strhodes			if (find == NULL)
3007135446Strhodes				find = ISC_LIST_HEAD(fctx->altfinds);
3008135446Strhodes		} while (find != start);
3009135446Strhodes	}
3010135446Strhodes
3011135446Strhodes	faddrinfo = addrinfo;
3012135446Strhodes
3013135446Strhodes	/*
3014135446Strhodes	 * See if we have a better alternate server by address.
3015135446Strhodes	 */
3016135446Strhodes
3017135446Strhodes	for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
3018135446Strhodes	     addrinfo != NULL;
3019135446Strhodes	     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
3020135446Strhodes		if (!UNMARKED(addrinfo))
3021135446Strhodes			continue;
3022135446Strhodes		possibly_mark(fctx, addrinfo);
3023135446Strhodes		if (UNMARKED(addrinfo) &&
3024135446Strhodes		    (faddrinfo == NULL ||
3025135446Strhodes		     addrinfo->srtt < faddrinfo->srtt)) {
3026135446Strhodes			if (faddrinfo != NULL)
3027135446Strhodes				faddrinfo->flags &= ~FCTX_ADDRINFO_MARK;
3028135446Strhodes			addrinfo->flags |= FCTX_ADDRINFO_MARK;
3029135446Strhodes			break;
3030135446Strhodes		}
3031135446Strhodes	}
3032135446Strhodes
3033135446Strhodes	if (addrinfo == NULL) {
3034135446Strhodes		addrinfo = faddrinfo;
3035135446Strhodes		fctx->altfind = find;
3036135446Strhodes	}
3037135446Strhodes
3038135446Strhodes	return (addrinfo);
3039135446Strhodes}
3040135446Strhodes
3041135446Strhodesstatic void
3042205292Sdougbfctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
3043135446Strhodes	isc_result_t result;
3044135446Strhodes	dns_adbaddrinfo_t *addrinfo;
3045135446Strhodes
3046135446Strhodes	FCTXTRACE("try");
3047135446Strhodes
3048135446Strhodes	REQUIRE(!ADDRWAIT(fctx));
3049135446Strhodes
3050275672Sdelphij	if (fctx->totalqueries > DEFAULT_MAX_QUERIES)
3051275672Sdelphij		fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
3052275672Sdelphij
3053135446Strhodes	addrinfo = fctx_nextaddress(fctx);
3054135446Strhodes	if (addrinfo == NULL) {
3055135446Strhodes		/*
3056135446Strhodes		 * We have no more addresses.  Start over.
3057135446Strhodes		 */
3058135446Strhodes		fctx_cancelqueries(fctx, ISC_TRUE);
3059135446Strhodes		fctx_cleanupfinds(fctx);
3060135446Strhodes		fctx_cleanupaltfinds(fctx);
3061135446Strhodes		fctx_cleanupforwaddrs(fctx);
3062135446Strhodes		fctx_cleanupaltaddrs(fctx);
3063205292Sdougb		result = fctx_getaddresses(fctx, badcache);
3064135446Strhodes		if (result == DNS_R_WAIT) {
3065135446Strhodes			/*
3066135446Strhodes			 * Sleep waiting for addresses.
3067135446Strhodes			 */
3068135446Strhodes			FCTXTRACE("addrwait");
3069135446Strhodes			fctx->attributes |= FCTX_ATTR_ADDRWAIT;
3070135446Strhodes			return;
3071135446Strhodes		} else if (result != ISC_R_SUCCESS) {
3072135446Strhodes			/*
3073135446Strhodes			 * Something bad happened.
3074135446Strhodes			 */
3075193149Sdougb			fctx_done(fctx, result, __LINE__);
3076135446Strhodes			return;
3077135446Strhodes		}
3078135446Strhodes
3079135446Strhodes		addrinfo = fctx_nextaddress(fctx);
3080135446Strhodes		/*
3081135446Strhodes		 * While we may have addresses from the ADB, they
3082135446Strhodes		 * might be bad ones.  In this case, return SERVFAIL.
3083135446Strhodes		 */
3084135446Strhodes		if (addrinfo == NULL) {
3085193149Sdougb			fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
3086135446Strhodes			return;
3087135446Strhodes		}
3088135446Strhodes	}
3089135446Strhodes
3090135446Strhodes	result = fctx_query(fctx, addrinfo, fctx->options);
3091135446Strhodes	if (result != ISC_R_SUCCESS)
3092193149Sdougb		fctx_done(fctx, result, __LINE__);
3093193149Sdougb	else if (retrying)
3094193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_retry);
3095135446Strhodes}
3096135446Strhodes
3097135446Strhodesstatic isc_boolean_t
3098236374Sdougbfctx_unlink(fetchctx_t *fctx) {
3099135446Strhodes	dns_resolver_t *res;
3100135446Strhodes	unsigned int bucketnum;
3101135446Strhodes
3102135446Strhodes	/*
3103135446Strhodes	 * Caller must be holding the bucket lock.
3104135446Strhodes	 */
3105135446Strhodes
3106135446Strhodes	REQUIRE(VALID_FCTX(fctx));
3107135446Strhodes	REQUIRE(fctx->state == fetchstate_done ||
3108135446Strhodes		fctx->state == fetchstate_init);
3109135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->events));
3110135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
3111135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->finds));
3112135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
3113135446Strhodes	REQUIRE(fctx->pending == 0);
3114166332Sdougb	REQUIRE(fctx->references == 0);
3115135446Strhodes	REQUIRE(ISC_LIST_EMPTY(fctx->validators));
3116135446Strhodes
3117236374Sdougb	FCTXTRACE("unlink");
3118135446Strhodes
3119135446Strhodes	res = fctx->res;
3120135446Strhodes	bucketnum = fctx->bucketnum;
3121135446Strhodes
3122135446Strhodes	ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
3123135446Strhodes
3124236374Sdougb	LOCK(&res->nlock);
3125236374Sdougb	res->nfctx--;
3126236374Sdougb	UNLOCK(&res->nlock);
3127236374Sdougb
3128236374Sdougb	if (res->buckets[bucketnum].exiting &&
3129236374Sdougb	    ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs))
3130236374Sdougb		return (ISC_TRUE);
3131236374Sdougb
3132236374Sdougb	return (ISC_FALSE);
3133236374Sdougb}
3134236374Sdougb
3135236374Sdougbstatic void
3136236374Sdougbfctx_destroy(fetchctx_t *fctx) {
3137236374Sdougb	isc_sockaddr_t *sa, *next_sa;
3138236374Sdougb
3139236374Sdougb	REQUIRE(VALID_FCTX(fctx));
3140236374Sdougb	REQUIRE(fctx->state == fetchstate_done ||
3141236374Sdougb		fctx->state == fetchstate_init);
3142236374Sdougb	REQUIRE(ISC_LIST_EMPTY(fctx->events));
3143236374Sdougb	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
3144236374Sdougb	REQUIRE(ISC_LIST_EMPTY(fctx->finds));
3145236374Sdougb	REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
3146236374Sdougb	REQUIRE(fctx->pending == 0);
3147236374Sdougb	REQUIRE(fctx->references == 0);
3148236374Sdougb	REQUIRE(ISC_LIST_EMPTY(fctx->validators));
3149236374Sdougb	REQUIRE(!ISC_LINK_LINKED(fctx, link));
3150236374Sdougb
3151236374Sdougb	FCTXTRACE("destroy");
3152236374Sdougb
3153135446Strhodes	/*
3154135446Strhodes	 * Free bad.
3155135446Strhodes	 */
3156135446Strhodes	for (sa = ISC_LIST_HEAD(fctx->bad);
3157135446Strhodes	     sa != NULL;
3158135446Strhodes	     sa = next_sa) {
3159135446Strhodes		next_sa = ISC_LIST_NEXT(sa, link);
3160135446Strhodes		ISC_LIST_UNLINK(fctx->bad, sa, link);
3161236374Sdougb		isc_mem_put(fctx->mctx, sa, sizeof(*sa));
3162135446Strhodes	}
3163135446Strhodes
3164170222Sdougb	for (sa = ISC_LIST_HEAD(fctx->edns);
3165170222Sdougb	     sa != NULL;
3166170222Sdougb	     sa = next_sa) {
3167170222Sdougb		next_sa = ISC_LIST_NEXT(sa, link);
3168170222Sdougb		ISC_LIST_UNLINK(fctx->edns, sa, link);
3169236374Sdougb		isc_mem_put(fctx->mctx, sa, sizeof(*sa));
3170170222Sdougb	}
3171170222Sdougb
3172170222Sdougb	for (sa = ISC_LIST_HEAD(fctx->edns512);
3173170222Sdougb	     sa != NULL;
3174170222Sdougb	     sa = next_sa) {
3175170222Sdougb		next_sa = ISC_LIST_NEXT(sa, link);
3176170222Sdougb		ISC_LIST_UNLINK(fctx->edns512, sa, link);
3177236374Sdougb		isc_mem_put(fctx->mctx, sa, sizeof(*sa));
3178170222Sdougb	}
3179170222Sdougb
3180218384Sdougb	for (sa = ISC_LIST_HEAD(fctx->bad_edns);
3181218384Sdougb	     sa != NULL;
3182218384Sdougb	     sa = next_sa) {
3183218384Sdougb		next_sa = ISC_LIST_NEXT(sa, link);
3184218384Sdougb		ISC_LIST_UNLINK(fctx->bad_edns, sa, link);
3185236374Sdougb		isc_mem_put(fctx->mctx, sa, sizeof(*sa));
3186218384Sdougb	}
3187218384Sdougb
3188135446Strhodes	isc_timer_detach(&fctx->timer);
3189135446Strhodes	dns_message_destroy(&fctx->rmessage);
3190135446Strhodes	dns_message_destroy(&fctx->qmessage);
3191135446Strhodes	if (dns_name_countlabels(&fctx->domain) > 0)
3192236374Sdougb		dns_name_free(&fctx->domain, fctx->mctx);
3193135446Strhodes	if (dns_rdataset_isassociated(&fctx->nameservers))
3194135446Strhodes		dns_rdataset_disassociate(&fctx->nameservers);
3195236374Sdougb	dns_name_free(&fctx->name, fctx->mctx);
3196135446Strhodes	dns_db_detach(&fctx->cache);
3197135446Strhodes	dns_adb_detach(&fctx->adb);
3198236374Sdougb	isc_mem_free(fctx->mctx, fctx->info);
3199236374Sdougb	isc_mem_putanddetach(&fctx->mctx, fctx, sizeof(*fctx));
3200135446Strhodes}
3201135446Strhodes
3202135446Strhodes/*
3203135446Strhodes * Fetch event handlers.
3204135446Strhodes */
3205135446Strhodes
3206135446Strhodesstatic void
3207135446Strhodesfctx_timeout(isc_task_t *task, isc_event_t *event) {
3208135446Strhodes	fetchctx_t *fctx = event->ev_arg;
3209182645Sdougb	isc_timerevent_t *tevent = (isc_timerevent_t *)event;
3210182645Sdougb	resquery_t *query;
3211135446Strhodes
3212135446Strhodes	REQUIRE(VALID_FCTX(fctx));
3213135446Strhodes
3214135446Strhodes	UNUSED(task);
3215135446Strhodes
3216135446Strhodes	FCTXTRACE("timeout");
3217135446Strhodes
3218193149Sdougb	inc_stats(fctx->res, dns_resstatscounter_querytimeout);
3219193149Sdougb
3220135446Strhodes	if (event->ev_type == ISC_TIMEREVENT_LIFE) {
3221193149Sdougb		fctx->reason = NULL;
3222193149Sdougb		fctx_done(fctx, ISC_R_TIMEDOUT, __LINE__);
3223135446Strhodes	} else {
3224135446Strhodes		isc_result_t result;
3225135446Strhodes
3226135446Strhodes		fctx->timeouts++;
3227193149Sdougb		fctx->timeout = ISC_TRUE;
3228135446Strhodes		/*
3229135446Strhodes		 * We could cancel the running queries here, or we could let
3230182645Sdougb		 * them keep going.  Since we normally use separate sockets for
3231182645Sdougb		 * different queries, we adopt the former approach to reduce
3232182645Sdougb		 * the number of open sockets: cancel the oldest query if it
3233182645Sdougb		 * expired after the query had started (this is usually the
3234182645Sdougb		 * case but is not always so, depending on the task schedule
3235182645Sdougb		 * timing).
3236135446Strhodes		 */
3237182645Sdougb		query = ISC_LIST_HEAD(fctx->queries);
3238182645Sdougb		if (query != NULL &&
3239182645Sdougb		    isc_time_compare(&tevent->due, &query->start) >= 0) {
3240182645Sdougb			fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
3241182645Sdougb		}
3242135446Strhodes		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
3243135446Strhodes		/*
3244135446Strhodes		 * Our timer has triggered.  Reestablish the fctx lifetime
3245135446Strhodes		 * timer.
3246135446Strhodes		 */
3247135446Strhodes		result = fctx_starttimer(fctx);
3248135446Strhodes		if (result != ISC_R_SUCCESS)
3249193149Sdougb			fctx_done(fctx, result, __LINE__);
3250135446Strhodes		else
3251135446Strhodes			/*
3252135446Strhodes			 * Keep trying.
3253135446Strhodes			 */
3254205292Sdougb			fctx_try(fctx, ISC_TRUE, ISC_FALSE);
3255135446Strhodes	}
3256135446Strhodes
3257135446Strhodes	isc_event_free(&event);
3258135446Strhodes}
3259135446Strhodes
3260135446Strhodesstatic void
3261135446Strhodesfctx_shutdown(fetchctx_t *fctx) {
3262135446Strhodes	isc_event_t *cevent;
3263135446Strhodes
3264135446Strhodes	/*
3265135446Strhodes	 * Start the shutdown process for fctx, if it isn't already underway.
3266135446Strhodes	 */
3267135446Strhodes
3268135446Strhodes	FCTXTRACE("shutdown");
3269135446Strhodes
3270135446Strhodes	/*
3271135446Strhodes	 * The caller must be holding the appropriate bucket lock.
3272135446Strhodes	 */
3273135446Strhodes
3274135446Strhodes	if (fctx->want_shutdown)
3275135446Strhodes		return;
3276135446Strhodes
3277135446Strhodes	fctx->want_shutdown = ISC_TRUE;
3278135446Strhodes
3279135446Strhodes	/*
3280135446Strhodes	 * Unless we're still initializing (in which case the
3281135446Strhodes	 * control event is still outstanding), we need to post
3282135446Strhodes	 * the control event to tell the fetch we want it to
3283135446Strhodes	 * exit.
3284135446Strhodes	 */
3285135446Strhodes	if (fctx->state != fetchstate_init) {
3286135446Strhodes		cevent = &fctx->control_event;
3287135446Strhodes		isc_task_send(fctx->res->buckets[fctx->bucketnum].task,
3288135446Strhodes			      &cevent);
3289135446Strhodes	}
3290135446Strhodes}
3291135446Strhodes
3292135446Strhodesstatic void
3293135446Strhodesfctx_doshutdown(isc_task_t *task, isc_event_t *event) {
3294135446Strhodes	fetchctx_t *fctx = event->ev_arg;
3295135446Strhodes	isc_boolean_t bucket_empty = ISC_FALSE;
3296135446Strhodes	dns_resolver_t *res;
3297135446Strhodes	unsigned int bucketnum;
3298135446Strhodes	dns_validator_t *validator;
3299236374Sdougb	isc_boolean_t destroy = ISC_FALSE;
3300135446Strhodes
3301135446Strhodes	REQUIRE(VALID_FCTX(fctx));
3302135446Strhodes
3303135446Strhodes	UNUSED(task);
3304135446Strhodes
3305135446Strhodes	res = fctx->res;
3306135446Strhodes	bucketnum = fctx->bucketnum;
3307135446Strhodes
3308135446Strhodes	FCTXTRACE("doshutdown");
3309135446Strhodes
3310135446Strhodes	/*
3311135446Strhodes	 * An fctx that is shutting down is no longer in ADDRWAIT mode.
3312135446Strhodes	 */
3313135446Strhodes	fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
3314135446Strhodes
3315135446Strhodes	/*
3316135446Strhodes	 * Cancel all pending validators.  Note that this must be done
3317135446Strhodes	 * without the bucket lock held, since that could cause deadlock.
3318135446Strhodes	 */
3319135446Strhodes	validator = ISC_LIST_HEAD(fctx->validators);
3320135446Strhodes	while (validator != NULL) {
3321135446Strhodes		dns_validator_cancel(validator);
3322135446Strhodes		validator = ISC_LIST_NEXT(validator, link);
3323135446Strhodes	}
3324186462Sdougb
3325135446Strhodes	if (fctx->nsfetch != NULL)
3326135446Strhodes		dns_resolver_cancelfetch(fctx->nsfetch);
3327135446Strhodes
3328135446Strhodes	/*
3329135446Strhodes	 * Shut down anything that is still running on behalf of this
3330135446Strhodes	 * fetch.  To avoid deadlock with the ADB, we must do this
3331135446Strhodes	 * before we lock the bucket lock.
3332135446Strhodes	 */
3333135446Strhodes	fctx_stopeverything(fctx, ISC_FALSE);
3334135446Strhodes
3335135446Strhodes	LOCK(&res->buckets[bucketnum].lock);
3336135446Strhodes
3337135446Strhodes	fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;
3338135446Strhodes
3339135446Strhodes	INSIST(fctx->state == fetchstate_active ||
3340135446Strhodes	       fctx->state == fetchstate_done);
3341135446Strhodes	INSIST(fctx->want_shutdown);
3342135446Strhodes
3343135446Strhodes	if (fctx->state != fetchstate_done) {
3344135446Strhodes		fctx->state = fetchstate_done;
3345193149Sdougb		fctx_sendevents(fctx, ISC_R_CANCELED, __LINE__);
3346135446Strhodes	}
3347135446Strhodes
3348135446Strhodes	if (fctx->references == 0 && fctx->pending == 0 &&
3349236374Sdougb	    fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) {
3350236374Sdougb		bucket_empty = fctx_unlink(fctx);
3351236374Sdougb		destroy = ISC_TRUE;
3352236374Sdougb	}
3353135446Strhodes
3354135446Strhodes	UNLOCK(&res->buckets[bucketnum].lock);
3355135446Strhodes
3356236374Sdougb	if (destroy) {
3357236374Sdougb		fctx_destroy(fctx);
3358236374Sdougb		if (bucket_empty)
3359236374Sdougb			empty_bucket(res);
3360236374Sdougb	}
3361135446Strhodes}
3362135446Strhodes
3363135446Strhodesstatic void
3364135446Strhodesfctx_start(isc_task_t *task, isc_event_t *event) {
3365135446Strhodes	fetchctx_t *fctx = event->ev_arg;
3366135446Strhodes	isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE;
3367135446Strhodes	dns_resolver_t *res;
3368135446Strhodes	unsigned int bucketnum;
3369236374Sdougb	isc_boolean_t destroy = ISC_FALSE;
3370135446Strhodes
3371135446Strhodes	REQUIRE(VALID_FCTX(fctx));
3372135446Strhodes
3373135446Strhodes	UNUSED(task);
3374135446Strhodes
3375135446Strhodes	res = fctx->res;
3376135446Strhodes	bucketnum = fctx->bucketnum;
3377135446Strhodes
3378135446Strhodes	FCTXTRACE("start");
3379135446Strhodes
3380135446Strhodes	LOCK(&res->buckets[bucketnum].lock);
3381135446Strhodes
3382135446Strhodes	INSIST(fctx->state == fetchstate_init);
3383135446Strhodes	if (fctx->want_shutdown) {
3384135446Strhodes		/*
3385135446Strhodes		 * We haven't started this fctx yet, and we've been requested
3386135446Strhodes		 * to shut it down.
3387135446Strhodes		 */
3388135446Strhodes		fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;
3389135446Strhodes		fctx->state = fetchstate_done;
3390193149Sdougb		fctx_sendevents(fctx, ISC_R_CANCELED, __LINE__);
3391135446Strhodes		/*
3392135446Strhodes		 * Since we haven't started, we INSIST that we have no
3393135446Strhodes		 * pending ADB finds and no pending validations.
3394135446Strhodes		 */
3395135446Strhodes		INSIST(fctx->pending == 0);
3396166332Sdougb		INSIST(fctx->nqueries == 0);
3397135446Strhodes		INSIST(ISC_LIST_EMPTY(fctx->validators));
3398135446Strhodes		if (fctx->references == 0) {
3399135446Strhodes			/*
3400135446Strhodes			 * It's now safe to destroy this fctx.
3401135446Strhodes			 */
3402236374Sdougb			bucket_empty = fctx_unlink(fctx);
3403236374Sdougb			destroy = ISC_TRUE;
3404135446Strhodes		}
3405135446Strhodes		done = ISC_TRUE;
3406135446Strhodes	} else {
3407135446Strhodes		/*
3408135446Strhodes		 * Normal fctx startup.
3409135446Strhodes		 */
3410135446Strhodes		fctx->state = fetchstate_active;
3411275672Sdelphij		fctx->totalqueries = 0;
3412135446Strhodes		/*
3413135446Strhodes		 * Reset the control event for later use in shutting down
3414135446Strhodes		 * the fctx.
3415135446Strhodes		 */
3416135446Strhodes		ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
3417135446Strhodes			       DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx,
3418135446Strhodes			       NULL, NULL, NULL);
3419135446Strhodes	}
3420135446Strhodes
3421135446Strhodes	UNLOCK(&res->buckets[bucketnum].lock);
3422135446Strhodes
3423135446Strhodes	if (!done) {
3424135446Strhodes		isc_result_t result;
3425135446Strhodes
3426236374Sdougb		INSIST(!destroy);
3427236374Sdougb
3428135446Strhodes		/*
3429135446Strhodes		 * All is well.  Start working on the fetch.
3430135446Strhodes		 */
3431135446Strhodes		result = fctx_starttimer(fctx);
3432135446Strhodes		if (result != ISC_R_SUCCESS)
3433193149Sdougb			fctx_done(fctx, result, __LINE__);
3434135446Strhodes		else
3435205292Sdougb			fctx_try(fctx, ISC_FALSE, ISC_FALSE);
3436236374Sdougb	} else if (destroy) {
3437236374Sdougb			fctx_destroy(fctx);
3438236374Sdougb		if (bucket_empty)
3439236374Sdougb			empty_bucket(res);
3440236374Sdougb	}
3441135446Strhodes}
3442135446Strhodes
3443135446Strhodes/*
3444135446Strhodes * Fetch Creation, Joining, and Cancelation.
3445135446Strhodes */
3446135446Strhodes
3447135446Strhodesstatic inline isc_result_t
3448170222Sdougbfctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client,
3449170222Sdougb	  dns_messageid_t id, isc_taskaction_t action, void *arg,
3450170222Sdougb	  dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
3451135446Strhodes	  dns_fetch_t *fetch)
3452135446Strhodes{
3453135446Strhodes	isc_task_t *clone;
3454135446Strhodes	dns_fetchevent_t *event;
3455135446Strhodes
3456135446Strhodes	FCTXTRACE("join");
3457135446Strhodes
3458135446Strhodes	/*
3459135446Strhodes	 * We store the task we're going to send this event to in the
3460135446Strhodes	 * sender field.  We'll make the fetch the sender when we actually
3461135446Strhodes	 * send the event.
3462135446Strhodes	 */
3463135446Strhodes	clone = NULL;
3464135446Strhodes	isc_task_attach(task, &clone);
3465135446Strhodes	event = (dns_fetchevent_t *)
3466170222Sdougb		isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE,
3467135446Strhodes				   action, arg, sizeof(*event));
3468135446Strhodes	if (event == NULL) {
3469135446Strhodes		isc_task_detach(&clone);
3470135446Strhodes		return (ISC_R_NOMEMORY);
3471135446Strhodes	}
3472135446Strhodes	event->result = DNS_R_SERVFAIL;
3473135446Strhodes	event->qtype = fctx->type;
3474135446Strhodes	event->db = NULL;
3475135446Strhodes	event->node = NULL;
3476135446Strhodes	event->rdataset = rdataset;
3477135446Strhodes	event->sigrdataset = sigrdataset;
3478135446Strhodes	event->fetch = fetch;
3479170222Sdougb	event->client = client;
3480170222Sdougb	event->id = id;
3481275672Sdelphij	event->qtotal = 0;
3482135446Strhodes	dns_fixedname_init(&event->foundname);
3483135446Strhodes
3484135446Strhodes	/*
3485135446Strhodes	 * Make sure that we can store the sigrdataset in the
3486135446Strhodes	 * first event if it is needed by any of the events.
3487135446Strhodes	 */
3488135446Strhodes	if (event->sigrdataset != NULL)
3489135446Strhodes		ISC_LIST_PREPEND(fctx->events, event, ev_link);
3490135446Strhodes	else
3491135446Strhodes		ISC_LIST_APPEND(fctx->events, event, ev_link);
3492135446Strhodes	fctx->references++;
3493224092Sdougb	fctx->client = client;
3494135446Strhodes
3495135446Strhodes	fetch->magic = DNS_FETCH_MAGIC;
3496135446Strhodes	fetch->private = fctx;
3497135446Strhodes
3498135446Strhodes	return (ISC_R_SUCCESS);
3499135446Strhodes}
3500135446Strhodes
3501234010Sdougbstatic inline void
3502234010Sdougblog_ns_ttl(fetchctx_t *fctx, const char *where) {
3503234010Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
3504234010Sdougb	char domainbuf[DNS_NAME_FORMATSIZE];
3505234010Sdougb
3506234010Sdougb	dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
3507234010Sdougb	dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
3508234010Sdougb	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
3509234010Sdougb		      DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10),
3510234010Sdougb		      "log_ns_ttl: fctx %p: %s: %s (in '%s'?): %u %u",
3511234010Sdougb		      fctx, where, namebuf, domainbuf,
3512234010Sdougb		      fctx->ns_ttl_ok, fctx->ns_ttl);
3513234010Sdougb}
3514234010Sdougb
3515135446Strhodesstatic isc_result_t
3516135446Strhodesfctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
3517135446Strhodes	    dns_name_t *domain, dns_rdataset_t *nameservers,
3518275672Sdelphij	    unsigned int options, unsigned int bucketnum, unsigned int depth,
3519275672Sdelphij	    fetchctx_t **fctxp)
3520135446Strhodes{
3521135446Strhodes	fetchctx_t *fctx;
3522149245Sdougb	isc_result_t result;
3523135446Strhodes	isc_result_t iresult;
3524135446Strhodes	isc_interval_t interval;
3525153816Sdougb	dns_fixedname_t fixed;
3526135446Strhodes	unsigned int findoptions = 0;
3527135446Strhodes	char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE];
3528135446Strhodes	char typebuf[DNS_RDATATYPE_FORMATSIZE];
3529135446Strhodes	dns_name_t suffix;
3530236374Sdougb	isc_mem_t *mctx;
3531135446Strhodes
3532135446Strhodes	/*
3533135446Strhodes	 * Caller must be holding the lock for bucket number 'bucketnum'.
3534135446Strhodes	 */
3535135446Strhodes	REQUIRE(fctxp != NULL && *fctxp == NULL);
3536135446Strhodes
3537236374Sdougb	mctx = res->buckets[bucketnum].mctx;
3538236374Sdougb	fctx = isc_mem_get(mctx, sizeof(*fctx));
3539135446Strhodes	if (fctx == NULL)
3540135446Strhodes		return (ISC_R_NOMEMORY);
3541135446Strhodes	dns_name_format(name, buf, sizeof(buf));
3542135446Strhodes	dns_rdatatype_format(type, typebuf, sizeof(typebuf));
3543193149Sdougb	strcat(buf, "/");       /* checked */
3544193149Sdougb	strcat(buf, typebuf);   /* checked */
3545236374Sdougb	fctx->info = isc_mem_strdup(mctx, buf);
3546149245Sdougb	if (fctx->info == NULL) {
3547149245Sdougb		result = ISC_R_NOMEMORY;
3548135446Strhodes		goto cleanup_fetch;
3549149245Sdougb	}
3550135446Strhodes	FCTXTRACE("create");
3551135446Strhodes	dns_name_init(&fctx->name, NULL);
3552236374Sdougb	result = dns_name_dup(name, mctx, &fctx->name);
3553135446Strhodes	if (result != ISC_R_SUCCESS)
3554135446Strhodes		goto cleanup_info;
3555135446Strhodes	dns_name_init(&fctx->domain, NULL);
3556135446Strhodes	dns_rdataset_init(&fctx->nameservers);
3557135446Strhodes
3558135446Strhodes	fctx->type = type;
3559135446Strhodes	fctx->options = options;
3560135446Strhodes	/*
3561135446Strhodes	 * Note!  We do not attach to the task.  We are relying on the
3562135446Strhodes	 * resolver to ensure that this task doesn't go away while we are
3563135446Strhodes	 * using it.
3564135446Strhodes	 */
3565135446Strhodes	fctx->res = res;
3566135446Strhodes	fctx->references = 0;
3567135446Strhodes	fctx->bucketnum = bucketnum;
3568135446Strhodes	fctx->state = fetchstate_init;
3569135446Strhodes	fctx->want_shutdown = ISC_FALSE;
3570135446Strhodes	fctx->cloned = ISC_FALSE;
3571275672Sdelphij	fctx->depth = depth;
3572135446Strhodes	ISC_LIST_INIT(fctx->queries);
3573135446Strhodes	ISC_LIST_INIT(fctx->finds);
3574135446Strhodes	ISC_LIST_INIT(fctx->altfinds);
3575135446Strhodes	ISC_LIST_INIT(fctx->forwaddrs);
3576135446Strhodes	ISC_LIST_INIT(fctx->altaddrs);
3577135446Strhodes	ISC_LIST_INIT(fctx->forwarders);
3578135446Strhodes	fctx->fwdpolicy = dns_fwdpolicy_none;
3579135446Strhodes	ISC_LIST_INIT(fctx->bad);
3580170222Sdougb	ISC_LIST_INIT(fctx->edns);
3581170222Sdougb	ISC_LIST_INIT(fctx->edns512);
3582218384Sdougb	ISC_LIST_INIT(fctx->bad_edns);
3583135446Strhodes	ISC_LIST_INIT(fctx->validators);
3584174187Sdougb	fctx->validator = NULL;
3585135446Strhodes	fctx->find = NULL;
3586135446Strhodes	fctx->altfind = NULL;
3587135446Strhodes	fctx->pending = 0;
3588135446Strhodes	fctx->restarts = 0;
3589193149Sdougb	fctx->querysent = 0;
3590275672Sdelphij	fctx->totalqueries = 0;
3591193149Sdougb	fctx->referrals = 0;
3592193149Sdougb	TIME_NOW(&fctx->start);
3593135446Strhodes	fctx->timeouts = 0;
3594193149Sdougb	fctx->lamecount = 0;
3595193149Sdougb	fctx->adberr = 0;
3596193149Sdougb	fctx->neterr = 0;
3597193149Sdougb	fctx->badresp = 0;
3598193149Sdougb	fctx->findfail = 0;
3599193149Sdougb	fctx->valfail = 0;
3600193149Sdougb	fctx->result = ISC_R_FAILURE;
3601193149Sdougb	fctx->vresult = ISC_R_SUCCESS;
3602193149Sdougb	fctx->exitline = -1;	/* sentinel */
3603193149Sdougb	fctx->logged = ISC_FALSE;
3604135446Strhodes	fctx->attributes = 0;
3605170222Sdougb	fctx->spilled = ISC_FALSE;
3606166332Sdougb	fctx->nqueries = 0;
3607193149Sdougb	fctx->reason = NULL;
3608193149Sdougb	fctx->rand_buf = 0;
3609193149Sdougb	fctx->rand_bits = 0;
3610193149Sdougb	fctx->timeout = ISC_FALSE;
3611224092Sdougb	fctx->addrinfo = NULL;
3612224092Sdougb	fctx->client = NULL;
3613234010Sdougb	fctx->ns_ttl = 0;
3614234010Sdougb	fctx->ns_ttl_ok = ISC_FALSE;
3615135446Strhodes
3616135446Strhodes	dns_name_init(&fctx->nsname, NULL);
3617135446Strhodes	fctx->nsfetch = NULL;
3618135446Strhodes	dns_rdataset_init(&fctx->nsrrset);
3619135446Strhodes
3620135446Strhodes	if (domain == NULL) {
3621135446Strhodes		dns_forwarders_t *forwarders = NULL;
3622135446Strhodes		unsigned int labels;
3623204619Sdougb		dns_name_t *fwdname = name;
3624135446Strhodes
3625135446Strhodes		/*
3626135446Strhodes		 * DS records are found in the parent server.
3627135446Strhodes		 * Strip label to get the correct forwarder (if any).
3628135446Strhodes		 */
3629204619Sdougb		if (dns_rdatatype_atparent(fctx->type) &&
3630135446Strhodes		    dns_name_countlabels(name) > 1) {
3631135446Strhodes			dns_name_init(&suffix, NULL);
3632135446Strhodes			labels = dns_name_countlabels(name);
3633135446Strhodes			dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
3634204619Sdougb			fwdname = &suffix;
3635135446Strhodes		}
3636153816Sdougb		dns_fixedname_init(&fixed);
3637153816Sdougb		domain = dns_fixedname_name(&fixed);
3638204619Sdougb		result = dns_fwdtable_find2(fctx->res->view->fwdtable, fwdname,
3639153816Sdougb					    domain, &forwarders);
3640135446Strhodes		if (result == ISC_R_SUCCESS)
3641135446Strhodes			fctx->fwdpolicy = forwarders->fwdpolicy;
3642135446Strhodes
3643135446Strhodes		if (fctx->fwdpolicy != dns_fwdpolicy_only) {
3644135446Strhodes			/*
3645135446Strhodes			 * The caller didn't supply a query domain and
3646135446Strhodes			 * nameservers, and we're not in forward-only mode,
3647135446Strhodes			 * so find the best nameservers to use.
3648135446Strhodes			 */
3649204619Sdougb			if (dns_rdatatype_atparent(fctx->type))
3650135446Strhodes				findoptions |= DNS_DBFIND_NOEXACT;
3651262706Serwin			result = dns_view_findzonecut(res->view, fwdname,
3652262706Serwin						      domain, 0, findoptions,
3653262706Serwin						      ISC_TRUE,
3654135446Strhodes						      &fctx->nameservers,
3655135446Strhodes						      NULL);
3656135446Strhodes			if (result != ISC_R_SUCCESS)
3657135446Strhodes				goto cleanup_name;
3658262706Serwin
3659236374Sdougb			result = dns_name_dup(domain, mctx, &fctx->domain);
3660135446Strhodes			if (result != ISC_R_SUCCESS) {
3661135446Strhodes				dns_rdataset_disassociate(&fctx->nameservers);
3662135446Strhodes				goto cleanup_name;
3663135446Strhodes			}
3664234010Sdougb			fctx->ns_ttl = fctx->nameservers.ttl;
3665234010Sdougb			fctx->ns_ttl_ok = ISC_TRUE;
3666135446Strhodes		} else {
3667135446Strhodes			/*
3668153816Sdougb			 * We're in forward-only mode.  Set the query domain.
3669135446Strhodes			 */
3670236374Sdougb			result = dns_name_dup(domain, mctx, &fctx->domain);
3671135446Strhodes			if (result != ISC_R_SUCCESS)
3672135446Strhodes				goto cleanup_name;
3673135446Strhodes		}
3674135446Strhodes	} else {
3675236374Sdougb		result = dns_name_dup(domain, mctx, &fctx->domain);
3676135446Strhodes		if (result != ISC_R_SUCCESS)
3677135446Strhodes			goto cleanup_name;
3678135446Strhodes		dns_rdataset_clone(nameservers, &fctx->nameservers);
3679234010Sdougb		fctx->ns_ttl = fctx->nameservers.ttl;
3680234010Sdougb		fctx->ns_ttl_ok = ISC_TRUE;
3681135446Strhodes	}
3682135446Strhodes
3683234010Sdougb	log_ns_ttl(fctx, "fctx_create");
3684234010Sdougb
3685135446Strhodes	INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
3686135446Strhodes
3687135446Strhodes	fctx->qmessage = NULL;
3688236374Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
3689135446Strhodes				    &fctx->qmessage);
3690135446Strhodes
3691135446Strhodes	if (result != ISC_R_SUCCESS)
3692135446Strhodes		goto cleanup_domain;
3693135446Strhodes
3694135446Strhodes	fctx->rmessage = NULL;
3695236374Sdougb	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
3696135446Strhodes				    &fctx->rmessage);
3697135446Strhodes
3698135446Strhodes	if (result != ISC_R_SUCCESS)
3699135446Strhodes		goto cleanup_qmessage;
3700135446Strhodes
3701135446Strhodes	/*
3702135446Strhodes	 * Compute an expiration time for the entire fetch.
3703135446Strhodes	 */
3704224092Sdougb	isc_interval_set(&interval, res->query_timeout, 0);
3705135446Strhodes	iresult = isc_time_nowplusinterval(&fctx->expires, &interval);
3706135446Strhodes	if (iresult != ISC_R_SUCCESS) {
3707135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
3708135446Strhodes				 "isc_time_nowplusinterval: %s",
3709135446Strhodes				 isc_result_totext(iresult));
3710135446Strhodes		result = ISC_R_UNEXPECTED;
3711135446Strhodes		goto cleanup_rmessage;
3712135446Strhodes	}
3713135446Strhodes
3714135446Strhodes	/*
3715135446Strhodes	 * Default retry interval initialization.  We set the interval now
3716135446Strhodes	 * mostly so it won't be uninitialized.  It will be set to the
3717135446Strhodes	 * correct value before a query is issued.
3718135446Strhodes	 */
3719135446Strhodes	isc_interval_set(&fctx->interval, 2, 0);
3720135446Strhodes
3721135446Strhodes	/*
3722135446Strhodes	 * Create an inactive timer.  It will be made active when the fetch
3723135446Strhodes	 * is actually started.
3724135446Strhodes	 */
3725135446Strhodes	fctx->timer = NULL;
3726135446Strhodes	iresult = isc_timer_create(res->timermgr, isc_timertype_inactive,
3727135446Strhodes				   NULL, NULL,
3728135446Strhodes				   res->buckets[bucketnum].task, fctx_timeout,
3729135446Strhodes				   fctx, &fctx->timer);
3730135446Strhodes	if (iresult != ISC_R_SUCCESS) {
3731135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
3732135446Strhodes				 "isc_timer_create: %s",
3733135446Strhodes				 isc_result_totext(iresult));
3734135446Strhodes		result = ISC_R_UNEXPECTED;
3735135446Strhodes		goto cleanup_rmessage;
3736135446Strhodes	}
3737135446Strhodes
3738135446Strhodes	/*
3739135446Strhodes	 * Attach to the view's cache and adb.
3740135446Strhodes	 */
3741135446Strhodes	fctx->cache = NULL;
3742135446Strhodes	dns_db_attach(res->view->cachedb, &fctx->cache);
3743135446Strhodes	fctx->adb = NULL;
3744135446Strhodes	dns_adb_attach(res->view->adb, &fctx->adb);
3745236374Sdougb	fctx->mctx = NULL;
3746236374Sdougb	isc_mem_attach(mctx, &fctx->mctx);
3747135446Strhodes
3748135446Strhodes	ISC_LIST_INIT(fctx->events);
3749135446Strhodes	ISC_LINK_INIT(fctx, link);
3750135446Strhodes	fctx->magic = FCTX_MAGIC;
3751135446Strhodes
3752135446Strhodes	ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
3753135446Strhodes
3754135446Strhodes	LOCK(&res->nlock);
3755135446Strhodes	res->nfctx++;
3756135446Strhodes	UNLOCK(&res->nlock);
3757135446Strhodes
3758135446Strhodes	*fctxp = fctx;
3759135446Strhodes
3760135446Strhodes	return (ISC_R_SUCCESS);
3761135446Strhodes
3762135446Strhodes cleanup_rmessage:
3763135446Strhodes	dns_message_destroy(&fctx->rmessage);
3764135446Strhodes
3765135446Strhodes cleanup_qmessage:
3766135446Strhodes	dns_message_destroy(&fctx->qmessage);
3767135446Strhodes
3768135446Strhodes cleanup_domain:
3769135446Strhodes	if (dns_name_countlabels(&fctx->domain) > 0)
3770236374Sdougb		dns_name_free(&fctx->domain, mctx);
3771135446Strhodes	if (dns_rdataset_isassociated(&fctx->nameservers))
3772135446Strhodes		dns_rdataset_disassociate(&fctx->nameservers);
3773135446Strhodes
3774135446Strhodes cleanup_name:
3775236374Sdougb	dns_name_free(&fctx->name, mctx);
3776135446Strhodes
3777135446Strhodes cleanup_info:
3778236374Sdougb	isc_mem_free(mctx, fctx->info);
3779135446Strhodes
3780135446Strhodes cleanup_fetch:
3781236374Sdougb	isc_mem_put(mctx, fctx, sizeof(*fctx));
3782135446Strhodes
3783135446Strhodes	return (result);
3784135446Strhodes}
3785135446Strhodes
3786135446Strhodes/*
3787135446Strhodes * Handle Responses
3788135446Strhodes */
3789135446Strhodesstatic inline isc_boolean_t
3790135446Strhodesis_lame(fetchctx_t *fctx) {
3791135446Strhodes	dns_message_t *message = fctx->rmessage;
3792135446Strhodes	dns_name_t *name;
3793135446Strhodes	dns_rdataset_t *rdataset;
3794135446Strhodes	isc_result_t result;
3795135446Strhodes
3796135446Strhodes	if (message->rcode != dns_rcode_noerror &&
3797135446Strhodes	    message->rcode != dns_rcode_nxdomain)
3798135446Strhodes		return (ISC_FALSE);
3799135446Strhodes
3800135446Strhodes	if (message->counts[DNS_SECTION_ANSWER] != 0)
3801135446Strhodes		return (ISC_FALSE);
3802135446Strhodes
3803135446Strhodes	if (message->counts[DNS_SECTION_AUTHORITY] == 0)
3804135446Strhodes		return (ISC_FALSE);
3805135446Strhodes
3806135446Strhodes	result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
3807135446Strhodes	while (result == ISC_R_SUCCESS) {
3808135446Strhodes		name = NULL;
3809135446Strhodes		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
3810135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
3811135446Strhodes		     rdataset != NULL;
3812135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
3813135446Strhodes			dns_namereln_t namereln;
3814135446Strhodes			int order;
3815135446Strhodes			unsigned int labels;
3816135446Strhodes			if (rdataset->type != dns_rdatatype_ns)
3817135446Strhodes				continue;
3818135446Strhodes			namereln = dns_name_fullcompare(name, &fctx->domain,
3819174187Sdougb							&order, &labels);
3820135446Strhodes			if (namereln == dns_namereln_equal &&
3821135446Strhodes			    (message->flags & DNS_MESSAGEFLAG_AA) != 0)
3822135446Strhodes				return (ISC_FALSE);
3823135446Strhodes			if (namereln == dns_namereln_subdomain)
3824135446Strhodes				return (ISC_FALSE);
3825135446Strhodes			return (ISC_TRUE);
3826135446Strhodes		}
3827135446Strhodes		result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
3828135446Strhodes	}
3829135446Strhodes
3830135446Strhodes	return (ISC_FALSE);
3831135446Strhodes}
3832135446Strhodes
3833135446Strhodesstatic inline void
3834135446Strhodeslog_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) {
3835135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
3836186462Sdougb	char domainbuf[DNS_NAME_FORMATSIZE];
3837135446Strhodes	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
3838186462Sdougb
3839135446Strhodes	dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
3840135446Strhodes	dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
3841135446Strhodes	isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf));
3842135446Strhodes	isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS,
3843135446Strhodes		      DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
3844135446Strhodes		      "lame server resolving '%s' (in '%s'?): %s",
3845135446Strhodes		      namebuf, domainbuf, addrbuf);
3846135446Strhodes}
3847135446Strhodes
3848224092Sdougbstatic inline void
3849224092Sdougblog_formerr(fetchctx_t *fctx, const char *format, ...) {
3850224092Sdougb	char nsbuf[ISC_SOCKADDR_FORMATSIZE];
3851224092Sdougb	char clbuf[ISC_SOCKADDR_FORMATSIZE];
3852224092Sdougb	const char *clmsg = "";
3853224092Sdougb	char msgbuf[2048];
3854224092Sdougb	va_list args;
3855224092Sdougb
3856224092Sdougb	va_start(args, format);
3857224092Sdougb	vsnprintf(msgbuf, sizeof(msgbuf), format, args);
3858224092Sdougb	va_end(args);
3859224092Sdougb
3860224092Sdougb	isc_sockaddr_format(&fctx->addrinfo->sockaddr, nsbuf, sizeof(nsbuf));
3861224092Sdougb
3862224092Sdougb	if (fctx->client != NULL) {
3863224092Sdougb		clmsg = " for client ";
3864224092Sdougb		isc_sockaddr_format(fctx->client, clbuf, sizeof(clbuf));
3865224092Sdougb	} else {
3866224092Sdougb		clbuf[0] = '\0';
3867224092Sdougb	}
3868224092Sdougb
3869224092Sdougb	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
3870224092Sdougb		      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
3871224092Sdougb		      "DNS format error from %s resolving %s%s%s: %s",
3872224092Sdougb		      nsbuf, fctx->info, clmsg, clbuf, msgbuf);
3873224092Sdougb}
3874224092Sdougb
3875135446Strhodesstatic inline isc_result_t
3876135446Strhodessame_question(fetchctx_t *fctx) {
3877135446Strhodes	isc_result_t result;
3878135446Strhodes	dns_message_t *message = fctx->rmessage;
3879135446Strhodes	dns_name_t *name;
3880135446Strhodes	dns_rdataset_t *rdataset;
3881135446Strhodes
3882135446Strhodes	/*
3883135446Strhodes	 * Caller must be holding the fctx lock.
3884135446Strhodes	 */
3885135446Strhodes
3886135446Strhodes	/*
3887135446Strhodes	 * XXXRTH  Currently we support only one question.
3888135446Strhodes	 */
3889224092Sdougb	if (message->counts[DNS_SECTION_QUESTION] != 1) {
3890224092Sdougb		log_formerr(fctx, "too many questions");
3891135446Strhodes		return (DNS_R_FORMERR);
3892224092Sdougb	}
3893135446Strhodes
3894135446Strhodes	result = dns_message_firstname(message, DNS_SECTION_QUESTION);
3895135446Strhodes	if (result != ISC_R_SUCCESS)
3896135446Strhodes		return (result);
3897135446Strhodes	name = NULL;
3898135446Strhodes	dns_message_currentname(message, DNS_SECTION_QUESTION, &name);
3899135446Strhodes	rdataset = ISC_LIST_HEAD(name->list);
3900135446Strhodes	INSIST(rdataset != NULL);
3901135446Strhodes	INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
3902224092Sdougb
3903135446Strhodes	if (fctx->type != rdataset->type ||
3904135446Strhodes	    fctx->res->rdclass != rdataset->rdclass ||
3905224092Sdougb	    !dns_name_equal(&fctx->name, name)) {
3906224092Sdougb		char namebuf[DNS_NAME_FORMATSIZE];
3907224092Sdougb		char class[DNS_RDATACLASS_FORMATSIZE];
3908224092Sdougb		char type[DNS_RDATATYPE_FORMATSIZE];
3909224092Sdougb
3910224092Sdougb		dns_name_format(name, namebuf, sizeof(namebuf));
3911224092Sdougb		dns_rdataclass_format(rdataset->rdclass, class, sizeof(class));
3912224092Sdougb		dns_rdatatype_format(rdataset->type, type, sizeof(type));
3913224092Sdougb		log_formerr(fctx, "question section mismatch: got %s/%s/%s",
3914224092Sdougb			    namebuf, class, type);
3915135446Strhodes		return (DNS_R_FORMERR);
3916224092Sdougb	}
3917135446Strhodes
3918135446Strhodes	return (ISC_R_SUCCESS);
3919135446Strhodes}
3920135446Strhodes
3921135446Strhodesstatic void
3922135446Strhodesclone_results(fetchctx_t *fctx) {
3923135446Strhodes	dns_fetchevent_t *event, *hevent;
3924135446Strhodes	isc_result_t result;
3925135446Strhodes	dns_name_t *name, *hname;
3926135446Strhodes
3927135446Strhodes	FCTXTRACE("clone_results");
3928135446Strhodes
3929135446Strhodes	/*
3930135446Strhodes	 * Set up any other events to have the same data as the first
3931135446Strhodes	 * event.
3932135446Strhodes	 *
3933135446Strhodes	 * Caller must be holding the appropriate lock.
3934135446Strhodes	 */
3935135446Strhodes
3936135446Strhodes	fctx->cloned = ISC_TRUE;
3937135446Strhodes	hevent = ISC_LIST_HEAD(fctx->events);
3938135446Strhodes	if (hevent == NULL)
3939135446Strhodes		return;
3940135446Strhodes	hname = dns_fixedname_name(&hevent->foundname);
3941135446Strhodes	for (event = ISC_LIST_NEXT(hevent, ev_link);
3942135446Strhodes	     event != NULL;
3943135446Strhodes	     event = ISC_LIST_NEXT(event, ev_link)) {
3944135446Strhodes		name = dns_fixedname_name(&event->foundname);
3945135446Strhodes		result = dns_name_copy(hname, name, NULL);
3946135446Strhodes		if (result != ISC_R_SUCCESS)
3947135446Strhodes			event->result = result;
3948135446Strhodes		else
3949135446Strhodes			event->result = hevent->result;
3950135446Strhodes		dns_db_attach(hevent->db, &event->db);
3951135446Strhodes		dns_db_attachnode(hevent->db, hevent->node, &event->node);
3952135446Strhodes		INSIST(hevent->rdataset != NULL);
3953135446Strhodes		INSIST(event->rdataset != NULL);
3954135446Strhodes		if (dns_rdataset_isassociated(hevent->rdataset))
3955135446Strhodes			dns_rdataset_clone(hevent->rdataset, event->rdataset);
3956135446Strhodes		INSIST(! (hevent->sigrdataset == NULL &&
3957135446Strhodes			  event->sigrdataset != NULL));
3958135446Strhodes		if (hevent->sigrdataset != NULL &&
3959135446Strhodes		    dns_rdataset_isassociated(hevent->sigrdataset) &&
3960135446Strhodes		    event->sigrdataset != NULL)
3961135446Strhodes			dns_rdataset_clone(hevent->sigrdataset,
3962135446Strhodes					   event->sigrdataset);
3963135446Strhodes	}
3964135446Strhodes}
3965135446Strhodes
3966193149Sdougb#define CACHE(r)        (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0)
3967193149Sdougb#define ANSWER(r)       (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0)
3968193149Sdougb#define ANSWERSIG(r)    (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0)
3969193149Sdougb#define EXTERNAL(r)     (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0)
3970193149Sdougb#define CHAINING(r)     (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0)
3971193149Sdougb#define CHASE(r)        (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0)
3972193149Sdougb#define CHECKNAMES(r)   (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0)
3973135446Strhodes
3974135446Strhodes
3975135446Strhodes/*
3976135446Strhodes * Destroy '*fctx' if it is ready to be destroyed (i.e., if it has
3977234010Sdougb * no references and is no longer waiting for any events).
3978135446Strhodes *
3979135446Strhodes * Requires:
3980193149Sdougb *      '*fctx' is shutting down.
3981234010Sdougb *
3982234010Sdougb * Returns:
3983234010Sdougb *	true if the resolver is exiting and this is the last fctx in the bucket.
3984135446Strhodes */
3985234010Sdougbstatic isc_boolean_t
3986234010Sdougbmaybe_destroy(fetchctx_t *fctx, isc_boolean_t locked) {
3987135446Strhodes	unsigned int bucketnum;
3988135446Strhodes	isc_boolean_t bucket_empty = ISC_FALSE;
3989135446Strhodes	dns_resolver_t *res = fctx->res;
3990174187Sdougb	dns_validator_t *validator, *next_validator;
3991236374Sdougb	isc_boolean_t destroy = ISC_FALSE;
3992135446Strhodes
3993135446Strhodes	REQUIRE(SHUTTINGDOWN(fctx));
3994135446Strhodes
3995234010Sdougb	bucketnum = fctx->bucketnum;
3996234010Sdougb	if (!locked)
3997234010Sdougb		LOCK(&res->buckets[bucketnum].lock);
3998166332Sdougb	if (fctx->pending != 0 || fctx->nqueries != 0)
3999234010Sdougb		goto unlock;
4000135446Strhodes
4001166332Sdougb	for (validator = ISC_LIST_HEAD(fctx->validators);
4002174187Sdougb	     validator != NULL; validator = next_validator) {
4003174187Sdougb		next_validator = ISC_LIST_NEXT(validator, link);
4004174187Sdougb		dns_validator_cancel(validator);
4005166332Sdougb	}
4006166332Sdougb
4007236374Sdougb	if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators)) {
4008236374Sdougb		bucket_empty = fctx_unlink(fctx);
4009236374Sdougb		destroy = ISC_TRUE;
4010236374Sdougb	}
4011234010Sdougb unlock:
4012234010Sdougb	if (!locked)
4013234010Sdougb		UNLOCK(&res->buckets[bucketnum].lock);
4014236374Sdougb	if (destroy)
4015236374Sdougb		fctx_destroy(fctx);
4016234010Sdougb	return (bucket_empty);
4017135446Strhodes}
4018135446Strhodes
4019135446Strhodes/*
4020135446Strhodes * The validator has finished.
4021135446Strhodes */
4022135446Strhodesstatic void
4023135446Strhodesvalidated(isc_task_t *task, isc_event_t *event) {
4024234010Sdougb	dns_adbaddrinfo_t *addrinfo;
4025234010Sdougb	dns_dbnode_t *node = NULL;
4026234010Sdougb	dns_dbnode_t *nsnode = NULL;
4027135446Strhodes	dns_fetchevent_t *hevent;
4028234010Sdougb	dns_name_t *name;
4029135446Strhodes	dns_rdataset_t *ardataset = NULL;
4030135446Strhodes	dns_rdataset_t *asigrdataset = NULL;
4031234010Sdougb	dns_rdataset_t *rdataset;
4032234010Sdougb	dns_rdataset_t *sigrdataset;
4033234010Sdougb	dns_resolver_t *res;
4034234010Sdougb	dns_valarg_t *valarg;
4035234010Sdougb	dns_validatorevent_t *vevent;
4036234010Sdougb	fetchctx_t *fctx;
4037234010Sdougb	isc_boolean_t chaining;
4038135446Strhodes	isc_boolean_t negative;
4039135446Strhodes	isc_boolean_t sentresponse;
4040234010Sdougb	isc_result_t eresult = ISC_R_SUCCESS;
4041234010Sdougb	isc_result_t result = ISC_R_SUCCESS;
4042234010Sdougb	isc_stdtime_t now;
4043135446Strhodes	isc_uint32_t ttl;
4044135446Strhodes
4045135446Strhodes	UNUSED(task); /* for now */
4046135446Strhodes
4047135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE);
4048153816Sdougb	valarg = event->ev_arg;
4049153816Sdougb	fctx = valarg->fctx;
4050234010Sdougb	res = fctx->res;
4051153816Sdougb	addrinfo = valarg->addrinfo;
4052135446Strhodes	REQUIRE(VALID_FCTX(fctx));
4053135446Strhodes	REQUIRE(!ISC_LIST_EMPTY(fctx->validators));
4054135446Strhodes
4055135446Strhodes	vevent = (dns_validatorevent_t *)event;
4056224092Sdougb	fctx->vresult = vevent->result;
4057135446Strhodes
4058135446Strhodes	FCTXTRACE("received validation completion event");
4059135446Strhodes
4060234010Sdougb	LOCK(&res->buckets[fctx->bucketnum].lock);
4061234010Sdougb
4062135446Strhodes	ISC_LIST_UNLINK(fctx->validators, vevent->validator, link);
4063174187Sdougb	fctx->validator = NULL;
4064135446Strhodes
4065135446Strhodes	/*
4066135446Strhodes	 * Destroy the validator early so that we can
4067135446Strhodes	 * destroy the fctx if necessary.
4068135446Strhodes	 */
4069135446Strhodes	dns_validator_destroy(&vevent->validator);
4070236374Sdougb	isc_mem_put(fctx->mctx, valarg, sizeof(*valarg));
4071135446Strhodes
4072135446Strhodes	negative = ISC_TF(vevent->rdataset == NULL);
4073135446Strhodes
4074135446Strhodes	sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0);
4075135446Strhodes
4076135446Strhodes	/*
4077135446Strhodes	 * If shutting down, ignore the results.  Check to see if we're
4078135446Strhodes	 * done waiting for validator completions and ADB pending events; if
4079135446Strhodes	 * so, destroy the fctx.
4080135446Strhodes	 */
4081135446Strhodes	if (SHUTTINGDOWN(fctx) && !sentresponse) {
4082234010Sdougb		isc_uint32_t bucketnum = fctx->bucketnum;
4083234010Sdougb		isc_boolean_t bucket_empty;
4084234010Sdougb		bucket_empty = maybe_destroy(fctx, ISC_TRUE);
4085234010Sdougb		UNLOCK(&res->buckets[bucketnum].lock);
4086234010Sdougb		if (bucket_empty)
4087234010Sdougb			empty_bucket(res);
4088135446Strhodes		goto cleanup_event;
4089135446Strhodes	}
4090135446Strhodes
4091205292Sdougb	isc_stdtime_get(&now);
4092205292Sdougb
4093135446Strhodes	/*
4094135446Strhodes	 * If chaining, we need to make sure that the right result code is
4095135446Strhodes	 * returned, and that the rdatasets are bound.
4096135446Strhodes	 */
4097135446Strhodes	if (vevent->result == ISC_R_SUCCESS &&
4098135446Strhodes	    !negative &&
4099135446Strhodes	    vevent->rdataset != NULL &&
4100135446Strhodes	    CHAINING(vevent->rdataset))
4101135446Strhodes	{
4102135446Strhodes		if (vevent->rdataset->type == dns_rdatatype_cname)
4103135446Strhodes			eresult = DNS_R_CNAME;
4104135446Strhodes		else {
4105135446Strhodes			INSIST(vevent->rdataset->type == dns_rdatatype_dname);
4106135446Strhodes			eresult = DNS_R_DNAME;
4107135446Strhodes		}
4108135446Strhodes		chaining = ISC_TRUE;
4109135446Strhodes	} else
4110135446Strhodes		chaining = ISC_FALSE;
4111135446Strhodes
4112135446Strhodes	/*
4113135446Strhodes	 * Either we're not shutting down, or we are shutting down but want
4114135446Strhodes	 * to cache the result anyway (if this was a validation started by
4115135446Strhodes	 * a query with cd set)
4116135446Strhodes	 */
4117135446Strhodes
4118135446Strhodes	hevent = ISC_LIST_HEAD(fctx->events);
4119135446Strhodes	if (hevent != NULL) {
4120135446Strhodes		if (!negative && !chaining &&
4121135446Strhodes		    (fctx->type == dns_rdatatype_any ||
4122162079Sdougb		     fctx->type == dns_rdatatype_rrsig ||
4123162079Sdougb		     fctx->type == dns_rdatatype_sig)) {
4124135446Strhodes			/*
4125135446Strhodes			 * Don't bind rdatasets; the caller
4126135446Strhodes			 * will iterate the node.
4127135446Strhodes			 */
4128135446Strhodes		} else {
4129135446Strhodes			ardataset = hevent->rdataset;
4130135446Strhodes			asigrdataset = hevent->sigrdataset;
4131135446Strhodes		}
4132135446Strhodes	}
4133135446Strhodes
4134135446Strhodes	if (vevent->result != ISC_R_SUCCESS) {
4135135446Strhodes		FCTXTRACE("validation failed");
4136234010Sdougb		inc_stats(res, dns_resstatscounter_valfail);
4137193149Sdougb		fctx->valfail++;
4138193149Sdougb		fctx->vresult = vevent->result;
4139205292Sdougb		if (fctx->vresult != DNS_R_BROKENCHAIN) {
4140205292Sdougb			result = ISC_R_NOTFOUND;
4141205292Sdougb			if (vevent->rdataset != NULL)
4142205292Sdougb				result = dns_db_findnode(fctx->cache,
4143205292Sdougb							 vevent->name,
4144205292Sdougb							 ISC_TRUE, &node);
4145205292Sdougb			if (result == ISC_R_SUCCESS)
4146205292Sdougb				(void)dns_db_deleterdataset(fctx->cache, node,
4147205292Sdougb							     NULL,
4148205292Sdougb							    vevent->type, 0);
4149205292Sdougb			if (result == ISC_R_SUCCESS &&
4150205292Sdougb			     vevent->sigrdataset != NULL)
4151205292Sdougb				(void)dns_db_deleterdataset(fctx->cache, node,
4152205292Sdougb							    NULL,
4153205292Sdougb							    dns_rdatatype_rrsig,
4154205292Sdougb							    vevent->type);
4155205292Sdougb			if (result == ISC_R_SUCCESS)
4156205292Sdougb				dns_db_detachnode(fctx->cache, &node);
4157205292Sdougb		}
4158205292Sdougb		if (fctx->vresult == DNS_R_BROKENCHAIN && !negative) {
4159205292Sdougb			/*
4160205292Sdougb			 * Cache the data as pending for later validation.
4161205292Sdougb			 */
4162205292Sdougb			result = ISC_R_NOTFOUND;
4163205292Sdougb			if (vevent->rdataset != NULL)
4164205292Sdougb				result = dns_db_findnode(fctx->cache,
4165205292Sdougb							 vevent->name,
4166205292Sdougb							 ISC_TRUE, &node);
4167205292Sdougb			if (result == ISC_R_SUCCESS) {
4168205292Sdougb				(void)dns_db_addrdataset(fctx->cache, node,
4169205292Sdougb							 NULL, now,
4170205292Sdougb							 vevent->rdataset, 0,
4171205292Sdougb							 NULL);
4172205292Sdougb			}
4173205292Sdougb			if (result == ISC_R_SUCCESS &&
4174205292Sdougb			    vevent->sigrdataset != NULL)
4175205292Sdougb				(void)dns_db_addrdataset(fctx->cache, node,
4176205292Sdougb							 NULL, now,
4177205292Sdougb							 vevent->sigrdataset,
4178205292Sdougb							 0, NULL);
4179205292Sdougb			if (result == ISC_R_SUCCESS)
4180205292Sdougb				dns_db_detachnode(fctx->cache, &node);
4181205292Sdougb		}
4182205292Sdougb		result = fctx->vresult;
4183193149Sdougb		add_bad(fctx, addrinfo, result, badns_validation);
4184153816Sdougb		isc_event_free(&event);
4185234010Sdougb		UNLOCK(&res->buckets[fctx->bucketnum].lock);
4186174187Sdougb		INSIST(fctx->validator == NULL);
4187174187Sdougb		fctx->validator = ISC_LIST_HEAD(fctx->validators);
4188205292Sdougb		if (fctx->validator != NULL)
4189174187Sdougb			dns_validator_send(fctx->validator);
4190205292Sdougb		else if (sentresponse)
4191193149Sdougb			fctx_done(fctx, result, __LINE__); /* Locks bucket. */
4192205292Sdougb		else if (result == DNS_R_BROKENCHAIN) {
4193205292Sdougb			isc_result_t tresult;
4194205292Sdougb			isc_time_t expire;
4195205292Sdougb			isc_interval_t i;
4196205292Sdougb
4197205292Sdougb			isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0);
4198205292Sdougb			tresult = isc_time_nowplusinterval(&expire, &i);
4199205292Sdougb			if (negative &&
4200205292Sdougb			    (fctx->type == dns_rdatatype_dnskey ||
4201205292Sdougb			     fctx->type == dns_rdatatype_dlv ||
4202205292Sdougb			     fctx->type == dns_rdatatype_ds) &&
4203205292Sdougb			     tresult == ISC_R_SUCCESS)
4204234010Sdougb				dns_resolver_addbadcache(res, &fctx->name,
4205205292Sdougb							 fctx->type, &expire);
4206205292Sdougb			fctx_done(fctx, result, __LINE__); /* Locks bucket. */
4207205292Sdougb		} else
4208205292Sdougb			fctx_try(fctx, ISC_TRUE, ISC_TRUE); /* Locks bucket. */
4209153816Sdougb		return;
4210135446Strhodes	}
4211135446Strhodes
4212135446Strhodes
4213135446Strhodes	if (negative) {
4214135446Strhodes		dns_rdatatype_t covers;
4215135446Strhodes		FCTXTRACE("nonexistence validation OK");
4216135446Strhodes
4217234010Sdougb		inc_stats(res, dns_resstatscounter_valnegsuccess);
4218193149Sdougb
4219135446Strhodes		if (fctx->rmessage->rcode == dns_rcode_nxdomain)
4220135446Strhodes			covers = dns_rdatatype_any;
4221135446Strhodes		else
4222135446Strhodes			covers = fctx->type;
4223135446Strhodes
4224135446Strhodes		result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE,
4225135446Strhodes					 &node);
4226135446Strhodes		if (result != ISC_R_SUCCESS)
4227135446Strhodes			goto noanswer_response;
4228135446Strhodes
4229135446Strhodes		/*
4230135446Strhodes		 * If we are asking for a SOA record set the cache time
4231135446Strhodes		 * to zero to facilitate locating the containing zone of
4232193149Sdougb		 * a arbitrary zone.
4233135446Strhodes		 */
4234234010Sdougb		ttl = res->view->maxncachettl;
4235135446Strhodes		if (fctx->type == dns_rdatatype_soa &&
4236234010Sdougb		    covers == dns_rdatatype_any && res->zero_no_soa_ttl)
4237135446Strhodes			ttl = 0;
4238135446Strhodes
4239135446Strhodes		result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
4240193149Sdougb					   covers, now, ttl, vevent->optout,
4241254402Serwin					   vevent->secure, ardataset, &eresult);
4242135446Strhodes		if (result != ISC_R_SUCCESS)
4243135446Strhodes			goto noanswer_response;
4244135446Strhodes		goto answer_response;
4245193149Sdougb	} else
4246234010Sdougb		inc_stats(res, dns_resstatscounter_valsuccess);
4247135446Strhodes
4248135446Strhodes	FCTXTRACE("validation OK");
4249135446Strhodes
4250135446Strhodes	if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) {
4251135446Strhodes		result = dns_rdataset_addnoqname(vevent->rdataset,
4252135446Strhodes				   vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]);
4253135446Strhodes		RUNTIME_CHECK(result == ISC_R_SUCCESS);
4254165071Sdougb		INSIST(vevent->sigrdataset != NULL);
4255135446Strhodes		vevent->sigrdataset->ttl = vevent->rdataset->ttl;
4256193149Sdougb		if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) {
4257193149Sdougb			result = dns_rdataset_addclosest(vevent->rdataset,
4258193149Sdougb				 vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER]);
4259193149Sdougb			RUNTIME_CHECK(result == ISC_R_SUCCESS);
4260193149Sdougb		}
4261254402Serwin	} else if (vevent->rdataset->trust == dns_trust_answer &&
4262254402Serwin		   vevent->rdataset->type != dns_rdatatype_rrsig)
4263254402Serwin	{
4264254402Serwin		isc_result_t tresult;
4265254402Serwin		dns_name_t *noqname = NULL;
4266254402Serwin		tresult = findnoqname(fctx, vevent->name,
4267254402Serwin				      vevent->rdataset->type, &noqname);
4268254402Serwin		if (tresult == ISC_R_SUCCESS && noqname != NULL) {
4269254402Serwin			tresult = dns_rdataset_addnoqname(vevent->rdataset,
4270254402Serwin							  noqname);
4271254402Serwin			RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
4272254402Serwin		}
4273135446Strhodes	}
4274135446Strhodes
4275135446Strhodes	/*
4276135446Strhodes	 * The data was already cached as pending data.
4277135446Strhodes	 * Re-cache it as secure and bind the cached
4278135446Strhodes	 * rdatasets to the first event on the fetch
4279135446Strhodes	 * event list.
4280135446Strhodes	 */
4281135446Strhodes	result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node);
4282135446Strhodes	if (result != ISC_R_SUCCESS)
4283135446Strhodes		goto noanswer_response;
4284135446Strhodes
4285135446Strhodes	result = dns_db_addrdataset(fctx->cache, node, NULL, now,
4286135446Strhodes				    vevent->rdataset, 0, ardataset);
4287135446Strhodes	if (result != ISC_R_SUCCESS &&
4288135446Strhodes	    result != DNS_R_UNCHANGED)
4289135446Strhodes		goto noanswer_response;
4290223812Sdougb	if (ardataset != NULL && NEGATIVE(ardataset)) {
4291174187Sdougb		if (NXDOMAIN(ardataset))
4292174187Sdougb			eresult = DNS_R_NCACHENXDOMAIN;
4293174187Sdougb		else
4294174187Sdougb			eresult = DNS_R_NCACHENXRRSET;
4295174187Sdougb	} else if (vevent->sigrdataset != NULL) {
4296135446Strhodes		result = dns_db_addrdataset(fctx->cache, node, NULL, now,
4297135446Strhodes					    vevent->sigrdataset, 0,
4298135446Strhodes					    asigrdataset);
4299135446Strhodes		if (result != ISC_R_SUCCESS &&
4300135446Strhodes		    result != DNS_R_UNCHANGED)
4301135446Strhodes			goto noanswer_response;
4302135446Strhodes	}
4303135446Strhodes
4304135446Strhodes	if (sentresponse) {
4305234010Sdougb		isc_boolean_t bucket_empty = ISC_FALSE;
4306135446Strhodes		/*
4307135446Strhodes		 * If we only deferred the destroy because we wanted to cache
4308135446Strhodes		 * the data, destroy now.
4309135446Strhodes		 */
4310174187Sdougb		dns_db_detachnode(fctx->cache, &node);
4311135446Strhodes		if (SHUTTINGDOWN(fctx))
4312234010Sdougb			bucket_empty = maybe_destroy(fctx, ISC_TRUE);
4313234010Sdougb		UNLOCK(&res->buckets[fctx->bucketnum].lock);
4314234010Sdougb		if (bucket_empty)
4315234010Sdougb			empty_bucket(res);
4316135446Strhodes		goto cleanup_event;
4317135446Strhodes	}
4318135446Strhodes
4319135446Strhodes	if (!ISC_LIST_EMPTY(fctx->validators)) {
4320135446Strhodes		INSIST(!negative);
4321135446Strhodes		INSIST(fctx->type == dns_rdatatype_any ||
4322162079Sdougb		       fctx->type == dns_rdatatype_rrsig ||
4323162079Sdougb		       fctx->type == dns_rdatatype_sig);
4324135446Strhodes		/*
4325135446Strhodes		 * Don't send a response yet - we have
4326135446Strhodes		 * more rdatasets that still need to
4327135446Strhodes		 * be validated.
4328135446Strhodes		 */
4329174187Sdougb		dns_db_detachnode(fctx->cache, &node);
4330234010Sdougb		UNLOCK(&res->buckets[fctx->bucketnum].lock);
4331166332Sdougb		dns_validator_send(ISC_LIST_HEAD(fctx->validators));
4332135446Strhodes		goto cleanup_event;
4333135446Strhodes	}
4334135446Strhodes
4335135446Strhodes answer_response:
4336135446Strhodes	/*
4337135446Strhodes	 * Cache any NS/NSEC records that happened to be validated.
4338135446Strhodes	 */
4339135446Strhodes	result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
4340135446Strhodes	while (result == ISC_R_SUCCESS) {
4341135446Strhodes		name = NULL;
4342135446Strhodes		dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY,
4343135446Strhodes					&name);
4344135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
4345135446Strhodes		     rdataset != NULL;
4346135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
4347135446Strhodes			if ((rdataset->type != dns_rdatatype_ns &&
4348135446Strhodes			     rdataset->type != dns_rdatatype_nsec) ||
4349135446Strhodes			    rdataset->trust != dns_trust_secure)
4350135446Strhodes				continue;
4351135446Strhodes			for (sigrdataset = ISC_LIST_HEAD(name->list);
4352135446Strhodes			     sigrdataset != NULL;
4353135446Strhodes			     sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
4354135446Strhodes				if (sigrdataset->type != dns_rdatatype_rrsig ||
4355135446Strhodes				    sigrdataset->covers != rdataset->type)
4356135446Strhodes					continue;
4357135446Strhodes				break;
4358135446Strhodes			}
4359135446Strhodes			if (sigrdataset == NULL ||
4360135446Strhodes			    sigrdataset->trust != dns_trust_secure)
4361135446Strhodes				continue;
4362135446Strhodes			result = dns_db_findnode(fctx->cache, name, ISC_TRUE,
4363135446Strhodes						 &nsnode);
4364135446Strhodes			if (result != ISC_R_SUCCESS)
4365135446Strhodes				continue;
4366135446Strhodes
4367135446Strhodes			result = dns_db_addrdataset(fctx->cache, nsnode, NULL,
4368135446Strhodes						    now, rdataset, 0, NULL);
4369135446Strhodes			if (result == ISC_R_SUCCESS)
4370135446Strhodes				result = dns_db_addrdataset(fctx->cache, nsnode,
4371135446Strhodes							    NULL, now,
4372135446Strhodes							    sigrdataset, 0,
4373135446Strhodes							    NULL);
4374135446Strhodes			dns_db_detachnode(fctx->cache, &nsnode);
4375225361Sdougb			if (result != ISC_R_SUCCESS)
4376225361Sdougb				continue;
4377135446Strhodes		}
4378135446Strhodes		result = dns_message_nextname(fctx->rmessage,
4379135446Strhodes					      DNS_SECTION_AUTHORITY);
4380135446Strhodes	}
4381135446Strhodes
4382135446Strhodes	result = ISC_R_SUCCESS;
4383135446Strhodes
4384135446Strhodes	/*
4385135446Strhodes	 * Respond with an answer, positive or negative,
4386135446Strhodes	 * as opposed to an error.  'node' must be non-NULL.
4387135446Strhodes	 */
4388135446Strhodes
4389135446Strhodes	fctx->attributes |= FCTX_ATTR_HAVEANSWER;
4390135446Strhodes
4391135446Strhodes	if (hevent != NULL) {
4392254402Serwin		/*
4393254402Serwin		 * Negative results must be indicated in event->result.
4394254402Serwin		 */
4395254402Serwin		if (dns_rdataset_isassociated(hevent->rdataset) &&
4396254402Serwin		    NEGATIVE(hevent->rdataset)) {
4397254402Serwin			INSIST(eresult == DNS_R_NCACHENXDOMAIN ||
4398254402Serwin			       eresult == DNS_R_NCACHENXRRSET);
4399254402Serwin		}
4400135446Strhodes		hevent->result = eresult;
4401135446Strhodes		RUNTIME_CHECK(dns_name_copy(vevent->name,
4402135446Strhodes			      dns_fixedname_name(&hevent->foundname), NULL)
4403135446Strhodes			      == ISC_R_SUCCESS);
4404135446Strhodes		dns_db_attach(fctx->cache, &hevent->db);
4405174187Sdougb		dns_db_transfernode(fctx->cache, &node, &hevent->node);
4406135446Strhodes		clone_results(fctx);
4407135446Strhodes	}
4408135446Strhodes
4409135446Strhodes noanswer_response:
4410135446Strhodes	if (node != NULL)
4411135446Strhodes		dns_db_detachnode(fctx->cache, &node);
4412135446Strhodes
4413234010Sdougb	UNLOCK(&res->buckets[fctx->bucketnum].lock);
4414193149Sdougb	fctx_done(fctx, result, __LINE__); /* Locks bucket. */
4415165071Sdougb
4416135446Strhodes cleanup_event:
4417174187Sdougb	INSIST(node == NULL);
4418135446Strhodes	isc_event_free(&event);
4419135446Strhodes}
4420135446Strhodes
4421254402Serwinstatic void
4422254402Serwinfctx_log(void *arg, int level, const char *fmt, ...) {
4423254402Serwin	char msgbuf[2048];
4424254402Serwin	va_list args;
4425254402Serwin	fetchctx_t *fctx = arg;
4426254402Serwin
4427254402Serwin	va_start(args, fmt);
4428254402Serwin	vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
4429254402Serwin	va_end(args);
4430254402Serwin
4431254402Serwin	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
4432254402Serwin		      DNS_LOGMODULE_RESOLVER, level,
4433254402Serwin		      "fctx %p(%s): %s", fctx, fctx->info, msgbuf);
4434254402Serwin}
4435254402Serwin
4436135446Strhodesstatic inline isc_result_t
4437254402Serwinfindnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
4438254402Serwin	    dns_name_t **noqnamep)
4439254402Serwin{
4440254402Serwin	dns_rdataset_t *nrdataset, *next, *sigrdataset;
4441254402Serwin	dns_rdata_rrsig_t rrsig;
4442254402Serwin	isc_result_t result;
4443254402Serwin	unsigned int labels;
4444254402Serwin	dns_section_t section;
4445254402Serwin	dns_name_t *zonename;
4446254402Serwin	dns_fixedname_t fzonename;
4447254402Serwin	dns_name_t *closest;
4448254402Serwin	dns_fixedname_t fclosest;
4449254402Serwin	dns_name_t *nearest;
4450254402Serwin	dns_fixedname_t fnearest;
4451254402Serwin	dns_rdatatype_t found = dns_rdatatype_none;
4452254402Serwin	dns_name_t *noqname = NULL;
4453254402Serwin
4454254402Serwin	FCTXTRACE("findnoqname");
4455254402Serwin
4456254402Serwin	REQUIRE(noqnamep != NULL && *noqnamep == NULL);
4457254402Serwin
4458254402Serwin	/*
4459254402Serwin	 * Find the SIG for this rdataset, if we have it.
4460254402Serwin	 */
4461254402Serwin	for (sigrdataset = ISC_LIST_HEAD(name->list);
4462254402Serwin	     sigrdataset != NULL;
4463254402Serwin	     sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
4464254402Serwin		if (sigrdataset->type == dns_rdatatype_rrsig &&
4465254402Serwin		    sigrdataset->covers == type)
4466254402Serwin			break;
4467254402Serwin	}
4468254402Serwin
4469254402Serwin	if (sigrdataset == NULL)
4470254402Serwin		return (ISC_R_NOTFOUND);
4471254402Serwin
4472254402Serwin	labels = dns_name_countlabels(name);
4473254402Serwin
4474254402Serwin	for (result = dns_rdataset_first(sigrdataset);
4475254402Serwin	     result == ISC_R_SUCCESS;
4476254402Serwin	     result = dns_rdataset_next(sigrdataset)) {
4477254402Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
4478254402Serwin		dns_rdataset_current(sigrdataset, &rdata);
4479254402Serwin		result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
4480254402Serwin		RUNTIME_CHECK(result == ISC_R_SUCCESS);
4481254402Serwin		/* Wildcard has rrsig.labels < labels - 1. */
4482254402Serwin		if (rrsig.labels + 1U >= labels)
4483254402Serwin			continue;
4484254402Serwin		break;
4485254402Serwin	}
4486254402Serwin
4487254402Serwin	if (result == ISC_R_NOMORE)
4488254402Serwin		return (ISC_R_NOTFOUND);
4489254402Serwin	if (result != ISC_R_SUCCESS)
4490254402Serwin		return (result);
4491254402Serwin
4492254402Serwin	dns_fixedname_init(&fzonename);
4493254402Serwin	zonename = dns_fixedname_name(&fzonename);
4494254402Serwin	dns_fixedname_init(&fclosest);
4495254402Serwin	closest = dns_fixedname_name(&fclosest);
4496254402Serwin	dns_fixedname_init(&fnearest);
4497254402Serwin	nearest = dns_fixedname_name(&fnearest);
4498254402Serwin
4499254402Serwin#define NXND(x) ((x) == ISC_R_SUCCESS)
4500254402Serwin
4501254402Serwin	section = DNS_SECTION_AUTHORITY;
4502254402Serwin	for (result = dns_message_firstname(fctx->rmessage, section);
4503254402Serwin	     result == ISC_R_SUCCESS;
4504254402Serwin	     result = dns_message_nextname(fctx->rmessage, section)) {
4505254402Serwin		dns_name_t *nsec = NULL;
4506254402Serwin		dns_message_currentname(fctx->rmessage, section, &nsec);
4507254402Serwin		for (nrdataset = ISC_LIST_HEAD(nsec->list);
4508254402Serwin		      nrdataset != NULL; nrdataset = next) {
4509254402Serwin			isc_boolean_t data = ISC_FALSE, exists = ISC_FALSE;
4510254402Serwin			isc_boolean_t optout = ISC_FALSE, unknown = ISC_FALSE;
4511254402Serwin			isc_boolean_t setclosest = ISC_FALSE;
4512254402Serwin			isc_boolean_t setnearest = ISC_FALSE;
4513254402Serwin
4514254402Serwin			next = ISC_LIST_NEXT(nrdataset, link);
4515254402Serwin			if (nrdataset->type != dns_rdatatype_nsec &&
4516254402Serwin			    nrdataset->type != dns_rdatatype_nsec3)
4517254402Serwin				continue;
4518254402Serwin
4519254402Serwin			if (nrdataset->type == dns_rdatatype_nsec &&
4520254402Serwin			    NXND(dns_nsec_noexistnodata(type, name, nsec,
4521254402Serwin							nrdataset, &exists,
4522254402Serwin							&data, NULL, fctx_log,
4523254402Serwin							fctx)))
4524254402Serwin			{
4525254402Serwin				if (!exists) {
4526254402Serwin					noqname = nsec;
4527254402Serwin					found = dns_rdatatype_nsec;
4528254402Serwin				}
4529254402Serwin			}
4530254402Serwin
4531254402Serwin			if (nrdataset->type == dns_rdatatype_nsec3 &&
4532254402Serwin			    NXND(dns_nsec3_noexistnodata(type, name, nsec,
4533254402Serwin							 nrdataset, zonename,
4534254402Serwin							 &exists, &data,
4535254402Serwin							 &optout, &unknown,
4536254402Serwin							 &setclosest,
4537254402Serwin							 &setnearest,
4538254402Serwin							 closest, nearest,
4539254402Serwin							 fctx_log, fctx)))
4540254402Serwin			{
4541254402Serwin				if (!exists && setnearest) {
4542254402Serwin					noqname = nsec;
4543254402Serwin					found = dns_rdatatype_nsec3;
4544254402Serwin				}
4545254402Serwin			}
4546254402Serwin		}
4547254402Serwin	}
4548254402Serwin	if (result == ISC_R_NOMORE)
4549254402Serwin		result = ISC_R_SUCCESS;
4550254402Serwin	if (noqname != NULL) {
4551254402Serwin		for (sigrdataset = ISC_LIST_HEAD(noqname->list);
4552254402Serwin		     sigrdataset != NULL;
4553254402Serwin		     sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
4554254402Serwin			if (sigrdataset->type == dns_rdatatype_rrsig &&
4555254402Serwin			    sigrdataset->covers == found)
4556254402Serwin				break;
4557254402Serwin		}
4558254402Serwin		if (sigrdataset != NULL)
4559254402Serwin			*noqnamep = noqname;
4560254402Serwin	}
4561254402Serwin	return (result);
4562254402Serwin}
4563254402Serwin
4564254402Serwinstatic inline isc_result_t
4565153816Sdougbcache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
4566174187Sdougb	   isc_stdtime_t now)
4567174187Sdougb{
4568135446Strhodes	dns_rdataset_t *rdataset, *sigrdataset;
4569135446Strhodes	dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset;
4570135446Strhodes	dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL;
4571135446Strhodes	dns_dbnode_t *node, **anodep;
4572135446Strhodes	dns_db_t **adbp;
4573135446Strhodes	dns_name_t *aname;
4574135446Strhodes	dns_resolver_t *res;
4575135446Strhodes	isc_boolean_t need_validation, secure_domain, have_answer;
4576135446Strhodes	isc_result_t result, eresult;
4577135446Strhodes	dns_fetchevent_t *event;
4578135446Strhodes	unsigned int options;
4579135446Strhodes	isc_task_t *task;
4580135446Strhodes	isc_boolean_t fail;
4581153816Sdougb	unsigned int valoptions = 0;
4582135446Strhodes
4583135446Strhodes	/*
4584135446Strhodes	 * The appropriate bucket lock must be held.
4585135446Strhodes	 */
4586135446Strhodes
4587135446Strhodes	res = fctx->res;
4588135446Strhodes	need_validation = ISC_FALSE;
4589225361Sdougb	POST(need_validation);
4590135446Strhodes	secure_domain = ISC_FALSE;
4591135446Strhodes	have_answer = ISC_FALSE;
4592135446Strhodes	eresult = ISC_R_SUCCESS;
4593135446Strhodes	task = res->buckets[fctx->bucketnum].task;
4594135446Strhodes
4595135446Strhodes	/*
4596135446Strhodes	 * Is DNSSEC validation required for this name?
4597135446Strhodes	 */
4598170222Sdougb	if (res->view->enablevalidation) {
4599224092Sdougb		result = dns_view_issecuredomain(res->view, name,
4600224092Sdougb						 &secure_domain);
4601170222Sdougb		if (result != ISC_R_SUCCESS)
4602170222Sdougb			return (result);
4603135446Strhodes
4604170222Sdougb		if (!secure_domain && res->view->dlv != NULL) {
4605170222Sdougb			valoptions = DNS_VALIDATOR_DLV;
4606170222Sdougb			secure_domain = ISC_TRUE;
4607170222Sdougb		}
4608153816Sdougb	}
4609135446Strhodes
4610135446Strhodes	if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
4611135446Strhodes		need_validation = ISC_FALSE;
4612135446Strhodes	else
4613135446Strhodes		need_validation = secure_domain;
4614135446Strhodes
4615135446Strhodes	adbp = NULL;
4616135446Strhodes	aname = NULL;
4617135446Strhodes	anodep = NULL;
4618135446Strhodes	ardataset = NULL;
4619135446Strhodes	asigrdataset = NULL;
4620135446Strhodes	event = NULL;
4621135446Strhodes	if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 &&
4622135446Strhodes	    !need_validation) {
4623135446Strhodes		have_answer = ISC_TRUE;
4624135446Strhodes		event = ISC_LIST_HEAD(fctx->events);
4625135446Strhodes		if (event != NULL) {
4626135446Strhodes			adbp = &event->db;
4627135446Strhodes			aname = dns_fixedname_name(&event->foundname);
4628135446Strhodes			result = dns_name_copy(name, aname, NULL);
4629135446Strhodes			if (result != ISC_R_SUCCESS)
4630135446Strhodes				return (result);
4631135446Strhodes			anodep = &event->node;
4632135446Strhodes			/*
4633162079Sdougb			 * If this is an ANY, SIG or RRSIG query, we're not
4634162079Sdougb			 * going to return any rdatasets, unless we encountered
4635135446Strhodes			 * a CNAME or DNAME as "the answer".  In this case,
4636135446Strhodes			 * we're going to return DNS_R_CNAME or DNS_R_DNAME
4637135446Strhodes			 * and we must set up the rdatasets.
4638135446Strhodes			 */
4639135446Strhodes			if ((fctx->type != dns_rdatatype_any &&
4640162079Sdougb			     fctx->type != dns_rdatatype_rrsig &&
4641162079Sdougb			     fctx->type != dns_rdatatype_sig) ||
4642135446Strhodes			    (name->attributes & DNS_NAMEATTR_CHAINING) != 0) {
4643135446Strhodes				ardataset = event->rdataset;
4644135446Strhodes				asigrdataset = event->sigrdataset;
4645135446Strhodes			}
4646135446Strhodes		}
4647135446Strhodes	}
4648135446Strhodes
4649135446Strhodes	/*
4650135446Strhodes	 * Find or create the cache node.
4651135446Strhodes	 */
4652135446Strhodes	node = NULL;
4653135446Strhodes	result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node);
4654135446Strhodes	if (result != ISC_R_SUCCESS)
4655135446Strhodes		return (result);
4656135446Strhodes
4657135446Strhodes	/*
4658135446Strhodes	 * Cache or validate each cacheable rdataset.
4659135446Strhodes	 */
4660153816Sdougb	fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0);
4661135446Strhodes	for (rdataset = ISC_LIST_HEAD(name->list);
4662135446Strhodes	     rdataset != NULL;
4663135446Strhodes	     rdataset = ISC_LIST_NEXT(rdataset, link)) {
4664135446Strhodes		if (!CACHE(rdataset))
4665135446Strhodes			continue;
4666135446Strhodes		if (CHECKNAMES(rdataset)) {
4667135446Strhodes			char namebuf[DNS_NAME_FORMATSIZE];
4668135446Strhodes			char typebuf[DNS_RDATATYPE_FORMATSIZE];
4669135446Strhodes			char classbuf[DNS_RDATATYPE_FORMATSIZE];
4670135446Strhodes
4671135446Strhodes			dns_name_format(name, namebuf, sizeof(namebuf));
4672135446Strhodes			dns_rdatatype_format(rdataset->type, typebuf,
4673135446Strhodes					     sizeof(typebuf));
4674135446Strhodes			dns_rdataclass_format(rdataset->rdclass, classbuf,
4675135446Strhodes					      sizeof(classbuf));
4676186462Sdougb			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
4677135446Strhodes				      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
4678186462Sdougb				      "check-names %s %s/%s/%s",
4679135446Strhodes				      fail ? "failure" : "warning",
4680135446Strhodes				      namebuf, typebuf, classbuf);
4681135446Strhodes			if (fail) {
4682174187Sdougb				if (ANSWER(rdataset)) {
4683174187Sdougb					dns_db_detachnode(fctx->cache, &node);
4684135446Strhodes					return (DNS_R_BADNAME);
4685174187Sdougb				}
4686135446Strhodes				continue;
4687135446Strhodes			}
4688135446Strhodes		}
4689135446Strhodes
4690135446Strhodes		/*
4691135446Strhodes		 * Enforce the configure maximum cache TTL.
4692135446Strhodes		 */
4693135446Strhodes		if (rdataset->ttl > res->view->maxcachettl)
4694135446Strhodes			rdataset->ttl = res->view->maxcachettl;
4695135446Strhodes
4696135446Strhodes		/*
4697254402Serwin		 * Find the SIG for this rdataset, if we have it.
4698254402Serwin		 */
4699254402Serwin		for (sigrdataset = ISC_LIST_HEAD(name->list);
4700254402Serwin		     sigrdataset != NULL;
4701254402Serwin		     sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
4702254402Serwin			if (sigrdataset->type == dns_rdatatype_rrsig &&
4703254402Serwin			    sigrdataset->covers == rdataset->type)
4704254402Serwin				break;
4705254402Serwin		}
4706254402Serwin
4707254402Serwin		/*
4708202961Sdougb		 * If this RRset is in a secure domain, is in bailiwick,
4709202961Sdougb		 * and is not glue, attempt DNSSEC validation.	(We do not
4710202961Sdougb		 * attempt to validate glue or out-of-bailiwick data--even
4711202961Sdougb		 * though there might be some performance benefit to doing
4712202961Sdougb		 * so--because it makes it simpler and safer to ensure that
4713202961Sdougb		 * records from a secure domain are only cached if validated
4714202961Sdougb		 * within the context of a query to the domain that owns
4715202961Sdougb		 * them.)
4716135446Strhodes		 */
4717202961Sdougb		if (secure_domain && rdataset->trust != dns_trust_glue &&
4718202961Sdougb		    !EXTERNAL(rdataset)) {
4719199958Sdougb			dns_trust_t trust;
4720202961Sdougb
4721135446Strhodes			/*
4722162079Sdougb			 * RRSIGs are validated as part of validating the
4723135446Strhodes			 * type they cover.
4724135446Strhodes			 */
4725135446Strhodes			if (rdataset->type == dns_rdatatype_rrsig)
4726135446Strhodes				continue;
4727254402Serwin
4728135446Strhodes			if (sigrdataset == NULL) {
4729135446Strhodes				if (!ANSWER(rdataset) && need_validation) {
4730135446Strhodes					/*
4731135446Strhodes					 * Ignore non-answer rdatasets that
4732135446Strhodes					 * are missing signatures.
4733135446Strhodes					 */
4734135446Strhodes					continue;
4735135446Strhodes				}
4736135446Strhodes			}
4737135446Strhodes
4738135446Strhodes			/*
4739135446Strhodes			 * Normalize the rdataset and sigrdataset TTLs.
4740135446Strhodes			 */
4741135446Strhodes			if (sigrdataset != NULL) {
4742135446Strhodes				rdataset->ttl = ISC_MIN(rdataset->ttl,
4743135446Strhodes							sigrdataset->ttl);
4744135446Strhodes				sigrdataset->ttl = rdataset->ttl;
4745135446Strhodes			}
4746135446Strhodes
4747135446Strhodes			/*
4748135446Strhodes			 * Cache this rdataset/sigrdataset pair as
4749199958Sdougb			 * pending data.  Track whether it was additional
4750199958Sdougb			 * or not.
4751135446Strhodes			 */
4752199958Sdougb			if (rdataset->trust == dns_trust_additional)
4753199958Sdougb				trust = dns_trust_pending_additional;
4754199958Sdougb			else
4755199958Sdougb				trust = dns_trust_pending_answer;
4756199958Sdougb
4757199958Sdougb			rdataset->trust = trust;
4758135446Strhodes			if (sigrdataset != NULL)
4759199958Sdougb				sigrdataset->trust = trust;
4760193149Sdougb			if (!need_validation || !ANSWER(rdataset)) {
4761254402Serwin				if (ANSWER(rdataset) &&
4762254402Serwin				   rdataset->type != dns_rdatatype_rrsig) {
4763254402Serwin					isc_result_t tresult;
4764254402Serwin					dns_name_t *noqname = NULL;
4765254402Serwin					tresult = findnoqname(fctx, name,
4766254402Serwin							      rdataset->type,
4767254402Serwin							      &noqname);
4768254402Serwin					if (tresult == ISC_R_SUCCESS &&
4769254402Serwin					    noqname != NULL) {
4770254402Serwin						tresult =
4771254402Serwin						     dns_rdataset_addnoqname(
4772254402Serwin							    rdataset, noqname);
4773254402Serwin						RUNTIME_CHECK(tresult ==
4774254402Serwin							      ISC_R_SUCCESS);
4775254402Serwin					}
4776254402Serwin				}
4777135446Strhodes				addedrdataset = ardataset;
4778193149Sdougb				result = dns_db_addrdataset(fctx->cache, node,
4779193149Sdougb							    NULL, now, rdataset,
4780193149Sdougb							    0, addedrdataset);
4781193149Sdougb				if (result == DNS_R_UNCHANGED) {
4782193149Sdougb					result = ISC_R_SUCCESS;
4783193149Sdougb					if (!need_validation &&
4784193149Sdougb					    ardataset != NULL &&
4785223812Sdougb					    NEGATIVE(ardataset)) {
4786193149Sdougb						/*
4787193149Sdougb						 * The answer in the cache is
4788193149Sdougb						 * better than the answer we
4789193149Sdougb						 * found, and is a negative
4790193149Sdougb						 * cache entry, so we must set
4791193149Sdougb						 * eresult appropriately.
4792193149Sdougb						 */
4793193149Sdougb						if (NXDOMAIN(ardataset))
4794193149Sdougb							eresult =
4795193149Sdougb							   DNS_R_NCACHENXDOMAIN;
4796193149Sdougb						else
4797193149Sdougb							eresult =
4798193149Sdougb							   DNS_R_NCACHENXRRSET;
4799193149Sdougb						/*
4800193149Sdougb						 * We have a negative response
4801193149Sdougb						 * from the cache so don't
4802193149Sdougb						 * attempt to add the RRSIG
4803193149Sdougb						 * rrset.
4804193149Sdougb						 */
4805193149Sdougb						continue;
4806193149Sdougb					}
4807174187Sdougb				}
4808135446Strhodes				if (result != ISC_R_SUCCESS)
4809135446Strhodes					break;
4810193149Sdougb				if (sigrdataset != NULL) {
4811193149Sdougb					addedrdataset = asigrdataset;
4812193149Sdougb					result = dns_db_addrdataset(fctx->cache,
4813193149Sdougb								node, NULL, now,
4814193149Sdougb								sigrdataset, 0,
4815193149Sdougb								addedrdataset);
4816193149Sdougb					if (result == DNS_R_UNCHANGED)
4817193149Sdougb						result = ISC_R_SUCCESS;
4818193149Sdougb					if (result != ISC_R_SUCCESS)
4819193149Sdougb						break;
4820193149Sdougb				} else if (!ANSWER(rdataset))
4821193149Sdougb					continue;
4822193149Sdougb			}
4823135446Strhodes
4824135446Strhodes			if (ANSWER(rdataset) && need_validation) {
4825135446Strhodes				if (fctx->type != dns_rdatatype_any &&
4826162079Sdougb				    fctx->type != dns_rdatatype_rrsig &&
4827162079Sdougb				    fctx->type != dns_rdatatype_sig) {
4828135446Strhodes					/*
4829135446Strhodes					 * This is The Answer.  We will
4830135446Strhodes					 * validate it, but first we cache
4831135446Strhodes					 * the rest of the response - it may
4832135446Strhodes					 * contain useful keys.
4833135446Strhodes					 */
4834135446Strhodes					INSIST(valrdataset == NULL &&
4835135446Strhodes					       valsigrdataset == NULL);
4836135446Strhodes					valrdataset = rdataset;
4837135446Strhodes					valsigrdataset = sigrdataset;
4838135446Strhodes				} else {
4839135446Strhodes					/*
4840135446Strhodes					 * This is one of (potentially)
4841135446Strhodes					 * multiple answers to an ANY
4842135446Strhodes					 * or SIG query.  To keep things
4843135446Strhodes					 * simple, we just start the
4844135446Strhodes					 * validator right away rather
4845135446Strhodes					 * than caching first and
4846135446Strhodes					 * having to remember which
4847135446Strhodes					 * rdatasets needed validation.
4848135446Strhodes					 */
4849153816Sdougb					result = valcreate(fctx, addrinfo,
4850153816Sdougb							   name, rdataset->type,
4851153816Sdougb							   rdataset,
4852153816Sdougb							   sigrdataset,
4853153816Sdougb							   valoptions, task);
4854135446Strhodes				}
4855143731Sdougb			} else if (CHAINING(rdataset)) {
4856143731Sdougb				if (rdataset->type == dns_rdatatype_cname)
4857143731Sdougb					eresult = DNS_R_CNAME;
4858143731Sdougb				else {
4859143731Sdougb					INSIST(rdataset->type ==
4860143731Sdougb					       dns_rdatatype_dname);
4861143731Sdougb					eresult = DNS_R_DNAME;
4862143731Sdougb				}
4863135446Strhodes			}
4864135446Strhodes		} else if (!EXTERNAL(rdataset)) {
4865135446Strhodes			/*
4866135446Strhodes			 * It's OK to cache this rdataset now.
4867135446Strhodes			 */
4868135446Strhodes			if (ANSWER(rdataset))
4869135446Strhodes				addedrdataset = ardataset;
4870135446Strhodes			else if (ANSWERSIG(rdataset))
4871135446Strhodes				addedrdataset = asigrdataset;
4872135446Strhodes			else
4873135446Strhodes				addedrdataset = NULL;
4874135446Strhodes			if (CHAINING(rdataset)) {
4875135446Strhodes				if (rdataset->type == dns_rdatatype_cname)
4876135446Strhodes					eresult = DNS_R_CNAME;
4877135446Strhodes				else {
4878135446Strhodes					INSIST(rdataset->type ==
4879135446Strhodes					       dns_rdatatype_dname);
4880135446Strhodes					eresult = DNS_R_DNAME;
4881135446Strhodes				}
4882135446Strhodes			}
4883135446Strhodes			if (rdataset->trust == dns_trust_glue &&
4884135446Strhodes			    (rdataset->type == dns_rdatatype_ns ||
4885135446Strhodes			     (rdataset->type == dns_rdatatype_rrsig &&
4886135446Strhodes			      rdataset->covers == dns_rdatatype_ns))) {
4887135446Strhodes				/*
4888135446Strhodes				 * If the trust level is 'dns_trust_glue'
4889135446Strhodes				 * then we are adding data from a referral
4890135446Strhodes				 * we got while executing the search algorithm.
4891135446Strhodes				 * New referral data always takes precedence
4892135446Strhodes				 * over the existing cache contents.
4893135446Strhodes				 */
4894135446Strhodes				options = DNS_DBADD_FORCE;
4895135446Strhodes			} else
4896135446Strhodes				options = 0;
4897254402Serwin
4898254402Serwin			if (ANSWER(rdataset) &&
4899254402Serwin			   rdataset->type != dns_rdatatype_rrsig) {
4900254402Serwin				isc_result_t tresult;
4901254402Serwin				dns_name_t *noqname = NULL;
4902254402Serwin				tresult = findnoqname(fctx, name,
4903254402Serwin						      rdataset->type, &noqname);
4904254402Serwin				if (tresult == ISC_R_SUCCESS &&
4905254402Serwin				    noqname != NULL) {
4906254402Serwin					tresult = dns_rdataset_addnoqname(
4907254402Serwin							    rdataset, noqname);
4908254402Serwin					RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
4909254402Serwin				}
4910254402Serwin			}
4911254402Serwin
4912135446Strhodes			/*
4913135446Strhodes			 * Now we can add the rdataset.
4914135446Strhodes			 */
4915135446Strhodes			result = dns_db_addrdataset(fctx->cache,
4916135446Strhodes						    node, NULL, now,
4917135446Strhodes						    rdataset,
4918135446Strhodes						    options,
4919135446Strhodes						    addedrdataset);
4920254402Serwin
4921135446Strhodes			if (result == DNS_R_UNCHANGED) {
4922135446Strhodes				if (ANSWER(rdataset) &&
4923135446Strhodes				    ardataset != NULL &&
4924223812Sdougb				    NEGATIVE(ardataset)) {
4925135446Strhodes					/*
4926135446Strhodes					 * The answer in the cache is better
4927135446Strhodes					 * than the answer we found, and is
4928135446Strhodes					 * a negative cache entry, so we
4929135446Strhodes					 * must set eresult appropriately.
4930135446Strhodes					 */
4931174187Sdougb					if (NXDOMAIN(ardataset))
4932174187Sdougb						eresult = DNS_R_NCACHENXDOMAIN;
4933174187Sdougb					else
4934174187Sdougb						eresult = DNS_R_NCACHENXRRSET;
4935135446Strhodes				}
4936135446Strhodes				result = ISC_R_SUCCESS;
4937135446Strhodes			} else if (result != ISC_R_SUCCESS)
4938135446Strhodes				break;
4939135446Strhodes		}
4940135446Strhodes	}
4941135446Strhodes
4942153816Sdougb	if (valrdataset != NULL)
4943153816Sdougb		result = valcreate(fctx, addrinfo, name, fctx->type,
4944153816Sdougb				   valrdataset, valsigrdataset, valoptions,
4945174187Sdougb				   task);
4946135446Strhodes
4947135446Strhodes	if (result == ISC_R_SUCCESS && have_answer) {
4948135446Strhodes		fctx->attributes |= FCTX_ATTR_HAVEANSWER;
4949135446Strhodes		if (event != NULL) {
4950174187Sdougb			/*
4951174187Sdougb			 * Negative results must be indicated in event->result.
4952174187Sdougb			 */
4953174187Sdougb			if (dns_rdataset_isassociated(event->rdataset) &&
4954223812Sdougb			    NEGATIVE(event->rdataset)) {
4955174187Sdougb				INSIST(eresult == DNS_R_NCACHENXDOMAIN ||
4956174187Sdougb				       eresult == DNS_R_NCACHENXRRSET);
4957174187Sdougb			}
4958135446Strhodes			event->result = eresult;
4959308205Sdelphij			if (adbp != NULL && *adbp != NULL) {
4960308205Sdelphij				if (anodep != NULL && *anodep != NULL)
4961308205Sdelphij					dns_db_detachnode(*adbp, anodep);
4962308205Sdelphij				dns_db_detach(adbp);
4963308205Sdelphij			}
4964135446Strhodes			dns_db_attach(fctx->cache, adbp);
4965174187Sdougb			dns_db_transfernode(fctx->cache, &node, anodep);
4966135446Strhodes			clone_results(fctx);
4967135446Strhodes		}
4968135446Strhodes	}
4969135446Strhodes
4970135446Strhodes	if (node != NULL)
4971135446Strhodes		dns_db_detachnode(fctx->cache, &node);
4972135446Strhodes
4973135446Strhodes	return (result);
4974135446Strhodes}
4975135446Strhodes
4976135446Strhodesstatic inline isc_result_t
4977153816Sdougbcache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now)
4978153816Sdougb{
4979135446Strhodes	isc_result_t result;
4980135446Strhodes	dns_section_t section;
4981135446Strhodes	dns_name_t *name;
4982135446Strhodes
4983135446Strhodes	FCTXTRACE("cache_message");
4984135446Strhodes
4985135446Strhodes	fctx->attributes &= ~FCTX_ATTR_WANTCACHE;
4986135446Strhodes
4987135446Strhodes	LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
4988135446Strhodes
4989135446Strhodes	for (section = DNS_SECTION_ANSWER;
4990135446Strhodes	     section <= DNS_SECTION_ADDITIONAL;
4991135446Strhodes	     section++) {
4992135446Strhodes		result = dns_message_firstname(fctx->rmessage, section);
4993135446Strhodes		while (result == ISC_R_SUCCESS) {
4994135446Strhodes			name = NULL;
4995135446Strhodes			dns_message_currentname(fctx->rmessage, section,
4996135446Strhodes						&name);
4997135446Strhodes			if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) {
4998153816Sdougb				result = cache_name(fctx, name, addrinfo, now);
4999135446Strhodes				if (result != ISC_R_SUCCESS)
5000135446Strhodes					break;
5001135446Strhodes			}
5002135446Strhodes			result = dns_message_nextname(fctx->rmessage, section);
5003135446Strhodes		}
5004135446Strhodes		if (result != ISC_R_NOMORE)
5005135446Strhodes			break;
5006135446Strhodes	}
5007135446Strhodes	if (result == ISC_R_NOMORE)
5008135446Strhodes		result = ISC_R_SUCCESS;
5009135446Strhodes
5010135446Strhodes	UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
5011135446Strhodes
5012135446Strhodes	return (result);
5013135446Strhodes}
5014135446Strhodes
5015135446Strhodes/*
5016193149Sdougb * Do what dns_ncache_addoptout() does, and then compute an appropriate eresult.
5017135446Strhodes */
5018135446Strhodesstatic isc_result_t
5019135446Strhodesncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
5020135446Strhodes		  dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
5021254402Serwin		  isc_boolean_t optout, isc_boolean_t secure,
5022254402Serwin		  dns_rdataset_t *ardataset, isc_result_t *eresultp)
5023135446Strhodes{
5024135446Strhodes	isc_result_t result;
5025162079Sdougb	dns_rdataset_t rdataset;
5026162079Sdougb
5027162079Sdougb	if (ardataset == NULL) {
5028162079Sdougb		dns_rdataset_init(&rdataset);
5029162079Sdougb		ardataset = &rdataset;
5030162079Sdougb	}
5031254402Serwin	if (secure)
5032254402Serwin		result = dns_ncache_addoptout(message, cache, node, covers,
5033254402Serwin					      now, maxttl, optout, ardataset);
5034254402Serwin	else
5035254402Serwin		result = dns_ncache_add(message, cache, node, covers, now,
5036254402Serwin					maxttl, ardataset);
5037162079Sdougb	if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) {
5038135446Strhodes		/*
5039162079Sdougb		 * If the cache now contains a negative entry and we
5040162079Sdougb		 * care about whether it is DNS_R_NCACHENXDOMAIN or
5041162079Sdougb		 * DNS_R_NCACHENXRRSET then extract it.
5042135446Strhodes		 */
5043223812Sdougb		if (NEGATIVE(ardataset)) {
5044135446Strhodes			/*
5045162079Sdougb			 * The cache data is a negative cache entry.
5046135446Strhodes			 */
5047135446Strhodes			if (NXDOMAIN(ardataset))
5048135446Strhodes				*eresultp = DNS_R_NCACHENXDOMAIN;
5049135446Strhodes			else
5050135446Strhodes				*eresultp = DNS_R_NCACHENXRRSET;
5051135446Strhodes		} else {
5052135446Strhodes			/*
5053135446Strhodes			 * Either we don't care about the nature of the
5054135446Strhodes			 * cache rdataset (because no fetch is interested
5055135446Strhodes			 * in the outcome), or the cache rdataset is not
5056135446Strhodes			 * a negative cache entry.  Whichever case it is,
5057135446Strhodes			 * we can return success.
5058135446Strhodes			 *
5059135446Strhodes			 * XXXRTH  There's a CNAME/DNAME problem here.
5060135446Strhodes			 */
5061135446Strhodes			*eresultp = ISC_R_SUCCESS;
5062135446Strhodes		}
5063162079Sdougb		result = ISC_R_SUCCESS;
5064135446Strhodes	}
5065162079Sdougb	if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset))
5066162079Sdougb		dns_rdataset_disassociate(ardataset);
5067135446Strhodes
5068135446Strhodes	return (result);
5069135446Strhodes}
5070135446Strhodes
5071135446Strhodesstatic inline isc_result_t
5072153816Sdougbncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
5073153816Sdougb	       dns_rdatatype_t covers, isc_stdtime_t now)
5074153816Sdougb{
5075135446Strhodes	isc_result_t result, eresult;
5076135446Strhodes	dns_name_t *name;
5077135446Strhodes	dns_resolver_t *res;
5078135446Strhodes	dns_db_t **adbp;
5079135446Strhodes	dns_dbnode_t *node, **anodep;
5080135446Strhodes	dns_rdataset_t *ardataset;
5081135446Strhodes	isc_boolean_t need_validation, secure_domain;
5082135446Strhodes	dns_name_t *aname;
5083135446Strhodes	dns_fetchevent_t *event;
5084135446Strhodes	isc_uint32_t ttl;
5085153816Sdougb	unsigned int valoptions = 0;
5086135446Strhodes
5087135446Strhodes	FCTXTRACE("ncache_message");
5088135446Strhodes
5089135446Strhodes	fctx->attributes &= ~FCTX_ATTR_WANTNCACHE;
5090135446Strhodes
5091135446Strhodes	res = fctx->res;
5092135446Strhodes	need_validation = ISC_FALSE;
5093225361Sdougb	POST(need_validation);
5094135446Strhodes	secure_domain = ISC_FALSE;
5095135446Strhodes	eresult = ISC_R_SUCCESS;
5096135446Strhodes	name = &fctx->name;
5097135446Strhodes	node = NULL;
5098135446Strhodes
5099135446Strhodes	/*
5100135446Strhodes	 * XXXMPA remove when we follow cnames and adjust the setting
5101135446Strhodes	 * of FCTX_ATTR_WANTNCACHE in noanswer_response().
5102135446Strhodes	 */
5103135446Strhodes	INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0);
5104135446Strhodes
5105135446Strhodes	/*
5106135446Strhodes	 * Is DNSSEC validation required for this name?
5107135446Strhodes	 */
5108170222Sdougb	if (fctx->res->view->enablevalidation) {
5109224092Sdougb		result = dns_view_issecuredomain(res->view, name,
5110224092Sdougb						 &secure_domain);
5111170222Sdougb		if (result != ISC_R_SUCCESS)
5112170222Sdougb			return (result);
5113135446Strhodes
5114170222Sdougb		if (!secure_domain && res->view->dlv != NULL) {
5115170222Sdougb			valoptions = DNS_VALIDATOR_DLV;
5116170222Sdougb			secure_domain = ISC_TRUE;
5117170222Sdougb		}
5118153816Sdougb	}
5119135446Strhodes
5120135446Strhodes	if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
5121135446Strhodes		need_validation = ISC_FALSE;
5122135446Strhodes	else
5123135446Strhodes		need_validation = secure_domain;
5124135446Strhodes
5125135446Strhodes	if (secure_domain) {
5126135446Strhodes		/*
5127135446Strhodes		 * Mark all rdatasets as pending.
5128135446Strhodes		 */
5129135446Strhodes		dns_rdataset_t *trdataset;
5130135446Strhodes		dns_name_t *tname;
5131135446Strhodes
5132135446Strhodes		result = dns_message_firstname(fctx->rmessage,
5133135446Strhodes					       DNS_SECTION_AUTHORITY);
5134135446Strhodes		while (result == ISC_R_SUCCESS) {
5135135446Strhodes			tname = NULL;
5136135446Strhodes			dns_message_currentname(fctx->rmessage,
5137135446Strhodes						DNS_SECTION_AUTHORITY,
5138135446Strhodes						&tname);
5139135446Strhodes			for (trdataset = ISC_LIST_HEAD(tname->list);
5140135446Strhodes			     trdataset != NULL;
5141135446Strhodes			     trdataset = ISC_LIST_NEXT(trdataset, link))
5142199958Sdougb				trdataset->trust = dns_trust_pending_answer;
5143135446Strhodes			result = dns_message_nextname(fctx->rmessage,
5144135446Strhodes						      DNS_SECTION_AUTHORITY);
5145135446Strhodes		}
5146135446Strhodes		if (result != ISC_R_NOMORE)
5147135446Strhodes			return (result);
5148135446Strhodes
5149135446Strhodes	}
5150135446Strhodes
5151135446Strhodes	if (need_validation) {
5152135446Strhodes		/*
5153135446Strhodes		 * Do negative response validation.
5154135446Strhodes		 */
5155153816Sdougb		result = valcreate(fctx, addrinfo, name, fctx->type,
5156153816Sdougb				   NULL, NULL, valoptions,
5157153816Sdougb				   res->buckets[fctx->bucketnum].task);
5158135446Strhodes		/*
5159135446Strhodes		 * If validation is necessary, return now.  Otherwise continue
5160135446Strhodes		 * to process the message, letting the validation complete
5161135446Strhodes		 * in its own good time.
5162135446Strhodes		 */
5163153816Sdougb		return (result);
5164135446Strhodes	}
5165135446Strhodes
5166135446Strhodes	LOCK(&res->buckets[fctx->bucketnum].lock);
5167135446Strhodes
5168135446Strhodes	adbp = NULL;
5169135446Strhodes	aname = NULL;
5170135446Strhodes	anodep = NULL;
5171135446Strhodes	ardataset = NULL;
5172135446Strhodes	if (!HAVE_ANSWER(fctx)) {
5173135446Strhodes		event = ISC_LIST_HEAD(fctx->events);
5174135446Strhodes		if (event != NULL) {
5175135446Strhodes			adbp = &event->db;
5176135446Strhodes			aname = dns_fixedname_name(&event->foundname);
5177135446Strhodes			result = dns_name_copy(name, aname, NULL);
5178135446Strhodes			if (result != ISC_R_SUCCESS)
5179135446Strhodes				goto unlock;
5180135446Strhodes			anodep = &event->node;
5181135446Strhodes			ardataset = event->rdataset;
5182135446Strhodes		}
5183135446Strhodes	} else
5184135446Strhodes		event = NULL;
5185135446Strhodes
5186135446Strhodes	result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node);
5187135446Strhodes	if (result != ISC_R_SUCCESS)
5188135446Strhodes		goto unlock;
5189135446Strhodes
5190135446Strhodes	/*
5191135446Strhodes	 * If we are asking for a SOA record set the cache time
5192135446Strhodes	 * to zero to facilitate locating the containing zone of
5193193149Sdougb	 * a arbitrary zone.
5194135446Strhodes	 */
5195135446Strhodes	ttl = fctx->res->view->maxncachettl;
5196135446Strhodes	if (fctx->type == dns_rdatatype_soa &&
5197193149Sdougb	    covers == dns_rdatatype_any &&
5198193149Sdougb	    fctx->res->zero_no_soa_ttl)
5199135446Strhodes		ttl = 0;
5200135446Strhodes
5201135446Strhodes	result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
5202193149Sdougb				   covers, now, ttl, ISC_FALSE,
5203254402Serwin				   ISC_FALSE, ardataset, &eresult);
5204135446Strhodes	if (result != ISC_R_SUCCESS)
5205135446Strhodes		goto unlock;
5206135446Strhodes
5207135446Strhodes	if (!HAVE_ANSWER(fctx)) {
5208135446Strhodes		fctx->attributes |= FCTX_ATTR_HAVEANSWER;
5209135446Strhodes		if (event != NULL) {
5210135446Strhodes			event->result = eresult;
5211308205Sdelphij			if (adbp != NULL && *adbp != NULL) {
5212308205Sdelphij				if (anodep != NULL && *anodep != NULL)
5213308205Sdelphij					dns_db_detachnode(*adbp, anodep);
5214308205Sdelphij				dns_db_detach(adbp);
5215308205Sdelphij			}
5216135446Strhodes			dns_db_attach(fctx->cache, adbp);
5217174187Sdougb			dns_db_transfernode(fctx->cache, &node, anodep);
5218135446Strhodes			clone_results(fctx);
5219135446Strhodes		}
5220135446Strhodes	}
5221135446Strhodes
5222135446Strhodes unlock:
5223135446Strhodes	UNLOCK(&res->buckets[fctx->bucketnum].lock);
5224135446Strhodes
5225135446Strhodes	if (node != NULL)
5226135446Strhodes		dns_db_detachnode(fctx->cache, &node);
5227135446Strhodes
5228135446Strhodes	return (result);
5229135446Strhodes}
5230135446Strhodes
5231135446Strhodesstatic inline void
5232135446Strhodesmark_related(dns_name_t *name, dns_rdataset_t *rdataset,
5233135446Strhodes	     isc_boolean_t external, isc_boolean_t gluing)
5234135446Strhodes{
5235135446Strhodes	name->attributes |= DNS_NAMEATTR_CACHE;
5236135446Strhodes	if (gluing) {
5237135446Strhodes		rdataset->trust = dns_trust_glue;
5238135446Strhodes		/*
5239135446Strhodes		 * Glue with 0 TTL causes problems.  We force the TTL to
5240135446Strhodes		 * 1 second to prevent this.
5241135446Strhodes		 */
5242135446Strhodes		if (rdataset->ttl == 0)
5243135446Strhodes			rdataset->ttl = 1;
5244135446Strhodes	} else
5245135446Strhodes		rdataset->trust = dns_trust_additional;
5246135446Strhodes	/*
5247135446Strhodes	 * Avoid infinite loops by only marking new rdatasets.
5248135446Strhodes	 */
5249135446Strhodes	if (!CACHE(rdataset)) {
5250135446Strhodes		name->attributes |= DNS_NAMEATTR_CHASE;
5251135446Strhodes		rdataset->attributes |= DNS_RDATASETATTR_CHASE;
5252135446Strhodes	}
5253135446Strhodes	rdataset->attributes |= DNS_RDATASETATTR_CACHE;
5254135446Strhodes	if (external)
5255135446Strhodes		rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL;
5256135446Strhodes}
5257135446Strhodes
5258135446Strhodesstatic isc_result_t
5259224092Sdougbcheck_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
5260224092Sdougb	      dns_section_t section)
5261224092Sdougb{
5262135446Strhodes	fetchctx_t *fctx = arg;
5263135446Strhodes	isc_result_t result;
5264135446Strhodes	dns_name_t *name;
5265135446Strhodes	dns_rdataset_t *rdataset;
5266135446Strhodes	isc_boolean_t external;
5267135446Strhodes	dns_rdatatype_t rtype;
5268135446Strhodes	isc_boolean_t gluing;
5269135446Strhodes
5270135446Strhodes	REQUIRE(VALID_FCTX(fctx));
5271135446Strhodes
5272224092Sdougb#if CHECK_FOR_GLUE_IN_ANSWER
5273224092Sdougb	if (section == DNS_SECTION_ANSWER && type != dns_rdatatype_a)
5274224092Sdougb		return (ISC_R_SUCCESS);
5275224092Sdougb#endif
5276224092Sdougb
5277135446Strhodes	if (GLUING(fctx))
5278135446Strhodes		gluing = ISC_TRUE;
5279135446Strhodes	else
5280135446Strhodes		gluing = ISC_FALSE;
5281135446Strhodes	name = NULL;
5282135446Strhodes	rdataset = NULL;
5283224092Sdougb	result = dns_message_findname(fctx->rmessage, section, addname,
5284224092Sdougb				      dns_rdatatype_any, 0, &name, NULL);
5285135446Strhodes	if (result == ISC_R_SUCCESS) {
5286135446Strhodes		external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
5287135446Strhodes		if (type == dns_rdatatype_a) {
5288135446Strhodes			for (rdataset = ISC_LIST_HEAD(name->list);
5289135446Strhodes			     rdataset != NULL;
5290135446Strhodes			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
5291135446Strhodes				if (rdataset->type == dns_rdatatype_rrsig)
5292135446Strhodes					rtype = rdataset->covers;
5293135446Strhodes				else
5294135446Strhodes					rtype = rdataset->type;
5295135446Strhodes				if (rtype == dns_rdatatype_a ||
5296135446Strhodes				    rtype == dns_rdatatype_aaaa)
5297135446Strhodes					mark_related(name, rdataset, external,
5298135446Strhodes						     gluing);
5299135446Strhodes			}
5300135446Strhodes		} else {
5301135446Strhodes			result = dns_message_findtype(name, type, 0,
5302135446Strhodes						      &rdataset);
5303135446Strhodes			if (result == ISC_R_SUCCESS) {
5304135446Strhodes				mark_related(name, rdataset, external, gluing);
5305135446Strhodes				/*
5306135446Strhodes				 * Do we have its SIG too?
5307135446Strhodes				 */
5308135446Strhodes				rdataset = NULL;
5309135446Strhodes				result = dns_message_findtype(name,
5310135446Strhodes						      dns_rdatatype_rrsig,
5311135446Strhodes						      type, &rdataset);
5312135446Strhodes				if (result == ISC_R_SUCCESS)
5313135446Strhodes					mark_related(name, rdataset, external,
5314135446Strhodes						     gluing);
5315135446Strhodes			}
5316135446Strhodes		}
5317135446Strhodes	}
5318135446Strhodes
5319135446Strhodes	return (ISC_R_SUCCESS);
5320135446Strhodes}
5321135446Strhodes
5322224092Sdougbstatic isc_result_t
5323224092Sdougbcheck_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) {
5324224092Sdougb	return (check_section(arg, addname, type, DNS_SECTION_ADDITIONAL));
5325224092Sdougb}
5326224092Sdougb
5327224092Sdougb#ifndef CHECK_FOR_GLUE_IN_ANSWER
5328224092Sdougb#define CHECK_FOR_GLUE_IN_ANSWER 0
5329224092Sdougb#endif
5330224092Sdougb#if CHECK_FOR_GLUE_IN_ANSWER
5331224092Sdougbstatic isc_result_t
5332224092Sdougbcheck_answer(void *arg, dns_name_t *addname, dns_rdatatype_t type) {
5333224092Sdougb	return (check_section(arg, addname, type, DNS_SECTION_ANSWER));
5334224092Sdougb}
5335224092Sdougb#endif
5336224092Sdougb
5337135446Strhodesstatic void
5338135446Strhodeschase_additional(fetchctx_t *fctx) {
5339135446Strhodes	isc_boolean_t rescan;
5340135446Strhodes	dns_section_t section = DNS_SECTION_ADDITIONAL;
5341135446Strhodes	isc_result_t result;
5342135446Strhodes
5343135446Strhodes again:
5344135446Strhodes	rescan = ISC_FALSE;
5345186462Sdougb
5346135446Strhodes	for (result = dns_message_firstname(fctx->rmessage, section);
5347135446Strhodes	     result == ISC_R_SUCCESS;
5348135446Strhodes	     result = dns_message_nextname(fctx->rmessage, section)) {
5349135446Strhodes		dns_name_t *name = NULL;
5350135446Strhodes		dns_rdataset_t *rdataset;
5351135446Strhodes		dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL,
5352135446Strhodes					&name);
5353135446Strhodes		if ((name->attributes & DNS_NAMEATTR_CHASE) == 0)
5354135446Strhodes			continue;
5355135446Strhodes		name->attributes &= ~DNS_NAMEATTR_CHASE;
5356135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
5357135446Strhodes		     rdataset != NULL;
5358135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
5359135446Strhodes			if (CHASE(rdataset)) {
5360135446Strhodes				rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
5361135446Strhodes				(void)dns_rdataset_additionaldata(rdataset,
5362135446Strhodes								  check_related,
5363135446Strhodes								  fctx);
5364135446Strhodes				rescan = ISC_TRUE;
5365135446Strhodes			}
5366135446Strhodes		}
5367135446Strhodes	}
5368135446Strhodes	if (rescan)
5369135446Strhodes		goto again;
5370135446Strhodes}
5371135446Strhodes
5372135446Strhodesstatic inline isc_result_t
5373135446Strhodescname_target(dns_rdataset_t *rdataset, dns_name_t *tname) {
5374135446Strhodes	isc_result_t result;
5375135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
5376135446Strhodes	dns_rdata_cname_t cname;
5377135446Strhodes
5378135446Strhodes	result = dns_rdataset_first(rdataset);
5379135446Strhodes	if (result != ISC_R_SUCCESS)
5380135446Strhodes		return (result);
5381135446Strhodes	dns_rdataset_current(rdataset, &rdata);
5382135446Strhodes	result = dns_rdata_tostruct(&rdata, &cname, NULL);
5383135446Strhodes	if (result != ISC_R_SUCCESS)
5384135446Strhodes		return (result);
5385135446Strhodes	dns_name_init(tname, NULL);
5386135446Strhodes	dns_name_clone(&cname.cname, tname);
5387135446Strhodes	dns_rdata_freestruct(&cname);
5388135446Strhodes
5389135446Strhodes	return (ISC_R_SUCCESS);
5390135446Strhodes}
5391135446Strhodes
5392135446Strhodesstatic inline isc_result_t
5393296611Sdelphijdname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
5394296611Sdelphij	     unsigned int nlabels, dns_fixedname_t *fixeddname)
5395135446Strhodes{
5396135446Strhodes	isc_result_t result;
5397135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
5398135446Strhodes	dns_rdata_dname_t dname;
5399135446Strhodes	dns_fixedname_t prefix;
5400135446Strhodes
5401135446Strhodes	/*
5402135446Strhodes	 * Get the target name of the DNAME.
5403135446Strhodes	 */
5404135446Strhodes	result = dns_rdataset_first(rdataset);
5405135446Strhodes	if (result != ISC_R_SUCCESS)
5406135446Strhodes		return (result);
5407135446Strhodes	dns_rdataset_current(rdataset, &rdata);
5408135446Strhodes	result = dns_rdata_tostruct(&rdata, &dname, NULL);
5409135446Strhodes	if (result != ISC_R_SUCCESS)
5410135446Strhodes		return (result);
5411135446Strhodes
5412135446Strhodes	dns_fixedname_init(&prefix);
5413186462Sdougb	dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
5414135446Strhodes	dns_fixedname_init(fixeddname);
5415135446Strhodes	result = dns_name_concatenate(dns_fixedname_name(&prefix),
5416135446Strhodes				      &dname.dname,
5417135446Strhodes				      dns_fixedname_name(fixeddname), NULL);
5418135446Strhodes	dns_rdata_freestruct(&dname);
5419135446Strhodes	return (result);
5420135446Strhodes}
5421135446Strhodes
5422224092Sdougbstatic isc_boolean_t
5423224092Sdougbis_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
5424224092Sdougb			 dns_rdataset_t *rdataset)
5425224092Sdougb{
5426224092Sdougb	isc_result_t result;
5427224092Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
5428224092Sdougb	struct in_addr ina;
5429224092Sdougb	struct in6_addr in6a;
5430224092Sdougb	isc_netaddr_t netaddr;
5431224092Sdougb	char addrbuf[ISC_NETADDR_FORMATSIZE];
5432224092Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
5433224092Sdougb	char classbuf[64];
5434224092Sdougb	char typebuf[64];
5435224092Sdougb	int match;
5436224092Sdougb
5437224092Sdougb	/* By default, we allow any addresses. */
5438224092Sdougb	if (view->denyansweracl == NULL)
5439224092Sdougb		return (ISC_TRUE);
5440224092Sdougb
5441224092Sdougb	/*
5442224092Sdougb	 * If the owner name matches one in the exclusion list, either exactly
5443224092Sdougb	 * or partially, allow it.
5444224092Sdougb	 */
5445224092Sdougb	if (view->answeracl_exclude != NULL) {
5446224092Sdougb		dns_rbtnode_t *node = NULL;
5447224092Sdougb
5448224092Sdougb		result = dns_rbt_findnode(view->answeracl_exclude, name, NULL,
5449224092Sdougb					  &node, NULL, 0, NULL, NULL);
5450224092Sdougb
5451224092Sdougb		if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
5452224092Sdougb			return (ISC_TRUE);
5453224092Sdougb	}
5454224092Sdougb
5455224092Sdougb	/*
5456224092Sdougb	 * Otherwise, search the filter list for a match for each address
5457224092Sdougb	 * record.  If a match is found, the address should be filtered,
5458224092Sdougb	 * so should the entire answer.
5459224092Sdougb	 */
5460224092Sdougb	for (result = dns_rdataset_first(rdataset);
5461224092Sdougb	     result == ISC_R_SUCCESS;
5462224092Sdougb	     result = dns_rdataset_next(rdataset)) {
5463224092Sdougb		dns_rdata_reset(&rdata);
5464224092Sdougb		dns_rdataset_current(rdataset, &rdata);
5465224092Sdougb		if (rdataset->type == dns_rdatatype_a) {
5466224092Sdougb			INSIST(rdata.length == sizeof(ina.s_addr));
5467262706Serwin			memmove(&ina.s_addr, rdata.data, sizeof(ina.s_addr));
5468224092Sdougb			isc_netaddr_fromin(&netaddr, &ina);
5469224092Sdougb		} else {
5470224092Sdougb			INSIST(rdata.length == sizeof(in6a.s6_addr));
5471262706Serwin			memmove(in6a.s6_addr, rdata.data, sizeof(in6a.s6_addr));
5472224092Sdougb			isc_netaddr_fromin6(&netaddr, &in6a);
5473224092Sdougb		}
5474224092Sdougb
5475224092Sdougb		result = dns_acl_match(&netaddr, NULL, view->denyansweracl,
5476224092Sdougb				       &view->aclenv, &match, NULL);
5477224092Sdougb
5478224092Sdougb		if (result == ISC_R_SUCCESS && match > 0) {
5479224092Sdougb			isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
5480224092Sdougb			dns_name_format(name, namebuf, sizeof(namebuf));
5481224092Sdougb			dns_rdatatype_format(rdataset->type, typebuf,
5482224092Sdougb					     sizeof(typebuf));
5483224092Sdougb			dns_rdataclass_format(rdataset->rdclass, classbuf,
5484224092Sdougb					      sizeof(classbuf));
5485224092Sdougb			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
5486224092Sdougb				      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
5487224092Sdougb				      "answer address %s denied for %s/%s/%s",
5488224092Sdougb				      addrbuf, namebuf, typebuf, classbuf);
5489224092Sdougb			return (ISC_FALSE);
5490224092Sdougb		}
5491224092Sdougb	}
5492224092Sdougb
5493224092Sdougb	return (ISC_TRUE);
5494224092Sdougb}
5495224092Sdougb
5496224092Sdougbstatic isc_boolean_t
5497224092Sdougbis_answertarget_allowed(dns_view_t *view, dns_name_t *name,
5498224092Sdougb			dns_rdatatype_t type, dns_name_t *tname,
5499224092Sdougb			dns_name_t *domain)
5500224092Sdougb{
5501224092Sdougb	isc_result_t result;
5502224092Sdougb	dns_rbtnode_t *node = NULL;
5503224092Sdougb	char qnamebuf[DNS_NAME_FORMATSIZE];
5504224092Sdougb	char tnamebuf[DNS_NAME_FORMATSIZE];
5505224092Sdougb	char classbuf[64];
5506224092Sdougb	char typebuf[64];
5507224092Sdougb
5508224092Sdougb	/* By default, we allow any target name. */
5509224092Sdougb	if (view->denyanswernames == NULL)
5510224092Sdougb		return (ISC_TRUE);
5511224092Sdougb
5512224092Sdougb	/*
5513224092Sdougb	 * If the owner name matches one in the exclusion list, either exactly
5514224092Sdougb	 * or partially, allow it.
5515224092Sdougb	 */
5516224092Sdougb	if (view->answernames_exclude != NULL) {
5517224092Sdougb		result = dns_rbt_findnode(view->answernames_exclude, name, NULL,
5518224092Sdougb					  &node, NULL, 0, NULL, NULL);
5519224092Sdougb		if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
5520224092Sdougb			return (ISC_TRUE);
5521224092Sdougb	}
5522224092Sdougb
5523224092Sdougb	/*
5524224092Sdougb	 * If the target name is a subdomain of the search domain, allow it.
5525224092Sdougb	 */
5526224092Sdougb	if (dns_name_issubdomain(tname, domain))
5527224092Sdougb		return (ISC_TRUE);
5528224092Sdougb
5529224092Sdougb	/*
5530224092Sdougb	 * Otherwise, apply filters.
5531224092Sdougb	 */
5532224092Sdougb	result = dns_rbt_findnode(view->denyanswernames, tname, NULL, &node,
5533224092Sdougb				  NULL, 0, NULL, NULL);
5534224092Sdougb	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
5535224092Sdougb		dns_name_format(name, qnamebuf, sizeof(qnamebuf));
5536224092Sdougb		dns_name_format(tname, tnamebuf, sizeof(tnamebuf));
5537224092Sdougb		dns_rdatatype_format(type, typebuf, sizeof(typebuf));
5538224092Sdougb		dns_rdataclass_format(view->rdclass, classbuf,
5539224092Sdougb				      sizeof(classbuf));
5540224092Sdougb		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
5541224092Sdougb			      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
5542224092Sdougb			      "%s target %s denied for %s/%s",
5543224092Sdougb			      typebuf, tnamebuf, qnamebuf, classbuf);
5544224092Sdougb		return (ISC_FALSE);
5545224092Sdougb	}
5546224092Sdougb
5547224092Sdougb	return (ISC_TRUE);
5548224092Sdougb}
5549224092Sdougb
5550234010Sdougbstatic void
5551234010Sdougbtrim_ns_ttl(fetchctx_t *fctx, dns_name_t *name, dns_rdataset_t *rdataset) {
5552234010Sdougb	char ns_namebuf[DNS_NAME_FORMATSIZE];
5553234010Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
5554234010Sdougb	char tbuf[DNS_RDATATYPE_FORMATSIZE];
5555234010Sdougb
5556234010Sdougb	if (fctx->ns_ttl_ok && rdataset->ttl > fctx->ns_ttl) {
5557234010Sdougb		dns_name_format(name, ns_namebuf, sizeof(ns_namebuf));
5558234010Sdougb		dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
5559234010Sdougb		dns_rdatatype_format(fctx->type, tbuf, sizeof(tbuf));
5560234010Sdougb
5561234010Sdougb		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
5562234010Sdougb			      DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10),
5563234010Sdougb			      "fctx %p: trimming ttl of %s/NS for %s/%s: "
5564234010Sdougb			      "%u -> %u", fctx, ns_namebuf, namebuf, tbuf,
5565234010Sdougb			      rdataset->ttl, fctx->ns_ttl);
5566234010Sdougb		rdataset->ttl = fctx->ns_ttl;
5567234010Sdougb	}
5568234010Sdougb}
5569234010Sdougb
5570135446Strhodes/*
5571135446Strhodes * Handle a no-answer response (NXDOMAIN, NXRRSET, or referral).
5572224092Sdougb * If look_in_options has LOOK_FOR_NS_IN_ANSWER then we look in the answer
5573224092Sdougb * section for the NS RRset if the query type is NS; if it has
5574224092Sdougb * LOOK_FOR_GLUE_IN_ANSWER we look for glue incorrectly returned in the answer
5575224092Sdougb * section for A and AAAA queries.
5576135446Strhodes */
5577224092Sdougb#define LOOK_FOR_NS_IN_ANSWER 0x1
5578224092Sdougb#define LOOK_FOR_GLUE_IN_ANSWER 0x2
5579224092Sdougb
5580135446Strhodesstatic isc_result_t
5581135446Strhodesnoanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
5582224092Sdougb		  unsigned int look_in_options)
5583135446Strhodes{
5584135446Strhodes	isc_result_t result;
5585135446Strhodes	dns_message_t *message;
5586254402Serwin	dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name, *save_name;
5587135446Strhodes	dns_rdataset_t *rdataset, *ns_rdataset;
5588170222Sdougb	isc_boolean_t aa, negative_response;
5589254402Serwin	dns_rdatatype_t type, save_type;
5590224092Sdougb	dns_section_t section;
5591135446Strhodes
5592135446Strhodes	FCTXTRACE("noanswer_response");
5593135446Strhodes
5594224092Sdougb	if ((look_in_options & LOOK_FOR_NS_IN_ANSWER) != 0) {
5595224092Sdougb		INSIST(fctx->type == dns_rdatatype_ns);
5596224092Sdougb		section = DNS_SECTION_ANSWER;
5597224092Sdougb	} else
5598224092Sdougb		section = DNS_SECTION_AUTHORITY;
5599224092Sdougb
5600135446Strhodes	message = fctx->rmessage;
5601135446Strhodes
5602135446Strhodes	/*
5603135446Strhodes	 * Setup qname.
5604135446Strhodes	 */
5605135446Strhodes	if (oqname == NULL) {
5606135446Strhodes		/*
5607135446Strhodes		 * We have a normal, non-chained negative response or
5608135446Strhodes		 * referral.
5609135446Strhodes		 */
5610135446Strhodes		if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
5611135446Strhodes			aa = ISC_TRUE;
5612135446Strhodes		else
5613135446Strhodes			aa = ISC_FALSE;
5614135446Strhodes		qname = &fctx->name;
5615135446Strhodes	} else {
5616135446Strhodes		/*
5617135446Strhodes		 * We're being invoked by answer_response() after it has
5618135446Strhodes		 * followed a CNAME/DNAME chain.
5619135446Strhodes		 */
5620135446Strhodes		qname = oqname;
5621135446Strhodes		aa = ISC_FALSE;
5622135446Strhodes		/*
5623135446Strhodes		 * If the current qname is not a subdomain of the query
5624135446Strhodes		 * domain, there's no point in looking at the authority
5625135446Strhodes		 * section without doing DNSSEC validation.
5626135446Strhodes		 *
5627135446Strhodes		 * Until we do that validation, we'll just return success
5628135446Strhodes		 * in this case.
5629135446Strhodes		 */
5630135446Strhodes		if (!dns_name_issubdomain(qname, &fctx->domain))
5631135446Strhodes			return (ISC_R_SUCCESS);
5632135446Strhodes	}
5633135446Strhodes
5634135446Strhodes	/*
5635135446Strhodes	 * We have to figure out if this is a negative response, or a
5636135446Strhodes	 * referral.
5637135446Strhodes	 */
5638135446Strhodes
5639135446Strhodes	/*
5640135446Strhodes	 * Sometimes we can tell if its a negative response by looking at
5641135446Strhodes	 * the message header.
5642135446Strhodes	 */
5643135446Strhodes	negative_response = ISC_FALSE;
5644135446Strhodes	if (message->rcode == dns_rcode_nxdomain ||
5645135446Strhodes	    (message->counts[DNS_SECTION_ANSWER] == 0 &&
5646135446Strhodes	     message->counts[DNS_SECTION_AUTHORITY] == 0))
5647135446Strhodes		negative_response = ISC_TRUE;
5648135446Strhodes
5649135446Strhodes	/*
5650135446Strhodes	 * Process the authority section.
5651135446Strhodes	 */
5652135446Strhodes	ns_name = NULL;
5653135446Strhodes	ns_rdataset = NULL;
5654135446Strhodes	soa_name = NULL;
5655135446Strhodes	ds_name = NULL;
5656254402Serwin	save_name = NULL;
5657254402Serwin	save_type = dns_rdatatype_none;
5658135446Strhodes	result = dns_message_firstname(message, section);
5659170222Sdougb	while (result == ISC_R_SUCCESS) {
5660135446Strhodes		name = NULL;
5661135446Strhodes		dns_message_currentname(message, section, &name);
5662135446Strhodes		if (dns_name_issubdomain(name, &fctx->domain)) {
5663135446Strhodes			/*
5664143731Sdougb			 * Look for NS/SOA RRsets first.
5665135446Strhodes			 */
5666135446Strhodes			for (rdataset = ISC_LIST_HEAD(name->list);
5667135446Strhodes			     rdataset != NULL;
5668135446Strhodes			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
5669135446Strhodes				type = rdataset->type;
5670135446Strhodes				if (type == dns_rdatatype_rrsig)
5671135446Strhodes					type = rdataset->covers;
5672135446Strhodes				if (((type == dns_rdatatype_ns ||
5673135446Strhodes				      type == dns_rdatatype_soa) &&
5674224092Sdougb				     !dns_name_issubdomain(qname, name))) {
5675224092Sdougb					char qbuf[DNS_NAME_FORMATSIZE];
5676224092Sdougb					char nbuf[DNS_NAME_FORMATSIZE];
5677224092Sdougb					char tbuf[DNS_RDATATYPE_FORMATSIZE];
5678224092Sdougb					dns_rdatatype_format(fctx->type, tbuf,
5679224092Sdougb							     sizeof(tbuf));
5680224092Sdougb					dns_name_format(name, nbuf,
5681224092Sdougb							     sizeof(nbuf));
5682224092Sdougb					dns_name_format(qname, qbuf,
5683224092Sdougb							     sizeof(qbuf));
5684224092Sdougb					log_formerr(fctx,
5685224092Sdougb						    "unrelated %s %s in "
5686224092Sdougb						    "%s authority section",
5687224092Sdougb						    tbuf, qbuf, nbuf);
5688135446Strhodes					return (DNS_R_FORMERR);
5689224092Sdougb				}
5690135446Strhodes				if (type == dns_rdatatype_ns) {
5691135446Strhodes					/*
5692143731Sdougb					 * NS or RRSIG NS.
5693135446Strhodes					 *
5694135446Strhodes					 * Only one set of NS RRs is allowed.
5695135446Strhodes					 */
5696135446Strhodes					if (rdataset->type ==
5697135446Strhodes					    dns_rdatatype_ns) {
5698135446Strhodes						if (ns_name != NULL &&
5699224092Sdougb						    name != ns_name) {
5700224092Sdougb							log_formerr(fctx,
5701224092Sdougb								"multiple NS "
5702224092Sdougb								"RRsets in "
5703224092Sdougb								"authority "
5704224092Sdougb								"section");
5705135446Strhodes							return (DNS_R_FORMERR);
5706224092Sdougb						}
5707135446Strhodes						ns_name = name;
5708135446Strhodes						ns_rdataset = rdataset;
5709135446Strhodes					}
5710135446Strhodes					name->attributes |=
5711135446Strhodes						DNS_NAMEATTR_CACHE;
5712135446Strhodes					rdataset->attributes |=
5713135446Strhodes						DNS_RDATASETATTR_CACHE;
5714135446Strhodes					rdataset->trust = dns_trust_glue;
5715135446Strhodes				}
5716143731Sdougb				if (type == dns_rdatatype_soa) {
5717135446Strhodes					/*
5718143731Sdougb					 * SOA, or RRSIG SOA.
5719135446Strhodes					 *
5720135446Strhodes					 * Only one SOA is allowed.
5721135446Strhodes					 */
5722135446Strhodes					if (rdataset->type ==
5723135446Strhodes					    dns_rdatatype_soa) {
5724135446Strhodes						if (soa_name != NULL &&
5725224092Sdougb						    name != soa_name) {
5726224092Sdougb							log_formerr(fctx,
5727224092Sdougb								"multiple SOA "
5728224092Sdougb								"RRs in "
5729224092Sdougb								"authority "
5730224092Sdougb								"section");
5731135446Strhodes							return (DNS_R_FORMERR);
5732224092Sdougb						}
5733135446Strhodes						soa_name = name;
5734135446Strhodes					}
5735143731Sdougb					name->attributes |=
5736143731Sdougb						DNS_NAMEATTR_NCACHE;
5737143731Sdougb					rdataset->attributes |=
5738143731Sdougb						DNS_RDATASETATTR_NCACHE;
5739143731Sdougb					if (aa)
5740143731Sdougb						rdataset->trust =
5741143731Sdougb						    dns_trust_authauthority;
5742234010Sdougb					else if (ISFORWARDER(fctx->addrinfo))
5743234010Sdougb						rdataset->trust =
5744234010Sdougb							dns_trust_answer;
5745143731Sdougb					else
5746143731Sdougb						rdataset->trust =
5747143731Sdougb							dns_trust_additional;
5748143731Sdougb				}
5749143731Sdougb			}
5750170222Sdougb		}
5751170222Sdougb		result = dns_message_nextname(message, section);
5752170222Sdougb		if (result == ISC_R_NOMORE)
5753170222Sdougb			break;
5754170222Sdougb		else if (result != ISC_R_SUCCESS)
5755170222Sdougb			return (result);
5756170222Sdougb	}
5757170222Sdougb
5758234010Sdougb	log_ns_ttl(fctx, "noanswer_response");
5759234010Sdougb
5760234010Sdougb	if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) &&
5761234010Sdougb	    !dns_name_equal(ns_name, dns_rootname))
5762234010Sdougb		trim_ns_ttl(fctx, ns_name, ns_rdataset);
5763234010Sdougb
5764170222Sdougb	/*
5765186462Sdougb	 * A negative response has a SOA record (Type 2)
5766170222Sdougb	 * and a optional NS RRset (Type 1) or it has neither
5767170222Sdougb	 * a SOA or a NS RRset (Type 3, handled above) or
5768170222Sdougb	 * rcode is NXDOMAIN (handled above) in which case
5769170222Sdougb	 * the NS RRset is allowed (Type 4).
5770170222Sdougb	 */
5771170222Sdougb	if (soa_name != NULL)
5772170222Sdougb		negative_response = ISC_TRUE;
5773170222Sdougb
5774170222Sdougb	result = dns_message_firstname(message, section);
5775170222Sdougb	while (result == ISC_R_SUCCESS) {
5776170222Sdougb		name = NULL;
5777170222Sdougb		dns_message_currentname(message, section, &name);
5778170222Sdougb		if (dns_name_issubdomain(name, &fctx->domain)) {
5779143731Sdougb			for (rdataset = ISC_LIST_HEAD(name->list);
5780143731Sdougb			     rdataset != NULL;
5781143731Sdougb			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
5782143731Sdougb				type = rdataset->type;
5783143731Sdougb				if (type == dns_rdatatype_rrsig)
5784143731Sdougb					type = rdataset->covers;
5785193149Sdougb				if (type == dns_rdatatype_nsec ||
5786193149Sdougb				    type == dns_rdatatype_nsec3) {
5787143731Sdougb					/*
5788143731Sdougb					 * NSEC or RRSIG NSEC.
5789143731Sdougb					 */
5790143731Sdougb					if (negative_response) {
5791135446Strhodes						name->attributes |=
5792135446Strhodes							DNS_NAMEATTR_NCACHE;
5793135446Strhodes						rdataset->attributes |=
5794135446Strhodes							DNS_RDATASETATTR_NCACHE;
5795193149Sdougb					} else if (type == dns_rdatatype_nsec) {
5796135446Strhodes						name->attributes |=
5797135446Strhodes							DNS_NAMEATTR_CACHE;
5798135446Strhodes						rdataset->attributes |=
5799135446Strhodes							DNS_RDATASETATTR_CACHE;
5800135446Strhodes					}
5801135446Strhodes					if (aa)
5802135446Strhodes						rdataset->trust =
5803135446Strhodes						    dns_trust_authauthority;
5804234010Sdougb					else if (ISFORWARDER(fctx->addrinfo))
5805234010Sdougb						rdataset->trust =
5806234010Sdougb							dns_trust_answer;
5807135446Strhodes					else
5808135446Strhodes						rdataset->trust =
5809135446Strhodes							dns_trust_additional;
5810135446Strhodes					/*
5811135446Strhodes					 * No additional data needs to be
5812135446Strhodes					 * marked.
5813135446Strhodes					 */
5814135446Strhodes				} else if (type == dns_rdatatype_ds) {
5815135446Strhodes					/*
5816135446Strhodes					 * DS or SIG DS.
5817135446Strhodes					 *
5818135446Strhodes					 * These should only be here if
5819135446Strhodes					 * this is a referral, and there
5820224092Sdougb					 * should only be one DS RRset.
5821135446Strhodes					 */
5822224092Sdougb					if (ns_name == NULL) {
5823224092Sdougb						log_formerr(fctx,
5824224092Sdougb							    "DS with no "
5825224092Sdougb							    "referral");
5826135446Strhodes						return (DNS_R_FORMERR);
5827224092Sdougb					}
5828135446Strhodes					if (rdataset->type ==
5829135446Strhodes					    dns_rdatatype_ds) {
5830135446Strhodes						if (ds_name != NULL &&
5831224092Sdougb						    name != ds_name) {
5832224092Sdougb							log_formerr(fctx,
5833224092Sdougb								"DS doesn't "
5834224092Sdougb								"match "
5835224092Sdougb								"referral "
5836224092Sdougb								"(NS)");
5837135446Strhodes							return (DNS_R_FORMERR);
5838224092Sdougb						}
5839135446Strhodes						ds_name = name;
5840135446Strhodes					}
5841135446Strhodes					name->attributes |=
5842135446Strhodes						DNS_NAMEATTR_CACHE;
5843135446Strhodes					rdataset->attributes |=
5844135446Strhodes						DNS_RDATASETATTR_CACHE;
5845135446Strhodes					if (aa)
5846135446Strhodes						rdataset->trust =
5847135446Strhodes						    dns_trust_authauthority;
5848234010Sdougb					else if (ISFORWARDER(fctx->addrinfo))
5849234010Sdougb						rdataset->trust =
5850234010Sdougb							dns_trust_answer;
5851135446Strhodes					else
5852135446Strhodes						rdataset->trust =
5853135446Strhodes							dns_trust_additional;
5854135446Strhodes				}
5855135446Strhodes			}
5856254402Serwin		} else {
5857254402Serwin			save_name = name;
5858254402Serwin			save_type = ISC_LIST_HEAD(name->list)->type;
5859135446Strhodes		}
5860135446Strhodes		result = dns_message_nextname(message, section);
5861135446Strhodes		if (result == ISC_R_NOMORE)
5862135446Strhodes			break;
5863135446Strhodes		else if (result != ISC_R_SUCCESS)
5864135446Strhodes			return (result);
5865135446Strhodes	}
5866135446Strhodes
5867135446Strhodes	/*
5868135446Strhodes	 * Trigger lookups for DNS nameservers.
5869135446Strhodes	 */
5870135446Strhodes	if (negative_response && message->rcode == dns_rcode_noerror &&
5871135446Strhodes	    fctx->type == dns_rdatatype_ds && soa_name != NULL &&
5872135446Strhodes	    dns_name_equal(soa_name, qname) &&
5873135446Strhodes	    !dns_name_equal(qname, dns_rootname))
5874135446Strhodes		return (DNS_R_CHASEDSSERVERS);
5875135446Strhodes
5876135446Strhodes	/*
5877135446Strhodes	 * Did we find anything?
5878135446Strhodes	 */
5879135446Strhodes	if (!negative_response && ns_name == NULL) {
5880135446Strhodes		/*
5881135446Strhodes		 * Nope.
5882135446Strhodes		 */
5883135446Strhodes		if (oqname != NULL) {
5884135446Strhodes			/*
5885135446Strhodes			 * We've already got a partial CNAME/DNAME chain,
5886135446Strhodes			 * and haven't found else anything useful here, but
5887135446Strhodes			 * no error has occurred since we have an answer.
5888135446Strhodes			 */
5889135446Strhodes			return (ISC_R_SUCCESS);
5890135446Strhodes		} else {
5891135446Strhodes			/*
5892135446Strhodes			 * The responder is insane.
5893135446Strhodes			 */
5894254402Serwin			if (save_name == NULL) {
5895254402Serwin				log_formerr(fctx, "invalid response");
5896254402Serwin				return (DNS_R_FORMERR);
5897254402Serwin			}
5898254402Serwin			if (!dns_name_issubdomain(save_name, &fctx->domain)) {
5899254402Serwin				char nbuf[DNS_NAME_FORMATSIZE];
5900254402Serwin				char dbuf[DNS_NAME_FORMATSIZE];
5901254402Serwin				char tbuf[DNS_RDATATYPE_FORMATSIZE];
5902254402Serwin
5903254402Serwin				dns_rdatatype_format(save_type, tbuf,
5904254402Serwin					sizeof(tbuf));
5905254402Serwin				dns_name_format(save_name, nbuf, sizeof(nbuf));
5906254402Serwin				dns_name_format(&fctx->domain, dbuf,
5907254402Serwin					sizeof(dbuf));
5908254402Serwin
5909254402Serwin				log_formerr(fctx, "Name %s (%s) not subdomain"
5910254402Serwin					" of zone %s -- invalid response",
5911254402Serwin					nbuf, tbuf, dbuf);
5912254402Serwin			} else {
5913254402Serwin				log_formerr(fctx, "invalid response");
5914254402Serwin			}
5915135446Strhodes			return (DNS_R_FORMERR);
5916135446Strhodes		}
5917135446Strhodes	}
5918135446Strhodes
5919135446Strhodes	/*
5920135446Strhodes	 * If we found both NS and SOA, they should be the same name.
5921135446Strhodes	 */
5922224092Sdougb	if (ns_name != NULL && soa_name != NULL && ns_name != soa_name) {
5923224092Sdougb		log_formerr(fctx, "NS/SOA mismatch");
5924135446Strhodes		return (DNS_R_FORMERR);
5925224092Sdougb	}
5926135446Strhodes
5927135446Strhodes	/*
5928135446Strhodes	 * Do we have a referral?  (We only want to follow a referral if
5929135446Strhodes	 * we're not following a chain.)
5930135446Strhodes	 */
5931135446Strhodes	if (!negative_response && ns_name != NULL && oqname == NULL) {
5932135446Strhodes		/*
5933135446Strhodes		 * We already know ns_name is a subdomain of fctx->domain.
5934135446Strhodes		 * If ns_name is equal to fctx->domain, we're not making
5935135446Strhodes		 * progress.  We return DNS_R_FORMERR so that we'll keep
5936135446Strhodes		 * trying other servers.
5937135446Strhodes		 */
5938224092Sdougb		if (dns_name_equal(ns_name, &fctx->domain)) {
5939224092Sdougb			log_formerr(fctx, "non-improving referral");
5940135446Strhodes			return (DNS_R_FORMERR);
5941224092Sdougb		}
5942135446Strhodes
5943135446Strhodes		/*
5944135446Strhodes		 * If the referral name is not a parent of the query
5945135446Strhodes		 * name, consider the responder insane.
5946135446Strhodes		 */
5947135446Strhodes		if (! dns_name_issubdomain(&fctx->name, ns_name)) {
5948224092Sdougb			/* Logged twice */
5949224092Sdougb			log_formerr(fctx, "referral to non-parent");
5950135446Strhodes			FCTXTRACE("referral to non-parent");
5951135446Strhodes			return (DNS_R_FORMERR);
5952135446Strhodes		}
5953135446Strhodes
5954135446Strhodes		/*
5955135446Strhodes		 * Mark any additional data related to this rdataset.
5956135446Strhodes		 * It's important that we do this before we change the
5957135446Strhodes		 * query domain.
5958135446Strhodes		 */
5959135446Strhodes		INSIST(ns_rdataset != NULL);
5960135446Strhodes		fctx->attributes |= FCTX_ATTR_GLUING;
5961135446Strhodes		(void)dns_rdataset_additionaldata(ns_rdataset, check_related,
5962135446Strhodes						  fctx);
5963224092Sdougb#if CHECK_FOR_GLUE_IN_ANSWER
5964224092Sdougb		/*
5965224092Sdougb		 * Look in the answer section for "glue" that is incorrectly
5966224092Sdougb		 * returned as a answer.  This is needed if the server also
5967224092Sdougb		 * minimizes the response size by not adding records to the
5968224092Sdougb		 * additional section that are in the answer section or if
5969224092Sdougb		 * the record gets dropped due to message size constraints.
5970224092Sdougb		 */
5971224092Sdougb		if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 &&
5972224092Sdougb		    (fctx->type == dns_rdatatype_aaaa ||
5973224092Sdougb		     fctx->type == dns_rdatatype_a))
5974224092Sdougb			(void)dns_rdataset_additionaldata(ns_rdataset,
5975224092Sdougb							  check_answer, fctx);
5976224092Sdougb#endif
5977135446Strhodes		fctx->attributes &= ~FCTX_ATTR_GLUING;
5978135446Strhodes		/*
5979135446Strhodes		 * NS rdatasets with 0 TTL cause problems.
5980135446Strhodes		 * dns_view_findzonecut() will not find them when we
5981135446Strhodes		 * try to follow the referral, and we'll SERVFAIL
5982135446Strhodes		 * because the best nameservers are now above QDOMAIN.
5983135446Strhodes		 * We force the TTL to 1 second to prevent this.
5984135446Strhodes		 */
5985135446Strhodes		if (ns_rdataset->ttl == 0)
5986135446Strhodes			ns_rdataset->ttl = 1;
5987135446Strhodes		/*
5988135446Strhodes		 * Set the current query domain to the referral name.
5989135446Strhodes		 *
5990135446Strhodes		 * XXXRTH  We should check if we're in forward-only mode, and
5991193149Sdougb		 *		if so we should bail out.
5992135446Strhodes		 */
5993135446Strhodes		INSIST(dns_name_countlabels(&fctx->domain) > 0);
5994236374Sdougb		dns_name_free(&fctx->domain, fctx->mctx);
5995135446Strhodes		if (dns_rdataset_isassociated(&fctx->nameservers))
5996135446Strhodes			dns_rdataset_disassociate(&fctx->nameservers);
5997135446Strhodes		dns_name_init(&fctx->domain, NULL);
5998236374Sdougb		result = dns_name_dup(ns_name, fctx->mctx, &fctx->domain);
5999135446Strhodes		if (result != ISC_R_SUCCESS)
6000135446Strhodes			return (result);
6001135446Strhodes		fctx->attributes |= FCTX_ATTR_WANTCACHE;
6002234010Sdougb		fctx->ns_ttl_ok = ISC_FALSE;
6003234010Sdougb		log_ns_ttl(fctx, "DELEGATION");
6004135446Strhodes		return (DNS_R_DELEGATION);
6005135446Strhodes	}
6006135446Strhodes
6007135446Strhodes	/*
6008135446Strhodes	 * Since we're not doing a referral, we don't want to cache any
6009135446Strhodes	 * NS RRs we may have found.
6010135446Strhodes	 */
6011135446Strhodes	if (ns_name != NULL)
6012135446Strhodes		ns_name->attributes &= ~DNS_NAMEATTR_CACHE;
6013135446Strhodes
6014135446Strhodes	if (negative_response && oqname == NULL)
6015135446Strhodes		fctx->attributes |= FCTX_ATTR_WANTNCACHE;
6016135446Strhodes
6017135446Strhodes	return (ISC_R_SUCCESS);
6018135446Strhodes}
6019135446Strhodes
6020135446Strhodesstatic isc_result_t
6021135446Strhodesanswer_response(fetchctx_t *fctx) {
6022135446Strhodes	isc_result_t result;
6023135446Strhodes	dns_message_t *message;
6024308205Sdelphij	dns_name_t *name, *dname = NULL, *qname, *dqname, tname, *ns_name;
6025308205Sdelphij	dns_name_t *cname = NULL;
6026234010Sdougb	dns_rdataset_t *rdataset, *ns_rdataset;
6027135446Strhodes	isc_boolean_t done, external, chaining, aa, found, want_chaining;
6028308205Sdelphij	isc_boolean_t have_answer, found_cname, found_dname, found_type;
6029308205Sdelphij	isc_boolean_t wanted_chaining;
6030135446Strhodes	unsigned int aflag;
6031135446Strhodes	dns_rdatatype_t type;
6032308205Sdelphij	dns_fixedname_t fdname, fqname, fqdname;
6033224092Sdougb	dns_view_t *view;
6034135446Strhodes
6035135446Strhodes	FCTXTRACE("answer_response");
6036135446Strhodes
6037135446Strhodes	message = fctx->rmessage;
6038135446Strhodes
6039135446Strhodes	/*
6040135446Strhodes	 * Examine the answer section, marking those rdatasets which are
6041135446Strhodes	 * part of the answer and should be cached.
6042135446Strhodes	 */
6043135446Strhodes
6044135446Strhodes	done = ISC_FALSE;
6045135446Strhodes	found_cname = ISC_FALSE;
6046308205Sdelphij	found_dname = ISC_FALSE;
6047135446Strhodes	found_type = ISC_FALSE;
6048135446Strhodes	chaining = ISC_FALSE;
6049135446Strhodes	have_answer = ISC_FALSE;
6050135446Strhodes	want_chaining = ISC_FALSE;
6051225361Sdougb	POST(want_chaining);
6052135446Strhodes	if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
6053135446Strhodes		aa = ISC_TRUE;
6054135446Strhodes	else
6055135446Strhodes		aa = ISC_FALSE;
6056308205Sdelphij	dqname = qname = &fctx->name;
6057135446Strhodes	type = fctx->type;
6058224092Sdougb	view = fctx->res->view;
6059308205Sdelphij	dns_fixedname_init(&fqdname);
6060135446Strhodes	result = dns_message_firstname(message, DNS_SECTION_ANSWER);
6061135446Strhodes	while (!done && result == ISC_R_SUCCESS) {
6062308205Sdelphij		dns_namereln_t namereln, dnamereln;
6063296611Sdelphij		int order;
6064296611Sdelphij		unsigned int nlabels;
6065296611Sdelphij
6066135446Strhodes		name = NULL;
6067135446Strhodes		dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
6068135446Strhodes		external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
6069296611Sdelphij		namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
6070308205Sdelphij		dnamereln = dns_name_fullcompare(dqname, name, &order,
6071308205Sdelphij						 &nlabels);
6072296611Sdelphij		if (namereln == dns_namereln_equal) {
6073135446Strhodes			wanted_chaining = ISC_FALSE;
6074135446Strhodes			for (rdataset = ISC_LIST_HEAD(name->list);
6075135446Strhodes			     rdataset != NULL;
6076135446Strhodes			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
6077135446Strhodes				found = ISC_FALSE;
6078135446Strhodes				want_chaining = ISC_FALSE;
6079135446Strhodes				aflag = 0;
6080193149Sdougb				if (rdataset->type == dns_rdatatype_nsec3) {
6081193149Sdougb					/*
6082193149Sdougb					 * NSEC3 records are not allowed to
6083193149Sdougb					 * appear in the answer section.
6084193149Sdougb					 */
6085224092Sdougb					log_formerr(fctx, "NSEC3 in answer");
6086193149Sdougb					return (DNS_R_FORMERR);
6087193149Sdougb				}
6088224092Sdougb
6089224092Sdougb				/*
6090224092Sdougb				 * Apply filters, if given, on answers to reject
6091224092Sdougb				 * a malicious attempt of rebinding.
6092224092Sdougb				 */
6093224092Sdougb				if ((rdataset->type == dns_rdatatype_a ||
6094224092Sdougb				     rdataset->type == dns_rdatatype_aaaa) &&
6095224092Sdougb				    !is_answeraddress_allowed(view, name,
6096224092Sdougb							      rdataset)) {
6097224092Sdougb					return (DNS_R_SERVFAIL);
6098224092Sdougb				}
6099224092Sdougb
6100135446Strhodes				if (rdataset->type == type && !found_cname) {
6101135446Strhodes					/*
6102135446Strhodes					 * We've found an ordinary answer.
6103135446Strhodes					 */
6104135446Strhodes					found = ISC_TRUE;
6105135446Strhodes					found_type = ISC_TRUE;
6106135446Strhodes					done = ISC_TRUE;
6107135446Strhodes					aflag = DNS_RDATASETATTR_ANSWER;
6108135446Strhodes				} else if (type == dns_rdatatype_any) {
6109135446Strhodes					/*
6110135446Strhodes					 * We've found an answer matching
6111135446Strhodes					 * an ANY query.  There may be
6112135446Strhodes					 * more.
6113135446Strhodes					 */
6114135446Strhodes					found = ISC_TRUE;
6115135446Strhodes					aflag = DNS_RDATASETATTR_ANSWER;
6116135446Strhodes				} else if (rdataset->type == dns_rdatatype_rrsig
6117135446Strhodes					   && rdataset->covers == type
6118135446Strhodes					   && !found_cname) {
6119135446Strhodes					/*
6120135446Strhodes					 * We've found a signature that
6121135446Strhodes					 * covers the type we're looking for.
6122135446Strhodes					 */
6123135446Strhodes					found = ISC_TRUE;
6124135446Strhodes					found_type = ISC_TRUE;
6125135446Strhodes					aflag = DNS_RDATASETATTR_ANSWERSIG;
6126135446Strhodes				} else if (rdataset->type ==
6127135446Strhodes					   dns_rdatatype_cname
6128135446Strhodes					   && !found_type) {
6129135446Strhodes					/*
6130135446Strhodes					 * We're looking for something else,
6131135446Strhodes					 * but we found a CNAME.
6132135446Strhodes					 *
6133135446Strhodes					 * Getting a CNAME response for some
6134254402Serwin					 * query types is an error, see
6135254402Serwin					 * RFC 4035, Section 2.5.
6136135446Strhodes					 */
6137135446Strhodes					if (type == dns_rdatatype_rrsig ||
6138254402Serwin					    type == dns_rdatatype_key ||
6139254402Serwin					    type == dns_rdatatype_nsec) {
6140224092Sdougb						char buf[DNS_RDATATYPE_FORMATSIZE];
6141224092Sdougb						dns_rdatatype_format(fctx->type,
6142224092Sdougb							      buf, sizeof(buf));
6143224092Sdougb						log_formerr(fctx,
6144224092Sdougb							    "CNAME response "
6145224092Sdougb							    "for %s RR", buf);
6146135446Strhodes						return (DNS_R_FORMERR);
6147224092Sdougb					}
6148135446Strhodes					found = ISC_TRUE;
6149135446Strhodes					found_cname = ISC_TRUE;
6150135446Strhodes					want_chaining = ISC_TRUE;
6151135446Strhodes					aflag = DNS_RDATASETATTR_ANSWER;
6152135446Strhodes					result = cname_target(rdataset,
6153135446Strhodes							      &tname);
6154135446Strhodes					if (result != ISC_R_SUCCESS)
6155135446Strhodes						return (result);
6156224092Sdougb					/* Apply filters on the target name. */
6157224092Sdougb					if (!is_answertarget_allowed(view,
6158224092Sdougb							name,
6159224092Sdougb							rdataset->type,
6160224092Sdougb							&tname,
6161224092Sdougb							&fctx->domain)) {
6162224092Sdougb						return (DNS_R_SERVFAIL);
6163224092Sdougb					}
6164135446Strhodes				} else if (rdataset->type == dns_rdatatype_rrsig
6165135446Strhodes					   && rdataset->covers ==
6166308205Sdelphij					      dns_rdatatype_cname
6167135446Strhodes					   && !found_type) {
6168135446Strhodes					/*
6169135446Strhodes					 * We're looking for something else,
6170135446Strhodes					 * but we found a SIG CNAME.
6171135446Strhodes					 */
6172135446Strhodes					found = ISC_TRUE;
6173135446Strhodes					found_cname = ISC_TRUE;
6174135446Strhodes					aflag = DNS_RDATASETATTR_ANSWERSIG;
6175135446Strhodes				}
6176135446Strhodes
6177135446Strhodes				if (found) {
6178135446Strhodes					/*
6179135446Strhodes					 * We've found an answer to our
6180135446Strhodes					 * question.
6181135446Strhodes					 */
6182135446Strhodes					name->attributes |=
6183135446Strhodes						DNS_NAMEATTR_CACHE;
6184135446Strhodes					rdataset->attributes |=
6185135446Strhodes						DNS_RDATASETATTR_CACHE;
6186135446Strhodes					rdataset->trust = dns_trust_answer;
6187135446Strhodes					if (!chaining) {
6188135446Strhodes						/*
6189135446Strhodes						 * This data is "the" answer
6190135446Strhodes						 * to our question only if
6191135446Strhodes						 * we're not chaining (i.e.
6192135446Strhodes						 * if we haven't followed
6193135446Strhodes						 * a CNAME or DNAME).
6194135446Strhodes						 */
6195135446Strhodes						INSIST(!external);
6196308205Sdelphij						if ((rdataset->type !=
6197308205Sdelphij						     dns_rdatatype_cname) ||
6198308205Sdelphij						    !found_dname ||
6199308205Sdelphij						    (aflag ==
6200308205Sdelphij						     DNS_RDATASETATTR_ANSWER))
6201308205Sdelphij						{
6202135446Strhodes							have_answer = ISC_TRUE;
6203308205Sdelphij							if (rdataset->type ==
6204308205Sdelphij							    dns_rdatatype_cname)
6205308205Sdelphij								cname = name;
6206296611Sdelphij							name->attributes |=
6207308205Sdelphij							    DNS_NAMEATTR_ANSWER;
6208296611Sdelphij						}
6209135446Strhodes						rdataset->attributes |= aflag;
6210135446Strhodes						if (aa)
6211135446Strhodes							rdataset->trust =
6212135446Strhodes							  dns_trust_authanswer;
6213135446Strhodes					} else if (external) {
6214135446Strhodes						/*
6215135446Strhodes						 * This data is outside of
6216135446Strhodes						 * our query domain, and
6217202961Sdougb						 * may not be cached.
6218135446Strhodes						 */
6219135446Strhodes						rdataset->attributes |=
6220135446Strhodes						    DNS_RDATASETATTR_EXTERNAL;
6221135446Strhodes					}
6222135446Strhodes
6223135446Strhodes					/*
6224135446Strhodes					 * Mark any additional data related
6225135446Strhodes					 * to this rdataset.
6226135446Strhodes					 */
6227135446Strhodes					(void)dns_rdataset_additionaldata(
6228135446Strhodes							rdataset,
6229135446Strhodes							check_related,
6230135446Strhodes							fctx);
6231135446Strhodes
6232135446Strhodes					/*
6233135446Strhodes					 * CNAME chaining.
6234135446Strhodes					 */
6235135446Strhodes					if (want_chaining) {
6236135446Strhodes						wanted_chaining = ISC_TRUE;
6237135446Strhodes						name->attributes |=
6238135446Strhodes							DNS_NAMEATTR_CHAINING;
6239135446Strhodes						rdataset->attributes |=
6240135446Strhodes						    DNS_RDATASETATTR_CHAINING;
6241135446Strhodes						qname = &tname;
6242135446Strhodes					}
6243135446Strhodes				}
6244135446Strhodes				/*
6245135446Strhodes				 * We could add an "else" clause here and
6246135446Strhodes				 * log that we're ignoring this rdataset.
6247135446Strhodes				 */
6248135446Strhodes			}
6249135446Strhodes			/*
6250135446Strhodes			 * If wanted_chaining is true, we've done
6251135446Strhodes			 * some chaining as the result of processing
6252135446Strhodes			 * this node, and thus we need to set
6253135446Strhodes			 * chaining to true.
6254135446Strhodes			 *
6255135446Strhodes			 * We don't set chaining inside of the
6256135446Strhodes			 * rdataset loop because doing that would
6257135446Strhodes			 * cause us to ignore the signatures of
6258135446Strhodes			 * CNAMEs.
6259135446Strhodes			 */
6260135446Strhodes			if (wanted_chaining)
6261135446Strhodes				chaining = ISC_TRUE;
6262135446Strhodes		} else {
6263296611Sdelphij			dns_rdataset_t *dnameset = NULL;
6264296611Sdelphij
6265135446Strhodes			/*
6266135446Strhodes			 * Look for a DNAME (or its SIG).  Anything else is
6267135446Strhodes			 * ignored.
6268135446Strhodes			 */
6269135446Strhodes			wanted_chaining = ISC_FALSE;
6270135446Strhodes			for (rdataset = ISC_LIST_HEAD(name->list);
6271135446Strhodes			     rdataset != NULL;
6272296611Sdelphij			     rdataset = ISC_LIST_NEXT(rdataset, link))
6273296611Sdelphij			{
6274296611Sdelphij				/*
6275296611Sdelphij				 * Only pass DNAME or RRSIG(DNAME).
6276296611Sdelphij				 */
6277296611Sdelphij				if (rdataset->type != dns_rdatatype_dname &&
6278296611Sdelphij				    (rdataset->type != dns_rdatatype_rrsig ||
6279296611Sdelphij				     rdataset->covers != dns_rdatatype_dname))
6280296611Sdelphij					continue;
6281224092Sdougb
6282296611Sdelphij				/*
6283296611Sdelphij				 * If we're not chaining, then the DNAME and
6284296611Sdelphij				 * its signature should not be external.
6285296611Sdelphij				 */
6286296611Sdelphij				if (!chaining && external) {
6287296611Sdelphij					char qbuf[DNS_NAME_FORMATSIZE];
6288296611Sdelphij					char obuf[DNS_NAME_FORMATSIZE];
6289296611Sdelphij
6290296611Sdelphij					dns_name_format(name, qbuf,
6291296611Sdelphij							sizeof(qbuf));
6292296611Sdelphij					dns_name_format(&fctx->domain, obuf,
6293296611Sdelphij							sizeof(obuf));
6294296611Sdelphij					log_formerr(fctx, "external DNAME or "
6295296611Sdelphij						    "RRSIG covering DNAME "
6296296611Sdelphij						    "in answer: %s is "
6297296611Sdelphij						    "not in %s", qbuf, obuf);
6298296611Sdelphij					return (DNS_R_FORMERR);
6299296611Sdelphij				}
6300296611Sdelphij
6301308205Sdelphij				if (dnamereln != dns_namereln_subdomain) {
6302296611Sdelphij					char qbuf[DNS_NAME_FORMATSIZE];
6303296611Sdelphij					char obuf[DNS_NAME_FORMATSIZE];
6304296611Sdelphij
6305308205Sdelphij					dns_name_format(dqname, qbuf,
6306296611Sdelphij							sizeof(qbuf));
6307296611Sdelphij					dns_name_format(name, obuf,
6308296611Sdelphij							sizeof(obuf));
6309296611Sdelphij					log_formerr(fctx, "unrelated DNAME "
6310296611Sdelphij						    "in answer: %s is "
6311296611Sdelphij						    "not in %s", qbuf, obuf);
6312296611Sdelphij					return (DNS_R_FORMERR);
6313296611Sdelphij				}
6314296611Sdelphij
6315135446Strhodes				aflag = 0;
6316135446Strhodes				if (rdataset->type == dns_rdatatype_dname) {
6317135446Strhodes					want_chaining = ISC_TRUE;
6318225361Sdougb					POST(want_chaining);
6319135446Strhodes					aflag = DNS_RDATASETATTR_ANSWER;
6320308205Sdelphij					result = dname_target(rdataset, dqname,
6321296611Sdelphij							      nlabels, &fdname);
6322135446Strhodes					if (result == ISC_R_NOSPACE) {
6323135446Strhodes						/*
6324135446Strhodes						 * We can't construct the
6325135446Strhodes						 * DNAME target.  Do not
6326135446Strhodes						 * try to continue.
6327135446Strhodes						 */
6328135446Strhodes						want_chaining = ISC_FALSE;
6329225361Sdougb						POST(want_chaining);
6330135446Strhodes					} else if (result != ISC_R_SUCCESS)
6331135446Strhodes						return (result);
6332135446Strhodes					else
6333296611Sdelphij						dnameset = rdataset;
6334224092Sdougb
6335296611Sdelphij					dname = dns_fixedname_name(&fdname);
6336224092Sdougb					if (!is_answertarget_allowed(view,
6337308205Sdelphij						     dqname, rdataset->type,
6338308205Sdelphij						     dname, &fctx->domain))
6339308205Sdelphij					{
6340224092Sdougb						return (DNS_R_SERVFAIL);
6341224092Sdougb					}
6342308205Sdelphij					dqname = dns_fixedname_name(&fqdname);
6343308205Sdelphij					dns_name_copy(dname, dqname, NULL);
6344296611Sdelphij				} else {
6345135446Strhodes					/*
6346135446Strhodes					 * We've found a signature that
6347135446Strhodes					 * covers the DNAME.
6348135446Strhodes					 */
6349135446Strhodes					aflag = DNS_RDATASETATTR_ANSWERSIG;
6350135446Strhodes				}
6351135446Strhodes
6352296611Sdelphij				/*
6353296611Sdelphij				 * We've found an answer to our
6354296611Sdelphij				 * question.
6355296611Sdelphij				 */
6356296611Sdelphij				name->attributes |= DNS_NAMEATTR_CACHE;
6357296611Sdelphij				rdataset->attributes |= DNS_RDATASETATTR_CACHE;
6358296611Sdelphij				rdataset->trust = dns_trust_answer;
6359296611Sdelphij				if (!chaining) {
6360135446Strhodes					/*
6361296611Sdelphij					 * This data is "the" answer to
6362296611Sdelphij					 * our question only if we're
6363296611Sdelphij					 * not chaining.
6364135446Strhodes					 */
6365296611Sdelphij					INSIST(!external);
6366296611Sdelphij					if (aflag == DNS_RDATASETATTR_ANSWER) {
6367296611Sdelphij						have_answer = ISC_TRUE;
6368308205Sdelphij						found_dname = ISC_TRUE;
6369308205Sdelphij						if (cname != NULL)
6370308205Sdelphij							cname->attributes &=
6371308205Sdelphij							   ~DNS_NAMEATTR_ANSWER;
6372135446Strhodes						name->attributes |=
6373135446Strhodes							DNS_NAMEATTR_ANSWER;
6374135446Strhodes					}
6375296611Sdelphij					rdataset->attributes |= aflag;
6376296611Sdelphij					if (aa)
6377296611Sdelphij						rdataset->trust =
6378296611Sdelphij						  dns_trust_authanswer;
6379296611Sdelphij				} else if (external) {
6380296611Sdelphij					rdataset->attributes |=
6381296611Sdelphij					    DNS_RDATASETATTR_EXTERNAL;
6382135446Strhodes				}
6383135446Strhodes			}
6384296611Sdelphij
6385296611Sdelphij			/*
6386296611Sdelphij			 * DNAME chaining.
6387296611Sdelphij			 */
6388296611Sdelphij			if (dnameset != NULL) {
6389296611Sdelphij				/*
6390296611Sdelphij				 * Copy the dname into the qname fixed name.
6391296611Sdelphij				 *
6392296611Sdelphij				 * Although we check for failure of the copy
6393296611Sdelphij				 * operation, in practice it should never fail
6394296611Sdelphij				 * since we already know that the  result fits
6395296611Sdelphij				 * in a fixedname.
6396296611Sdelphij				 */
6397296611Sdelphij				dns_fixedname_init(&fqname);
6398296611Sdelphij				qname = dns_fixedname_name(&fqname);
6399296611Sdelphij				result = dns_name_copy(dname, qname, NULL);
6400296611Sdelphij				if (result != ISC_R_SUCCESS)
6401296611Sdelphij					return (result);
6402296611Sdelphij				wanted_chaining = ISC_TRUE;
6403296611Sdelphij				name->attributes |= DNS_NAMEATTR_CHAINING;
6404296611Sdelphij				dnameset->attributes |=
6405296611Sdelphij					    DNS_RDATASETATTR_CHAINING;
6406296611Sdelphij			}
6407135446Strhodes			if (wanted_chaining)
6408135446Strhodes				chaining = ISC_TRUE;
6409135446Strhodes		}
6410135446Strhodes		result = dns_message_nextname(message, DNS_SECTION_ANSWER);
6411135446Strhodes	}
6412135446Strhodes	if (result == ISC_R_NOMORE)
6413135446Strhodes		result = ISC_R_SUCCESS;
6414135446Strhodes	if (result != ISC_R_SUCCESS)
6415135446Strhodes		return (result);
6416135446Strhodes
6417135446Strhodes	/*
6418135446Strhodes	 * We should have found an answer.
6419135446Strhodes	 */
6420224092Sdougb	if (!have_answer) {
6421224092Sdougb		log_formerr(fctx, "reply has no answer");
6422135446Strhodes		return (DNS_R_FORMERR);
6423224092Sdougb	}
6424135446Strhodes
6425135446Strhodes	/*
6426135446Strhodes	 * This response is now potentially cacheable.
6427135446Strhodes	 */
6428135446Strhodes	fctx->attributes |= FCTX_ATTR_WANTCACHE;
6429135446Strhodes
6430135446Strhodes	/*
6431135446Strhodes	 * Did chaining end before we got the final answer?
6432135446Strhodes	 */
6433135446Strhodes	if (chaining) {
6434135446Strhodes		/*
6435135446Strhodes		 * Yes.  This may be a negative reply, so hand off
6436135446Strhodes		 * authority section processing to the noanswer code.
6437135446Strhodes		 * If it isn't a noanswer response, no harm will be
6438135446Strhodes		 * done.
6439135446Strhodes		 */
6440224092Sdougb		return (noanswer_response(fctx, qname, 0));
6441135446Strhodes	}
6442135446Strhodes
6443135446Strhodes	/*
6444135446Strhodes	 * We didn't end with an incomplete chain, so the rcode should be
6445135446Strhodes	 * "no error".
6446135446Strhodes	 */
6447224092Sdougb	if (message->rcode != dns_rcode_noerror) {
6448224092Sdougb		log_formerr(fctx, "CNAME/DNAME chain complete, but RCODE "
6449224092Sdougb				  "indicates error");
6450135446Strhodes		return (DNS_R_FORMERR);
6451224092Sdougb	}
6452135446Strhodes
6453135446Strhodes	/*
6454135446Strhodes	 * Examine the authority section (if there is one).
6455135446Strhodes	 *
6456135446Strhodes	 * We expect there to be only one owner name for all the rdatasets
6457135446Strhodes	 * in this section, and we expect that it is not external.
6458135446Strhodes	 */
6459135446Strhodes	done = ISC_FALSE;
6460234010Sdougb	ns_name = NULL;
6461234010Sdougb	ns_rdataset = NULL;
6462135446Strhodes	result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
6463135446Strhodes	while (!done && result == ISC_R_SUCCESS) {
6464135446Strhodes		name = NULL;
6465135446Strhodes		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
6466135446Strhodes		external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
6467135446Strhodes		if (!external) {
6468135446Strhodes			/*
6469135446Strhodes			 * We expect to find NS or SIG NS rdatasets, and
6470135446Strhodes			 * nothing else.
6471135446Strhodes			 */
6472135446Strhodes			for (rdataset = ISC_LIST_HEAD(name->list);
6473135446Strhodes			     rdataset != NULL;
6474135446Strhodes			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
6475135446Strhodes				if (rdataset->type == dns_rdatatype_ns ||
6476135446Strhodes				    (rdataset->type == dns_rdatatype_rrsig &&
6477135446Strhodes				     rdataset->covers == dns_rdatatype_ns)) {
6478135446Strhodes					name->attributes |=
6479135446Strhodes						DNS_NAMEATTR_CACHE;
6480135446Strhodes					rdataset->attributes |=
6481135446Strhodes						DNS_RDATASETATTR_CACHE;
6482135446Strhodes					if (aa && !chaining)
6483135446Strhodes						rdataset->trust =
6484135446Strhodes						    dns_trust_authauthority;
6485135446Strhodes					else
6486135446Strhodes						rdataset->trust =
6487135446Strhodes						    dns_trust_additional;
6488135446Strhodes
6489234010Sdougb					if (rdataset->type == dns_rdatatype_ns) {
6490234010Sdougb						ns_name = name;
6491234010Sdougb						ns_rdataset = rdataset;
6492234010Sdougb					}
6493135446Strhodes					/*
6494135446Strhodes					 * Mark any additional data related
6495135446Strhodes					 * to this rdataset.
6496135446Strhodes					 */
6497135446Strhodes					(void)dns_rdataset_additionaldata(
6498135446Strhodes							rdataset,
6499135446Strhodes							check_related,
6500135446Strhodes							fctx);
6501135446Strhodes					done = ISC_TRUE;
6502135446Strhodes				}
6503135446Strhodes			}
6504135446Strhodes		}
6505135446Strhodes		result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
6506135446Strhodes	}
6507135446Strhodes	if (result == ISC_R_NOMORE)
6508135446Strhodes		result = ISC_R_SUCCESS;
6509135446Strhodes
6510234010Sdougb	log_ns_ttl(fctx, "answer_response");
6511234010Sdougb
6512234010Sdougb	if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) &&
6513234010Sdougb	    !dns_name_equal(ns_name, dns_rootname))
6514234010Sdougb		trim_ns_ttl(fctx, ns_name, ns_rdataset);
6515234010Sdougb
6516135446Strhodes	return (result);
6517135446Strhodes}
6518135446Strhodes
6519214586Sdougbstatic isc_boolean_t
6520214586Sdougbfctx_decreference(fetchctx_t *fctx) {
6521214586Sdougb	isc_boolean_t bucket_empty = ISC_FALSE;
6522214586Sdougb
6523214586Sdougb	INSIST(fctx->references > 0);
6524214586Sdougb	fctx->references--;
6525214586Sdougb	if (fctx->references == 0) {
6526214586Sdougb		/*
6527214586Sdougb		 * No one cares about the result of this fetch anymore.
6528214586Sdougb		 */
6529214586Sdougb		if (fctx->pending == 0 && fctx->nqueries == 0 &&
6530214586Sdougb		    ISC_LIST_EMPTY(fctx->validators) && SHUTTINGDOWN(fctx)) {
6531214586Sdougb			/*
6532214586Sdougb			 * This fctx is already shutdown; we were just
6533214586Sdougb			 * waiting for the last reference to go away.
6534214586Sdougb			 */
6535236374Sdougb			bucket_empty = fctx_unlink(fctx);
6536236374Sdougb			fctx_destroy(fctx);
6537214586Sdougb		} else {
6538214586Sdougb			/*
6539214586Sdougb			 * Initiate shutdown.
6540214586Sdougb			 */
6541214586Sdougb			fctx_shutdown(fctx);
6542214586Sdougb		}
6543214586Sdougb	}
6544214586Sdougb	return (bucket_empty);
6545214586Sdougb}
6546214586Sdougb
6547135446Strhodesstatic void
6548135446Strhodesresume_dslookup(isc_task_t *task, isc_event_t *event) {
6549135446Strhodes	dns_fetchevent_t *fevent;
6550135446Strhodes	dns_resolver_t *res;
6551135446Strhodes	fetchctx_t *fctx;
6552135446Strhodes	isc_result_t result;
6553214586Sdougb	isc_boolean_t bucket_empty;
6554135446Strhodes	isc_boolean_t locked = ISC_FALSE;
6555135446Strhodes	unsigned int bucketnum;
6556135446Strhodes	dns_rdataset_t nameservers;
6557135446Strhodes	dns_fixedname_t fixed;
6558135446Strhodes	dns_name_t *domain;
6559135446Strhodes
6560135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
6561135446Strhodes	fevent = (dns_fetchevent_t *)event;
6562135446Strhodes	fctx = event->ev_arg;
6563135446Strhodes	REQUIRE(VALID_FCTX(fctx));
6564135446Strhodes	res = fctx->res;
6565135446Strhodes
6566135446Strhodes	UNUSED(task);
6567135446Strhodes	FCTXTRACE("resume_dslookup");
6568135446Strhodes
6569135446Strhodes	if (fevent->node != NULL)
6570135446Strhodes		dns_db_detachnode(fevent->db, &fevent->node);
6571135446Strhodes	if (fevent->db != NULL)
6572135446Strhodes		dns_db_detach(&fevent->db);
6573135446Strhodes
6574135446Strhodes	dns_rdataset_init(&nameservers);
6575135446Strhodes
6576135446Strhodes	bucketnum = fctx->bucketnum;
6577135446Strhodes	if (fevent->result == ISC_R_CANCELED) {
6578135446Strhodes		dns_resolver_destroyfetch(&fctx->nsfetch);
6579193149Sdougb		fctx_done(fctx, ISC_R_CANCELED, __LINE__);
6580135446Strhodes	} else if (fevent->result == ISC_R_SUCCESS) {
6581135446Strhodes
6582135446Strhodes		FCTXTRACE("resuming DS lookup");
6583135446Strhodes
6584135446Strhodes		dns_resolver_destroyfetch(&fctx->nsfetch);
6585135446Strhodes		if (dns_rdataset_isassociated(&fctx->nameservers))
6586135446Strhodes			dns_rdataset_disassociate(&fctx->nameservers);
6587135446Strhodes		dns_rdataset_clone(fevent->rdataset, &fctx->nameservers);
6588234010Sdougb		fctx->ns_ttl = fctx->nameservers.ttl;
6589234010Sdougb		fctx->ns_ttl_ok = ISC_TRUE;
6590234010Sdougb		log_ns_ttl(fctx, "resume_dslookup");
6591236374Sdougb		dns_name_free(&fctx->domain, fctx->mctx);
6592135446Strhodes		dns_name_init(&fctx->domain, NULL);
6593236374Sdougb		result = dns_name_dup(&fctx->nsname, fctx->mctx, &fctx->domain);
6594135446Strhodes		if (result != ISC_R_SUCCESS) {
6595193149Sdougb			fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
6596135446Strhodes			goto cleanup;
6597135446Strhodes		}
6598135446Strhodes		/*
6599135446Strhodes		 * Try again.
6600135446Strhodes		 */
6601205292Sdougb		fctx_try(fctx, ISC_TRUE, ISC_FALSE);
6602135446Strhodes	} else {
6603135446Strhodes		unsigned int n;
6604165071Sdougb		dns_rdataset_t *nsrdataset = NULL;
6605135446Strhodes
6606135446Strhodes		/*
6607135446Strhodes		 * Retrieve state from fctx->nsfetch before we destroy it.
6608135446Strhodes		 */
6609135446Strhodes		dns_fixedname_init(&fixed);
6610135446Strhodes		domain = dns_fixedname_name(&fixed);
6611135446Strhodes		dns_name_copy(&fctx->nsfetch->private->domain, domain, NULL);
6612135446Strhodes		if (dns_name_equal(&fctx->nsname, domain)) {
6613193149Sdougb			fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
6614165071Sdougb			dns_resolver_destroyfetch(&fctx->nsfetch);
6615135446Strhodes			goto cleanup;
6616135446Strhodes		}
6617165071Sdougb		if (dns_rdataset_isassociated(
6618165071Sdougb		    &fctx->nsfetch->private->nameservers)) {
6619165071Sdougb			dns_rdataset_clone(
6620165071Sdougb			    &fctx->nsfetch->private->nameservers,
6621165071Sdougb			    &nameservers);
6622165071Sdougb			nsrdataset = &nameservers;
6623165071Sdougb		} else
6624165071Sdougb			domain = NULL;
6625165071Sdougb		dns_resolver_destroyfetch(&fctx->nsfetch);
6626135446Strhodes		n = dns_name_countlabels(&fctx->nsname);
6627135446Strhodes		dns_name_getlabelsequence(&fctx->nsname, 1, n - 1,
6628135446Strhodes					  &fctx->nsname);
6629135446Strhodes
6630135446Strhodes		if (dns_rdataset_isassociated(fevent->rdataset))
6631135446Strhodes			dns_rdataset_disassociate(fevent->rdataset);
6632135446Strhodes		FCTXTRACE("continuing to look for parent's NS records");
6633135446Strhodes		result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
6634135446Strhodes						  dns_rdatatype_ns, domain,
6635165071Sdougb						  nsrdataset, NULL, 0, task,
6636135446Strhodes						  resume_dslookup, fctx,
6637135446Strhodes						  &fctx->nsrrset, NULL,
6638135446Strhodes						  &fctx->nsfetch);
6639135446Strhodes		if (result != ISC_R_SUCCESS)
6640193149Sdougb			fctx_done(fctx, result, __LINE__);
6641135446Strhodes		else {
6642135446Strhodes			LOCK(&res->buckets[bucketnum].lock);
6643135446Strhodes			locked = ISC_TRUE;
6644135446Strhodes			fctx->references++;
6645135446Strhodes		}
6646135446Strhodes	}
6647135446Strhodes
6648135446Strhodes cleanup:
6649135446Strhodes	if (dns_rdataset_isassociated(&nameservers))
6650135446Strhodes		dns_rdataset_disassociate(&nameservers);
6651135446Strhodes	if (dns_rdataset_isassociated(fevent->rdataset))
6652135446Strhodes		dns_rdataset_disassociate(fevent->rdataset);
6653135446Strhodes	INSIST(fevent->sigrdataset == NULL);
6654135446Strhodes	isc_event_free(&event);
6655135446Strhodes	if (!locked)
6656135446Strhodes		LOCK(&res->buckets[bucketnum].lock);
6657214586Sdougb	bucket_empty = fctx_decreference(fctx);
6658135446Strhodes	UNLOCK(&res->buckets[bucketnum].lock);
6659135446Strhodes	if (bucket_empty)
6660135446Strhodes		empty_bucket(res);
6661135446Strhodes}
6662135446Strhodes
6663135446Strhodesstatic inline void
6664135446Strhodeschecknamessection(dns_message_t *message, dns_section_t section) {
6665135446Strhodes	isc_result_t result;
6666135446Strhodes	dns_name_t *name;
6667135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
6668135446Strhodes	dns_rdataset_t *rdataset;
6669186462Sdougb
6670135446Strhodes	for (result = dns_message_firstname(message, section);
6671135446Strhodes	     result == ISC_R_SUCCESS;
6672135446Strhodes	     result = dns_message_nextname(message, section))
6673135446Strhodes	{
6674135446Strhodes		name = NULL;
6675135446Strhodes		dns_message_currentname(message, section, &name);
6676135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
6677135446Strhodes		     rdataset != NULL;
6678135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
6679135446Strhodes			for (result = dns_rdataset_first(rdataset);
6680135446Strhodes			     result == ISC_R_SUCCESS;
6681135446Strhodes			     result = dns_rdataset_next(rdataset)) {
6682135446Strhodes				dns_rdataset_current(rdataset, &rdata);
6683135446Strhodes				if (!dns_rdata_checkowner(name, rdata.rdclass,
6684135446Strhodes							  rdata.type,
6685135446Strhodes							  ISC_FALSE) ||
6686135446Strhodes				    !dns_rdata_checknames(&rdata, name, NULL))
6687135446Strhodes				{
6688186462Sdougb					rdataset->attributes |=
6689135446Strhodes						DNS_RDATASETATTR_CHECKNAMES;
6690135446Strhodes				}
6691135446Strhodes				dns_rdata_reset(&rdata);
6692135446Strhodes			}
6693135446Strhodes		}
6694135446Strhodes	}
6695135446Strhodes}
6696135446Strhodes
6697135446Strhodesstatic void
6698135446Strhodeschecknames(dns_message_t *message) {
6699135446Strhodes
6700135446Strhodes	checknamessection(message, DNS_SECTION_ANSWER);
6701135446Strhodes	checknamessection(message, DNS_SECTION_AUTHORITY);
6702135446Strhodes	checknamessection(message, DNS_SECTION_ADDITIONAL);
6703135446Strhodes}
6704135446Strhodes
6705193149Sdougb/*
6706193149Sdougb * Log server NSID at log level 'level'
6707193149Sdougb */
6708254402Serwinstatic void
6709254402Serwinlog_nsid(isc_buffer_t *opt, size_t nsid_len, resquery_t *query,
6710254402Serwin	 int level, isc_mem_t *mctx)
6711193149Sdougb{
6712193149Sdougb	static const char hex[17] = "0123456789abcdef";
6713193149Sdougb	char addrbuf[ISC_SOCKADDR_FORMATSIZE];
6714254402Serwin	isc_uint16_t buflen, i;
6715193149Sdougb	unsigned char *p, *buf, *nsid;
6716193149Sdougb
6717193149Sdougb	/* Allocate buffer for storing hex version of the NSID */
6718262706Serwin	buflen = (isc_uint16_t)nsid_len * 2 + 1;
6719193149Sdougb	buf = isc_mem_get(mctx, buflen);
6720193149Sdougb	if (buf == NULL)
6721254402Serwin		return;
6722193149Sdougb
6723193149Sdougb	/* Convert to hex */
6724193149Sdougb	p = buf;
6725254402Serwin	nsid = isc_buffer_current(opt);
6726193149Sdougb	for (i = 0; i < nsid_len; i++) {
6727193149Sdougb		*p++ = hex[(nsid[0] >> 4) & 0xf];
6728193149Sdougb		*p++ = hex[nsid[0] & 0xf];
6729193149Sdougb		nsid++;
6730193149Sdougb	}
6731193149Sdougb	*p = '\0';
6732193149Sdougb
6733193149Sdougb	isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
6734193149Sdougb			    sizeof(addrbuf));
6735193149Sdougb	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
6736193149Sdougb		      DNS_LOGMODULE_RESOLVER, level,
6737193149Sdougb		      "received NSID '%s' from %s", buf, addrbuf);
6738193149Sdougb
6739193149Sdougb	/* Clean up */
6740193149Sdougb	isc_mem_put(mctx, buf, buflen);
6741254402Serwin	return;
6742193149Sdougb}
6743193149Sdougb
6744135446Strhodesstatic void
6745153816Sdougblog_packet(dns_message_t *message, int level, isc_mem_t *mctx) {
6746153816Sdougb	isc_buffer_t buffer;
6747153816Sdougb	char *buf = NULL;
6748153816Sdougb	int len = 1024;
6749153816Sdougb	isc_result_t result;
6750153816Sdougb
6751153816Sdougb	if (! isc_log_wouldlog(dns_lctx, level))
6752153816Sdougb		return;
6753153816Sdougb
6754153816Sdougb	/*
6755153816Sdougb	 * Note that these are multiline debug messages.  We want a newline
6756153816Sdougb	 * to appear in the log after each message.
6757153816Sdougb	 */
6758153816Sdougb
6759153816Sdougb	do {
6760153816Sdougb		buf = isc_mem_get(mctx, len);
6761153816Sdougb		if (buf == NULL)
6762153816Sdougb			break;
6763153816Sdougb		isc_buffer_init(&buffer, buf, len);
6764153816Sdougb		result = dns_message_totext(message, &dns_master_style_debug,
6765153816Sdougb					    0, &buffer);
6766153816Sdougb		if (result == ISC_R_NOSPACE) {
6767153816Sdougb			isc_mem_put(mctx, buf, len);
6768153816Sdougb			len += 1024;
6769153816Sdougb		} else if (result == ISC_R_SUCCESS)
6770153816Sdougb			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
6771153816Sdougb				      DNS_LOGMODULE_RESOLVER, level,
6772153816Sdougb				      "received packet:\n%.*s",
6773153816Sdougb				      (int)isc_buffer_usedlength(&buffer),
6774153816Sdougb				      buf);
6775153816Sdougb	} while (result == ISC_R_NOSPACE);
6776153816Sdougb
6777153816Sdougb	if (buf != NULL)
6778153816Sdougb		isc_mem_put(mctx, buf, len);
6779153816Sdougb}
6780153816Sdougb
6781224092Sdougbstatic isc_boolean_t
6782224092Sdougbiscname(fetchctx_t *fctx) {
6783224092Sdougb	isc_result_t result;
6784224092Sdougb
6785224092Sdougb	result = dns_message_findname(fctx->rmessage, DNS_SECTION_ANSWER,
6786224092Sdougb				      &fctx->name, dns_rdatatype_cname, 0,
6787224092Sdougb				      NULL, NULL);
6788224092Sdougb	return (result == ISC_R_SUCCESS ? ISC_TRUE : ISC_FALSE);
6789224092Sdougb}
6790224092Sdougb
6791224092Sdougbstatic isc_boolean_t
6792224092Sdougbbetterreferral(fetchctx_t *fctx) {
6793224092Sdougb	isc_result_t result;
6794224092Sdougb	dns_name_t *name;
6795224092Sdougb	dns_rdataset_t *rdataset;
6796224092Sdougb	dns_message_t *message = fctx->rmessage;
6797224092Sdougb
6798224092Sdougb	for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
6799224092Sdougb	     result == ISC_R_SUCCESS;
6800224092Sdougb	     result = dns_message_nextname(message, DNS_SECTION_AUTHORITY)) {
6801224092Sdougb		name = NULL;
6802224092Sdougb		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
6803224092Sdougb		if (!isstrictsubdomain(name, &fctx->domain))
6804224092Sdougb			continue;
6805224092Sdougb		for (rdataset = ISC_LIST_HEAD(name->list);
6806224092Sdougb		     rdataset != NULL;
6807224092Sdougb		     rdataset = ISC_LIST_NEXT(rdataset, link))
6808224092Sdougb			if (rdataset->type == dns_rdatatype_ns)
6809224092Sdougb				return (ISC_TRUE);
6810224092Sdougb	}
6811224092Sdougb	return (ISC_FALSE);
6812224092Sdougb}
6813224092Sdougb
6814153816Sdougbstatic void
6815254402Serwinprocess_opt(resquery_t *query, dns_rdataset_t *opt) {
6816254402Serwin	dns_rdata_t rdata;
6817254402Serwin	isc_buffer_t optbuf;
6818254402Serwin	isc_result_t result;
6819254402Serwin	isc_uint16_t optcode;
6820254402Serwin	isc_uint16_t optlen;
6821254402Serwin
6822254402Serwin	result = dns_rdataset_first(opt);
6823254402Serwin	if (result == ISC_R_SUCCESS) {
6824254402Serwin		dns_rdata_init(&rdata);
6825254402Serwin		dns_rdataset_current(opt, &rdata);
6826254402Serwin		isc_buffer_init(&optbuf, rdata.data, rdata.length);
6827254402Serwin		isc_buffer_add(&optbuf, rdata.length);
6828254402Serwin		while (isc_buffer_remaininglength(&optbuf) >= 4) {
6829254402Serwin			optcode = isc_buffer_getuint16(&optbuf);
6830254402Serwin			optlen = isc_buffer_getuint16(&optbuf);
6831254402Serwin			INSIST(optlen <= isc_buffer_remaininglength(&optbuf));
6832254402Serwin			switch (optcode) {
6833254402Serwin			case DNS_OPT_NSID:
6834254402Serwin				if (query->options & DNS_FETCHOPT_WANTNSID)
6835254402Serwin					log_nsid(&optbuf, optlen, query,
6836254402Serwin						 ISC_LOG_INFO,
6837254402Serwin						 query->fctx->res->mctx);
6838254402Serwin				isc_buffer_forward(&optbuf, optlen);
6839254402Serwin				break;
6840254402Serwin			default:
6841254402Serwin				isc_buffer_forward(&optbuf, optlen);
6842254402Serwin				break;
6843254402Serwin			}
6844254402Serwin		}
6845254402Serwin		INSIST(isc_buffer_remaininglength(&optbuf) == 0U);
6846254402Serwin	}
6847254402Serwin}
6848254402Serwin
6849254402Serwinstatic void
6850135446Strhodesresquery_response(isc_task_t *task, isc_event_t *event) {
6851135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
6852135446Strhodes	resquery_t *query = event->ev_arg;
6853135446Strhodes	dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
6854135446Strhodes	isc_boolean_t keep_trying, get_nameservers, resend;
6855135446Strhodes	isc_boolean_t truncated;
6856135446Strhodes	dns_message_t *message;
6857193149Sdougb	dns_rdataset_t *opt;
6858135446Strhodes	fetchctx_t *fctx;
6859135446Strhodes	dns_name_t *fname;
6860135446Strhodes	dns_fixedname_t foundname;
6861135446Strhodes	isc_stdtime_t now;
6862135446Strhodes	isc_time_t tnow, *finish;
6863135446Strhodes	dns_adbaddrinfo_t *addrinfo;
6864135446Strhodes	unsigned int options;
6865135446Strhodes	unsigned int findoptions;
6866135446Strhodes	isc_result_t broken_server;
6867193149Sdougb	badnstype_t broken_type = badns_response;
6868218384Sdougb	isc_boolean_t no_response;
6869135446Strhodes
6870135446Strhodes	REQUIRE(VALID_QUERY(query));
6871135446Strhodes	fctx = query->fctx;
6872135446Strhodes	options = query->options;
6873135446Strhodes	REQUIRE(VALID_FCTX(fctx));
6874135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_DISPATCH);
6875135446Strhodes
6876135446Strhodes	QTRACE("response");
6877135446Strhodes
6878193149Sdougb	if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET)
6879193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_responsev4);
6880193149Sdougb	else
6881193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_responsev6);
6882193149Sdougb
6883135446Strhodes	(void)isc_timer_touch(fctx->timer);
6884135446Strhodes
6885135446Strhodes	keep_trying = ISC_FALSE;
6886135446Strhodes	broken_server = ISC_R_SUCCESS;
6887135446Strhodes	get_nameservers = ISC_FALSE;
6888135446Strhodes	resend = ISC_FALSE;
6889135446Strhodes	truncated = ISC_FALSE;
6890135446Strhodes	finish = NULL;
6891218384Sdougb	no_response = ISC_FALSE;
6892135446Strhodes
6893135446Strhodes	if (fctx->res->exiting) {
6894135446Strhodes		result = ISC_R_SHUTTINGDOWN;
6895135446Strhodes		goto done;
6896135446Strhodes	}
6897135446Strhodes
6898135446Strhodes	fctx->timeouts = 0;
6899193149Sdougb	fctx->timeout = ISC_FALSE;
6900224092Sdougb	fctx->addrinfo = query->addrinfo;
6901135446Strhodes
6902135446Strhodes	/*
6903135446Strhodes	 * XXXRTH  We should really get the current time just once.  We
6904193149Sdougb	 *		need a routine to convert from an isc_time_t to an
6905193149Sdougb	 *		isc_stdtime_t.
6906135446Strhodes	 */
6907135446Strhodes	TIME_NOW(&tnow);
6908135446Strhodes	finish = &tnow;
6909135446Strhodes	isc_stdtime_get(&now);
6910135446Strhodes
6911135446Strhodes	/*
6912135446Strhodes	 * Did the dispatcher have a problem?
6913135446Strhodes	 */
6914135446Strhodes	if (devent->result != ISC_R_SUCCESS) {
6915135446Strhodes		if (devent->result == ISC_R_EOF &&
6916135446Strhodes		    (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
6917135446Strhodes			/*
6918135446Strhodes			 * The problem might be that they
6919135446Strhodes			 * don't understand EDNS0.  Turn it
6920135446Strhodes			 * off and try again.
6921135446Strhodes			 */
6922135446Strhodes			options |= DNS_FETCHOPT_NOEDNS0;
6923135446Strhodes			resend = ISC_TRUE;
6924254402Serwin			add_bad_edns(fctx, &query->addrinfo->sockaddr);
6925135446Strhodes		} else {
6926135446Strhodes			/*
6927135446Strhodes			 * There's no hope for this query.
6928135446Strhodes			 */
6929135446Strhodes			keep_trying = ISC_TRUE;
6930186462Sdougb
6931186462Sdougb			/*
6932186462Sdougb			 * If this is a network error on an exclusive query
6933186462Sdougb			 * socket, mark the server as bad so that we won't try
6934218384Sdougb			 * it for this fetch again.  Also adjust finish and
6935218384Sdougb			 * no_response so that we penalize this address in SRTT
6936218384Sdougb			 * adjustment later.
6937186462Sdougb			 */
6938186462Sdougb			if (query->exclusivesocket &&
6939186462Sdougb			    (devent->result == ISC_R_HOSTUNREACH ||
6940186462Sdougb			     devent->result == ISC_R_NETUNREACH ||
6941186462Sdougb			     devent->result == ISC_R_CONNREFUSED ||
6942186462Sdougb			     devent->result == ISC_R_CANCELED)) {
6943186462Sdougb				    broken_server = devent->result;
6944193149Sdougb				    broken_type = badns_unreachable;
6945218384Sdougb				    finish = NULL;
6946218384Sdougb				    no_response = ISC_TRUE;
6947186462Sdougb			}
6948135446Strhodes		}
6949135446Strhodes		goto done;
6950135446Strhodes	}
6951135446Strhodes
6952135446Strhodes	message = fctx->rmessage;
6953135446Strhodes
6954135446Strhodes	if (query->tsig != NULL) {
6955135446Strhodes		result = dns_message_setquerytsig(message, query->tsig);
6956135446Strhodes		if (result != ISC_R_SUCCESS)
6957135446Strhodes			goto done;
6958135446Strhodes	}
6959135446Strhodes
6960135446Strhodes	if (query->tsigkey) {
6961135446Strhodes		result = dns_message_settsigkey(message, query->tsigkey);
6962135446Strhodes		if (result != ISC_R_SUCCESS)
6963135446Strhodes			goto done;
6964135446Strhodes	}
6965135446Strhodes
6966292321Sdelphij	dns_message_setclass(message, fctx->res->rdclass);
6967292321Sdelphij
6968135446Strhodes	result = dns_message_parse(message, &devent->buffer, 0);
6969135446Strhodes	if (result != ISC_R_SUCCESS) {
6970135446Strhodes		switch (result) {
6971135446Strhodes		case ISC_R_UNEXPECTEDEND:
6972135446Strhodes			if (!message->question_ok ||
6973135446Strhodes			    (message->flags & DNS_MESSAGEFLAG_TC) == 0 ||
6974135446Strhodes			    (options & DNS_FETCHOPT_TCP) != 0) {
6975135446Strhodes				/*
6976135446Strhodes				 * Either the message ended prematurely,
6977135446Strhodes				 * and/or wasn't marked as being truncated,
6978135446Strhodes				 * and/or this is a response to a query we
6979135446Strhodes				 * sent over TCP.  In all of these cases,
6980135446Strhodes				 * something is wrong with the remote
6981135446Strhodes				 * server and we don't want to retry using
6982135446Strhodes				 * TCP.
6983135446Strhodes				 */
6984135446Strhodes				if ((query->options & DNS_FETCHOPT_NOEDNS0)
6985135446Strhodes				    == 0) {
6986135446Strhodes					/*
6987135446Strhodes					 * The problem might be that they
6988135446Strhodes					 * don't understand EDNS0.  Turn it
6989135446Strhodes					 * off and try again.
6990135446Strhodes					 */
6991135446Strhodes					options |= DNS_FETCHOPT_NOEDNS0;
6992135446Strhodes					resend = ISC_TRUE;
6993254402Serwin					add_bad_edns(fctx,
6994254402Serwin						    &query->addrinfo->sockaddr);
6995193149Sdougb					inc_stats(fctx->res,
6996193149Sdougb						 dns_resstatscounter_edns0fail);
6997135446Strhodes				} else {
6998135446Strhodes					broken_server = result;
6999135446Strhodes					keep_trying = ISC_TRUE;
7000135446Strhodes				}
7001135446Strhodes				goto done;
7002135446Strhodes			}
7003135446Strhodes			/*
7004135446Strhodes			 * We defer retrying via TCP for a bit so we can
7005135446Strhodes			 * check out this message further.
7006135446Strhodes			 */
7007135446Strhodes			truncated = ISC_TRUE;
7008135446Strhodes			break;
7009135446Strhodes		case DNS_R_FORMERR:
7010135446Strhodes			if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
7011135446Strhodes				/*
7012135446Strhodes				 * The problem might be that they
7013135446Strhodes				 * don't understand EDNS0.  Turn it
7014135446Strhodes				 * off and try again.
7015135446Strhodes				 */
7016135446Strhodes				options |= DNS_FETCHOPT_NOEDNS0;
7017135446Strhodes				resend = ISC_TRUE;
7018254402Serwin				add_bad_edns(fctx, &query->addrinfo->sockaddr);
7019193149Sdougb				inc_stats(fctx->res,
7020193149Sdougb						 dns_resstatscounter_edns0fail);
7021135446Strhodes			} else {
7022135446Strhodes				broken_server = DNS_R_UNEXPECTEDRCODE;
7023135446Strhodes				keep_trying = ISC_TRUE;
7024135446Strhodes			}
7025135446Strhodes			goto done;
7026135446Strhodes		default:
7027135446Strhodes			/*
7028135446Strhodes			 * Something bad has happened.
7029135446Strhodes			 */
7030135446Strhodes			goto done;
7031135446Strhodes		}
7032135446Strhodes	}
7033135446Strhodes
7034193149Sdougb
7035135446Strhodes	/*
7036153816Sdougb	 * Log the incoming packet.
7037153816Sdougb	 */
7038153816Sdougb	log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx);
7039153816Sdougb
7040292321Sdelphij	if (message->rdclass != fctx->res->rdclass) {
7041292321Sdelphij		resend = ISC_TRUE;
7042292321Sdelphij		FCTXTRACE("bad class");
7043292321Sdelphij		goto done;
7044292321Sdelphij	}
7045292321Sdelphij
7046153816Sdougb	/*
7047254402Serwin	 * Process receive opt record.
7048193149Sdougb	 */
7049193149Sdougb	opt = dns_message_getopt(message);
7050254402Serwin	if (opt != NULL)
7051254402Serwin		process_opt(query, opt);
7052193149Sdougb
7053193149Sdougb	/*
7054135446Strhodes	 * If the message is signed, check the signature.  If not, this
7055135446Strhodes	 * returns success anyway.
7056135446Strhodes	 */
7057135446Strhodes	result = dns_message_checksig(message, fctx->res->view);
7058135446Strhodes	if (result != ISC_R_SUCCESS)
7059135446Strhodes		goto done;
7060135446Strhodes
7061135446Strhodes	/*
7062135446Strhodes	 * The dispatcher should ensure we only get responses with QR set.
7063135446Strhodes	 */
7064135446Strhodes	INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0);
7065135446Strhodes	/*
7066135446Strhodes	 * INSIST() that the message comes from the place we sent it to,
7067135446Strhodes	 * since the dispatch code should ensure this.
7068135446Strhodes	 *
7069135446Strhodes	 * INSIST() that the message id is correct (this should also be
7070135446Strhodes	 * ensured by the dispatch code).
7071135446Strhodes	 */
7072135446Strhodes
7073218384Sdougb	/*
7074218384Sdougb	 * We have an affirmative response to the query and we have
7075218384Sdougb	 * previously got a response from this server which indicated
7076218384Sdougb	 * EDNS may not be supported so we can now cache the lack of
7077218384Sdougb	 * EDNS support.
7078218384Sdougb	 */
7079218384Sdougb	if (opt == NULL &&
7080218384Sdougb	    (message->rcode == dns_rcode_noerror ||
7081218384Sdougb	     message->rcode == dns_rcode_nxdomain ||
7082218384Sdougb	     message->rcode == dns_rcode_refused ||
7083218384Sdougb	     message->rcode == dns_rcode_yxdomain) &&
7084218384Sdougb	     bad_edns(fctx, &query->addrinfo->sockaddr)) {
7085218384Sdougb		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
7086218384Sdougb		isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
7087218384Sdougb				    sizeof(addrbuf));
7088218384Sdougb		dns_adb_changeflags(fctx->adb, query->addrinfo,
7089218384Sdougb				    DNS_FETCHOPT_NOEDNS0,
7090218384Sdougb				    DNS_FETCHOPT_NOEDNS0);
7091218384Sdougb	}
7092135446Strhodes
7093135446Strhodes	/*
7094135446Strhodes	 * Deal with truncated responses by retrying using TCP.
7095135446Strhodes	 */
7096135446Strhodes	if ((message->flags & DNS_MESSAGEFLAG_TC) != 0)
7097135446Strhodes		truncated = ISC_TRUE;
7098135446Strhodes
7099135446Strhodes	if (truncated) {
7100193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_truncated);
7101135446Strhodes		if ((options & DNS_FETCHOPT_TCP) != 0) {
7102135446Strhodes			broken_server = DNS_R_TRUNCATEDTCP;
7103135446Strhodes			keep_trying = ISC_TRUE;
7104135446Strhodes		} else {
7105135446Strhodes			options |= DNS_FETCHOPT_TCP;
7106135446Strhodes			resend = ISC_TRUE;
7107135446Strhodes		}
7108135446Strhodes		goto done;
7109135446Strhodes	}
7110135446Strhodes
7111135446Strhodes	/*
7112135446Strhodes	 * Is it a query response?
7113135446Strhodes	 */
7114135446Strhodes	if (message->opcode != dns_opcode_query) {
7115135446Strhodes		/* XXXRTH Log */
7116135446Strhodes		broken_server = DNS_R_UNEXPECTEDOPCODE;
7117135446Strhodes		keep_trying = ISC_TRUE;
7118135446Strhodes		goto done;
7119135446Strhodes	}
7120135446Strhodes
7121135446Strhodes	/*
7122193149Sdougb	 * Update statistics about erroneous responses.
7123193149Sdougb	 */
7124193149Sdougb	if (message->rcode != dns_rcode_noerror) {
7125193149Sdougb		switch (message->rcode) {
7126193149Sdougb		case dns_rcode_nxdomain:
7127193149Sdougb			inc_stats(fctx->res, dns_resstatscounter_nxdomain);
7128193149Sdougb			break;
7129193149Sdougb		case dns_rcode_servfail:
7130193149Sdougb			inc_stats(fctx->res, dns_resstatscounter_servfail);
7131193149Sdougb			break;
7132193149Sdougb		case dns_rcode_formerr:
7133193149Sdougb			inc_stats(fctx->res, dns_resstatscounter_formerr);
7134193149Sdougb			break;
7135193149Sdougb		default:
7136193149Sdougb			inc_stats(fctx->res, dns_resstatscounter_othererror);
7137193149Sdougb			break;
7138193149Sdougb		}
7139193149Sdougb	}
7140193149Sdougb
7141193149Sdougb	/*
7142135446Strhodes	 * Is the remote server broken, or does it dislike us?
7143135446Strhodes	 */
7144135446Strhodes	if (message->rcode != dns_rcode_noerror &&
7145135446Strhodes	    message->rcode != dns_rcode_nxdomain) {
7146186462Sdougb		if (((message->rcode == dns_rcode_formerr ||
7147218384Sdougb		      message->rcode == dns_rcode_notimp) ||
7148218384Sdougb		     (message->rcode == dns_rcode_servfail &&
7149218384Sdougb		      dns_message_getopt(message) == NULL)) &&
7150135446Strhodes		    (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
7151135446Strhodes			/*
7152135446Strhodes			 * It's very likely they don't like EDNS0.
7153186462Sdougb			 * If the response code is SERVFAIL, also check if the
7154186462Sdougb			 * response contains an OPT RR and don't cache the
7155186462Sdougb			 * failure since it can be returned for various other
7156186462Sdougb			 * reasons.
7157135446Strhodes			 *
7158135446Strhodes			 * XXXRTH  We should check if the question
7159193149Sdougb			 *		we're asking requires EDNS0, and
7160193149Sdougb			 *		if so, we should bail out.
7161135446Strhodes			 */
7162135446Strhodes			options |= DNS_FETCHOPT_NOEDNS0;
7163135446Strhodes			resend = ISC_TRUE;
7164135446Strhodes			/*
7165218384Sdougb			 * Remember that they may not like EDNS0.
7166135446Strhodes			 */
7167218384Sdougb			add_bad_edns(fctx, &query->addrinfo->sockaddr);
7168193149Sdougb			inc_stats(fctx->res, dns_resstatscounter_edns0fail);
7169135446Strhodes		} else if (message->rcode == dns_rcode_formerr) {
7170135446Strhodes			if (ISFORWARDER(query->addrinfo)) {
7171135446Strhodes				/*
7172135446Strhodes				 * This forwarder doesn't understand us,
7173135446Strhodes				 * but other forwarders might.  Keep trying.
7174135446Strhodes				 */
7175135446Strhodes				broken_server = DNS_R_REMOTEFORMERR;
7176135446Strhodes				keep_trying = ISC_TRUE;
7177135446Strhodes			} else {
7178135446Strhodes				/*
7179135446Strhodes				 * The server doesn't understand us.  Since
7180135446Strhodes				 * all servers for a zone need similar
7181135446Strhodes				 * capabilities, we assume that we will get
7182135446Strhodes				 * FORMERR from all servers, and thus we
7183135446Strhodes				 * cannot make any more progress with this
7184135446Strhodes				 * fetch.
7185135446Strhodes				 */
7186224092Sdougb				log_formerr(fctx, "server sent FORMERR");
7187135446Strhodes				result = DNS_R_FORMERR;
7188135446Strhodes			}
7189135446Strhodes		} else if (message->rcode == dns_rcode_yxdomain) {
7190135446Strhodes			/*
7191135446Strhodes			 * DNAME mapping failed because the new name
7192135446Strhodes			 * was too long.  There's no chance of success
7193135446Strhodes			 * for this fetch.
7194135446Strhodes			 */
7195135446Strhodes			result = DNS_R_YXDOMAIN;
7196170222Sdougb		} else if (message->rcode == dns_rcode_badvers) {
7197170222Sdougb			unsigned int flags, mask;
7198170222Sdougb			unsigned int version;
7199170222Sdougb
7200170222Sdougb			resend = ISC_TRUE;
7201225361Sdougb			INSIST(opt != NULL);
7202170222Sdougb			version = (opt->ttl >> 16) & 0xff;
7203170222Sdougb			flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) |
7204170222Sdougb				DNS_FETCHOPT_EDNSVERSIONSET;
7205170222Sdougb			mask = DNS_FETCHOPT_EDNSVERSIONMASK |
7206170222Sdougb			       DNS_FETCHOPT_EDNSVERSIONSET;
7207170222Sdougb			switch (version) {
7208170222Sdougb			case 0:
7209170222Sdougb				dns_adb_changeflags(fctx->adb, query->addrinfo,
7210170222Sdougb						    flags, mask);
7211170222Sdougb				break;
7212170222Sdougb			default:
7213170222Sdougb				broken_server = DNS_R_BADVERS;
7214170222Sdougb				keep_trying = ISC_TRUE;
7215170222Sdougb				break;
7216170222Sdougb			}
7217135446Strhodes		} else {
7218135446Strhodes			/*
7219135446Strhodes			 * XXXRTH log.
7220135446Strhodes			 */
7221135446Strhodes			broken_server = DNS_R_UNEXPECTEDRCODE;
7222135446Strhodes			INSIST(broken_server != ISC_R_SUCCESS);
7223135446Strhodes			keep_trying = ISC_TRUE;
7224135446Strhodes		}
7225135446Strhodes		goto done;
7226135446Strhodes	}
7227135446Strhodes
7228135446Strhodes	/*
7229135446Strhodes	 * Is the question the same as the one we asked?
7230135446Strhodes	 */
7231135446Strhodes	result = same_question(fctx);
7232135446Strhodes	if (result != ISC_R_SUCCESS) {
7233135446Strhodes		/* XXXRTH Log */
7234135446Strhodes		if (result == DNS_R_FORMERR)
7235135446Strhodes			keep_trying = ISC_TRUE;
7236135446Strhodes		goto done;
7237135446Strhodes	}
7238135446Strhodes
7239135446Strhodes	/*
7240135446Strhodes	 * Is the server lame?
7241135446Strhodes	 */
7242135446Strhodes	if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) &&
7243135446Strhodes	    is_lame(fctx)) {
7244193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_lame);
7245135446Strhodes		log_lame(fctx, query->addrinfo);
7246135446Strhodes		result = dns_adb_marklame(fctx->adb, query->addrinfo,
7247170222Sdougb					  &fctx->name, fctx->type,
7248135446Strhodes					  now + fctx->res->lame_ttl);
7249135446Strhodes		if (result != ISC_R_SUCCESS)
7250135446Strhodes			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
7251135446Strhodes				      DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
7252135446Strhodes				      "could not mark server as lame: %s",
7253135446Strhodes				      isc_result_totext(result));
7254135446Strhodes		broken_server = DNS_R_LAME;
7255135446Strhodes		keep_trying = ISC_TRUE;
7256135446Strhodes		goto done;
7257135446Strhodes	}
7258135446Strhodes
7259135446Strhodes	/*
7260135446Strhodes	 * Enforce delegations only zones like NET and COM.
7261135446Strhodes	 */
7262135446Strhodes	if (!ISFORWARDER(query->addrinfo) &&
7263135446Strhodes	    dns_view_isdelegationonly(fctx->res->view, &fctx->domain) &&
7264135446Strhodes	    !dns_name_equal(&fctx->domain, &fctx->name) &&
7265135446Strhodes	    fix_mustbedelegationornxdomain(message, fctx)) {
7266135446Strhodes		char namebuf[DNS_NAME_FORMATSIZE];
7267135446Strhodes		char domainbuf[DNS_NAME_FORMATSIZE];
7268135446Strhodes		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
7269135446Strhodes		char classbuf[64];
7270135446Strhodes		char typebuf[64];
7271135446Strhodes
7272135446Strhodes		dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
7273135446Strhodes		dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
7274135446Strhodes		dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf));
7275135446Strhodes		dns_rdataclass_format(fctx->res->rdclass, classbuf,
7276135446Strhodes				      sizeof(classbuf));
7277135446Strhodes		isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
7278135446Strhodes				    sizeof(addrbuf));
7279135446Strhodes
7280135446Strhodes		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY,
7281135446Strhodes			     DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
7282135446Strhodes			     "enforced delegation-only for '%s' (%s/%s/%s) "
7283135446Strhodes			     "from %s",
7284135446Strhodes			     domainbuf, namebuf, typebuf, classbuf, addrbuf);
7285135446Strhodes	}
7286135446Strhodes
7287143731Sdougb	if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0)
7288135446Strhodes		checknames(message);
7289135446Strhodes
7290135446Strhodes	/*
7291143731Sdougb	 * Clear cache bits.
7292143731Sdougb	 */
7293143731Sdougb	fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE);
7294143731Sdougb
7295143731Sdougb	/*
7296135446Strhodes	 * Did we get any answers?
7297135446Strhodes	 */
7298135446Strhodes	if (message->counts[DNS_SECTION_ANSWER] > 0 &&
7299135446Strhodes	    (message->rcode == dns_rcode_noerror ||
7300135446Strhodes	     message->rcode == dns_rcode_nxdomain)) {
7301135446Strhodes		/*
7302224092Sdougb		 * [normal case]
7303224092Sdougb		 * We've got answers.  If it has an authoritative answer or an
7304224092Sdougb		 * answer from a forwarder, we're done.
7305135446Strhodes		 */
7306224092Sdougb		if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 ||
7307224092Sdougb		    ISFORWARDER(query->addrinfo))
7308224092Sdougb			result = answer_response(fctx);
7309224092Sdougb		else if (iscname(fctx) &&
7310224092Sdougb			 fctx->type != dns_rdatatype_any &&
7311224092Sdougb			 fctx->type != dns_rdatatype_cname) {
7312224092Sdougb			/*
7313224092Sdougb			 * A BIND8 server could return a non-authoritative
7314224092Sdougb			 * answer when a CNAME is followed.  We should treat
7315224092Sdougb			 * it as a valid answer.
7316224092Sdougb			 */
7317224092Sdougb			result = answer_response(fctx);
7318224092Sdougb		} else if (fctx->type != dns_rdatatype_ns &&
7319224092Sdougb			   !betterreferral(fctx)) {
7320224092Sdougb			/*
7321224092Sdougb			 * Lame response !!!.
7322224092Sdougb			 */
7323224092Sdougb			result = answer_response(fctx);
7324224092Sdougb		} else {
7325224092Sdougb			if (fctx->type == dns_rdatatype_ns) {
7326224092Sdougb				/*
7327224092Sdougb				 * A BIND 8 server could incorrectly return a
7328224092Sdougb				 * non-authoritative answer to an NS query
7329224092Sdougb				 * instead of a referral. Since this answer
7330224092Sdougb				 * lacks the SIGs necessary to do DNSSEC
7331224092Sdougb				 * validation, we must invoke the following
7332224092Sdougb				 * special kludge to treat it as a referral.
7333224092Sdougb				 */
7334224092Sdougb				result = noanswer_response(fctx, NULL,
7335224092Sdougb						   LOOK_FOR_NS_IN_ANSWER);
7336224092Sdougb			} else {
7337224092Sdougb				/*
7338224092Sdougb				 * Some other servers may still somehow include
7339224092Sdougb				 * an answer when it should return a referral
7340224092Sdougb				 * with an empty answer.  Check to see if we can
7341224092Sdougb				 * treat this as a referral by ignoring the
7342224092Sdougb				 * answer.  Further more, there may be an
7343224092Sdougb				 * implementation that moves A/AAAA glue records
7344224092Sdougb				 * to the answer section for that type of
7345224092Sdougb				 * delegation when the query is for that glue
7346224092Sdougb				 * record.  LOOK_FOR_GLUE_IN_ANSWER will handle
7347224092Sdougb				 * such a corner case.
7348224092Sdougb				 */
7349224092Sdougb				result = noanswer_response(fctx, NULL,
7350224092Sdougb						   LOOK_FOR_GLUE_IN_ANSWER);
7351224092Sdougb			}
7352135446Strhodes			if (result != DNS_R_DELEGATION) {
7353135446Strhodes				/*
7354224092Sdougb				 * At this point, AA is not set, the response
7355224092Sdougb				 * is not a referral, and the server is not a
7356224092Sdougb				 * forwarder.  It is technically lame and it's
7357224092Sdougb				 * easier to treat it as such than to figure out
7358135446Strhodes				 * some more elaborate course of action.
7359135446Strhodes				 */
7360135446Strhodes				broken_server = DNS_R_LAME;
7361135446Strhodes				keep_trying = ISC_TRUE;
7362135446Strhodes				goto done;
7363135446Strhodes			}
7364135446Strhodes			goto force_referral;
7365135446Strhodes		}
7366135446Strhodes		if (result != ISC_R_SUCCESS) {
7367135446Strhodes			if (result == DNS_R_FORMERR)
7368135446Strhodes				keep_trying = ISC_TRUE;
7369135446Strhodes			goto done;
7370135446Strhodes		}
7371135446Strhodes	} else if (message->counts[DNS_SECTION_AUTHORITY] > 0 ||
7372135446Strhodes		   message->rcode == dns_rcode_noerror ||
7373135446Strhodes		   message->rcode == dns_rcode_nxdomain) {
7374135446Strhodes		/*
7375135446Strhodes		 * NXDOMAIN, NXRDATASET, or referral.
7376135446Strhodes		 */
7377224092Sdougb		result = noanswer_response(fctx, NULL, 0);
7378262706Serwin		switch (result) {
7379262706Serwin		case ISC_R_SUCCESS:
7380262706Serwin		case DNS_R_CHASEDSSERVERS:
7381262706Serwin			break;
7382262706Serwin		case DNS_R_DELEGATION:
7383262706Serwin force_referral:
7384135446Strhodes			/*
7385135446Strhodes			 * We don't have the answer, but we know a better
7386135446Strhodes			 * place to look.
7387135446Strhodes			 */
7388135446Strhodes			get_nameservers = ISC_TRUE;
7389135446Strhodes			keep_trying = ISC_TRUE;
7390135446Strhodes			/*
7391135446Strhodes			 * We have a new set of name servers, and it
7392135446Strhodes			 * has not experienced any restarts yet.
7393135446Strhodes			 */
7394135446Strhodes			fctx->restarts = 0;
7395193149Sdougb
7396193149Sdougb			/*
7397193149Sdougb			 * Update local statistics counters collected for each
7398193149Sdougb			 * new zone.
7399193149Sdougb			 */
7400193149Sdougb			fctx->referrals++;
7401193149Sdougb			fctx->querysent = 0;
7402193149Sdougb			fctx->lamecount = 0;
7403193149Sdougb			fctx->neterr = 0;
7404193149Sdougb			fctx->badresp = 0;
7405193149Sdougb			fctx->adberr = 0;
7406193149Sdougb
7407135446Strhodes			result = ISC_R_SUCCESS;
7408262706Serwin			break;
7409262706Serwin		default:
7410135446Strhodes			/*
7411135446Strhodes			 * Something has gone wrong.
7412135446Strhodes			 */
7413135446Strhodes			if (result == DNS_R_FORMERR)
7414135446Strhodes				keep_trying = ISC_TRUE;
7415135446Strhodes			goto done;
7416135446Strhodes		}
7417135446Strhodes	} else {
7418135446Strhodes		/*
7419135446Strhodes		 * The server is insane.
7420135446Strhodes		 */
7421135446Strhodes		/* XXXRTH Log */
7422135446Strhodes		broken_server = DNS_R_UNEXPECTEDRCODE;
7423135446Strhodes		keep_trying = ISC_TRUE;
7424135446Strhodes		goto done;
7425135446Strhodes	}
7426135446Strhodes
7427135446Strhodes	/*
7428135446Strhodes	 * Follow additional section data chains.
7429135446Strhodes	 */
7430135446Strhodes	chase_additional(fctx);
7431135446Strhodes
7432135446Strhodes	/*
7433135446Strhodes	 * Cache the cacheable parts of the message.  This may also cause
7434135446Strhodes	 * work to be queued to the DNSSEC validator.
7435135446Strhodes	 */
7436135446Strhodes	if (WANTCACHE(fctx)) {
7437153816Sdougb		result = cache_message(fctx, query->addrinfo, now);
7438135446Strhodes		if (result != ISC_R_SUCCESS)
7439135446Strhodes			goto done;
7440135446Strhodes	}
7441135446Strhodes
7442135446Strhodes	/*
7443135446Strhodes	 * Ncache the negatively cacheable parts of the message.  This may
7444135446Strhodes	 * also cause work to be queued to the DNSSEC validator.
7445135446Strhodes	 */
7446135446Strhodes	if (WANTNCACHE(fctx)) {
7447135446Strhodes		dns_rdatatype_t covers;
7448135446Strhodes		if (message->rcode == dns_rcode_nxdomain)
7449135446Strhodes			covers = dns_rdatatype_any;
7450135446Strhodes		else
7451135446Strhodes			covers = fctx->type;
7452135446Strhodes
7453135446Strhodes		/*
7454135446Strhodes		 * Cache any negative cache entries in the message.
7455135446Strhodes		 */
7456153816Sdougb		result = ncache_message(fctx, query->addrinfo, covers, now);
7457135446Strhodes	}
7458135446Strhodes
7459135446Strhodes done:
7460135446Strhodes	/*
7461135446Strhodes	 * Remember the query's addrinfo, in case we need to mark the
7462135446Strhodes	 * server as broken.
7463135446Strhodes	 */
7464135446Strhodes	addrinfo = query->addrinfo;
7465135446Strhodes
7466135446Strhodes	/*
7467135446Strhodes	 * Cancel the query.
7468135446Strhodes	 *
7469135446Strhodes	 * XXXRTH  Don't cancel the query if waiting for validation?
7470135446Strhodes	 */
7471218384Sdougb	fctx_cancelquery(&query, &devent, finish, no_response);
7472135446Strhodes
7473135446Strhodes	if (keep_trying) {
7474135446Strhodes		if (result == DNS_R_FORMERR)
7475135446Strhodes			broken_server = DNS_R_FORMERR;
7476135446Strhodes		if (broken_server != ISC_R_SUCCESS) {
7477135446Strhodes			/*
7478135446Strhodes			 * Add this server to the list of bad servers for
7479135446Strhodes			 * this fctx.
7480135446Strhodes			 */
7481193149Sdougb			add_bad(fctx, addrinfo, broken_server, broken_type);
7482135446Strhodes		}
7483135446Strhodes
7484135446Strhodes		if (get_nameservers) {
7485135446Strhodes			dns_name_t *name;
7486135446Strhodes			dns_fixedname_init(&foundname);
7487135446Strhodes			fname = dns_fixedname_name(&foundname);
7488135446Strhodes			if (result != ISC_R_SUCCESS) {
7489193149Sdougb				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
7490135446Strhodes				return;
7491135446Strhodes			}
7492135446Strhodes			findoptions = 0;
7493143731Sdougb			if (dns_rdatatype_atparent(fctx->type))
7494143731Sdougb				findoptions |= DNS_DBFIND_NOEXACT;
7495135446Strhodes			if ((options & DNS_FETCHOPT_UNSHARED) == 0)
7496135446Strhodes				name = &fctx->name;
7497135446Strhodes			else
7498135446Strhodes				name = &fctx->domain;
7499135446Strhodes			result = dns_view_findzonecut(fctx->res->view,
7500135446Strhodes						      name, fname,
7501143731Sdougb						      now, findoptions,
7502143731Sdougb						      ISC_TRUE,
7503135446Strhodes						      &fctx->nameservers,
7504135446Strhodes						      NULL);
7505135446Strhodes			if (result != ISC_R_SUCCESS) {
7506135446Strhodes				FCTXTRACE("couldn't find a zonecut");
7507193149Sdougb				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
7508135446Strhodes				return;
7509135446Strhodes			}
7510135446Strhodes			if (!dns_name_issubdomain(fname, &fctx->domain)) {
7511135446Strhodes				/*
7512135446Strhodes				 * The best nameservers are now above our
7513135446Strhodes				 * QDOMAIN.
7514135446Strhodes				 */
7515135446Strhodes				FCTXTRACE("nameservers now above QDOMAIN");
7516193149Sdougb				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
7517135446Strhodes				return;
7518135446Strhodes			}
7519236374Sdougb			dns_name_free(&fctx->domain, fctx->mctx);
7520135446Strhodes			dns_name_init(&fctx->domain, NULL);
7521236374Sdougb			result = dns_name_dup(fname, fctx->mctx, &fctx->domain);
7522135446Strhodes			if (result != ISC_R_SUCCESS) {
7523193149Sdougb				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
7524135446Strhodes				return;
7525135446Strhodes			}
7526234010Sdougb			fctx->ns_ttl = fctx->nameservers.ttl;
7527234010Sdougb			fctx->ns_ttl_ok = ISC_TRUE;
7528135446Strhodes			fctx_cancelqueries(fctx, ISC_TRUE);
7529135446Strhodes			fctx_cleanupfinds(fctx);
7530135446Strhodes			fctx_cleanupaltfinds(fctx);
7531135446Strhodes			fctx_cleanupforwaddrs(fctx);
7532135446Strhodes			fctx_cleanupaltaddrs(fctx);
7533135446Strhodes		}
7534135446Strhodes		/*
7535135446Strhodes		 * Try again.
7536135446Strhodes		 */
7537205292Sdougb		fctx_try(fctx, !get_nameservers, ISC_FALSE);
7538135446Strhodes	} else if (resend) {
7539135446Strhodes		/*
7540135446Strhodes		 * Resend (probably with changed options).
7541135446Strhodes		 */
7542135446Strhodes		FCTXTRACE("resend");
7543193149Sdougb		inc_stats(fctx->res, dns_resstatscounter_retry);
7544135446Strhodes		result = fctx_query(fctx, addrinfo, options);
7545135446Strhodes		if (result != ISC_R_SUCCESS)
7546193149Sdougb			fctx_done(fctx, result, __LINE__);
7547135446Strhodes	} else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) {
7548135446Strhodes		/*
7549135446Strhodes		 * All has gone well so far, but we are waiting for the
7550135446Strhodes		 * DNSSEC validator to validate the answer.
7551135446Strhodes		 */
7552135446Strhodes		FCTXTRACE("wait for validator");
7553135446Strhodes		fctx_cancelqueries(fctx, ISC_TRUE);
7554135446Strhodes		/*
7555135446Strhodes		 * We must not retransmit while the validator is working;
7556135446Strhodes		 * it has references to the current rmessage.
7557135446Strhodes		 */
7558135446Strhodes		result = fctx_stopidletimer(fctx);
7559135446Strhodes		if (result != ISC_R_SUCCESS)
7560193149Sdougb			fctx_done(fctx, result, __LINE__);
7561135446Strhodes	} else if (result == DNS_R_CHASEDSSERVERS) {
7562135446Strhodes		unsigned int n;
7563193149Sdougb		add_bad(fctx, addrinfo, result, broken_type);
7564135446Strhodes		fctx_cancelqueries(fctx, ISC_TRUE);
7565135446Strhodes		fctx_cleanupfinds(fctx);
7566135446Strhodes		fctx_cleanupforwaddrs(fctx);
7567135446Strhodes
7568135446Strhodes		n = dns_name_countlabels(&fctx->name);
7569135446Strhodes		dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname);
7570135446Strhodes
7571135446Strhodes		FCTXTRACE("suspending DS lookup to find parent's NS records");
7572135446Strhodes
7573135446Strhodes		result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
7574135446Strhodes						  dns_rdatatype_ns,
7575135446Strhodes						  NULL, NULL, NULL, 0, task,
7576135446Strhodes						  resume_dslookup, fctx,
7577135446Strhodes						  &fctx->nsrrset, NULL,
7578135446Strhodes						  &fctx->nsfetch);
7579135446Strhodes		if (result != ISC_R_SUCCESS)
7580193149Sdougb			fctx_done(fctx, result, __LINE__);
7581214586Sdougb		else {
7582214586Sdougb			LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
7583214586Sdougb			fctx->references++;
7584214586Sdougb			UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
7585214586Sdougb			result = fctx_stopidletimer(fctx);
7586214586Sdougb			if (result != ISC_R_SUCCESS)
7587214586Sdougb				fctx_done(fctx, result, __LINE__);
7588214586Sdougb		}
7589135446Strhodes	} else {
7590135446Strhodes		/*
7591135446Strhodes		 * We're done.
7592135446Strhodes		 */
7593193149Sdougb		fctx_done(fctx, result, __LINE__);
7594135446Strhodes	}
7595135446Strhodes}
7596135446Strhodes
7597135446Strhodes
7598135446Strhodes/***
7599135446Strhodes *** Resolver Methods
7600135446Strhodes ***/
7601205292Sdougbstatic void
7602205292Sdougbdestroy_badcache(dns_resolver_t *res) {
7603205292Sdougb	dns_badcache_t *bad, *next;
7604205292Sdougb	unsigned int i;
7605135446Strhodes
7606205292Sdougb	if (res->badcache != NULL) {
7607205292Sdougb		for (i = 0; i < res->badhash; i++)
7608205292Sdougb			for (bad = res->badcache[i]; bad != NULL;
7609205292Sdougb			     bad = next) {
7610205292Sdougb				next = bad->next;
7611205292Sdougb				isc_mem_put(res->mctx, bad, sizeof(*bad) +
7612205292Sdougb					    bad->name.length);
7613205292Sdougb				res->badcount--;
7614205292Sdougb			}
7615205292Sdougb		isc_mem_put(res->mctx, res->badcache,
7616205292Sdougb			    sizeof(*res->badcache) * res->badhash);
7617205292Sdougb		res->badcache = NULL;
7618205292Sdougb		res->badhash = 0;
7619205292Sdougb		INSIST(res->badcount == 0);
7620205292Sdougb	}
7621205292Sdougb}
7622205292Sdougb
7623135446Strhodesstatic void
7624135446Strhodesdestroy(dns_resolver_t *res) {
7625135446Strhodes	unsigned int i;
7626135446Strhodes	alternate_t *a;
7627135446Strhodes
7628135446Strhodes	REQUIRE(res->references == 0);
7629135446Strhodes	REQUIRE(!res->priming);
7630135446Strhodes	REQUIRE(res->primefetch == NULL);
7631135446Strhodes
7632135446Strhodes	RTRACE("destroy");
7633135446Strhodes
7634135446Strhodes	INSIST(res->nfctx == 0);
7635135446Strhodes
7636135446Strhodes	DESTROYLOCK(&res->primelock);
7637135446Strhodes	DESTROYLOCK(&res->nlock);
7638135446Strhodes	DESTROYLOCK(&res->lock);
7639135446Strhodes	for (i = 0; i < res->nbuckets; i++) {
7640135446Strhodes		INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs));
7641135446Strhodes		isc_task_shutdown(res->buckets[i].task);
7642135446Strhodes		isc_task_detach(&res->buckets[i].task);
7643135446Strhodes		DESTROYLOCK(&res->buckets[i].lock);
7644170222Sdougb		isc_mem_detach(&res->buckets[i].mctx);
7645135446Strhodes	}
7646135446Strhodes	isc_mem_put(res->mctx, res->buckets,
7647135446Strhodes		    res->nbuckets * sizeof(fctxbucket_t));
7648254897Serwin	if (res->dispatches4 != NULL)
7649254897Serwin		dns_dispatchset_destroy(&res->dispatches4);
7650254897Serwin	if (res->dispatches6 != NULL)
7651254897Serwin		dns_dispatchset_destroy(&res->dispatches6);
7652135446Strhodes	while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) {
7653135446Strhodes		ISC_LIST_UNLINK(res->alternates, a, link);
7654135446Strhodes		if (!a->isaddress)
7655135446Strhodes			dns_name_free(&a->_u._n.name, res->mctx);
7656135446Strhodes		isc_mem_put(res->mctx, a, sizeof(*a));
7657135446Strhodes	}
7658135446Strhodes	dns_resolver_reset_algorithms(res);
7659205292Sdougb	destroy_badcache(res);
7660135446Strhodes	dns_resolver_resetmustbesecure(res);
7661135446Strhodes#if USE_ALGLOCK
7662135446Strhodes	isc_rwlock_destroy(&res->alglock);
7663135446Strhodes#endif
7664135446Strhodes#if USE_MBSLOCK
7665135446Strhodes	isc_rwlock_destroy(&res->mbslock);
7666135446Strhodes#endif
7667170222Sdougb	isc_timer_detach(&res->spillattimer);
7668135446Strhodes	res->magic = 0;
7669135446Strhodes	isc_mem_put(res->mctx, res, sizeof(*res));
7670135446Strhodes}
7671135446Strhodes
7672135446Strhodesstatic void
7673135446Strhodessend_shutdown_events(dns_resolver_t *res) {
7674135446Strhodes	isc_event_t *event, *next_event;
7675135446Strhodes	isc_task_t *etask;
7676135446Strhodes
7677135446Strhodes	/*
7678135446Strhodes	 * Caller must be holding the resolver lock.
7679135446Strhodes	 */
7680135446Strhodes
7681135446Strhodes	for (event = ISC_LIST_HEAD(res->whenshutdown);
7682135446Strhodes	     event != NULL;
7683135446Strhodes	     event = next_event) {
7684135446Strhodes		next_event = ISC_LIST_NEXT(event, ev_link);
7685135446Strhodes		ISC_LIST_UNLINK(res->whenshutdown, event, ev_link);
7686135446Strhodes		etask = event->ev_sender;
7687135446Strhodes		event->ev_sender = res;
7688135446Strhodes		isc_task_sendanddetach(&etask, &event);
7689135446Strhodes	}
7690135446Strhodes}
7691135446Strhodes
7692135446Strhodesstatic void
7693135446Strhodesempty_bucket(dns_resolver_t *res) {
7694135446Strhodes	RTRACE("empty_bucket");
7695135446Strhodes
7696135446Strhodes	LOCK(&res->lock);
7697135446Strhodes
7698135446Strhodes	INSIST(res->activebuckets > 0);
7699135446Strhodes	res->activebuckets--;
7700135446Strhodes	if (res->activebuckets == 0)
7701135446Strhodes		send_shutdown_events(res);
7702135446Strhodes
7703135446Strhodes	UNLOCK(&res->lock);
7704135446Strhodes}
7705135446Strhodes
7706170222Sdougbstatic void
7707170222Sdougbspillattimer_countdown(isc_task_t *task, isc_event_t *event) {
7708170222Sdougb	dns_resolver_t *res = event->ev_arg;
7709170222Sdougb	isc_result_t result;
7710170222Sdougb	unsigned int count;
7711170222Sdougb	isc_boolean_t logit = ISC_FALSE;
7712170222Sdougb
7713170222Sdougb	REQUIRE(VALID_RESOLVER(res));
7714170222Sdougb
7715170222Sdougb	UNUSED(task);
7716186462Sdougb
7717170222Sdougb	LOCK(&res->lock);
7718170222Sdougb	INSIST(!res->exiting);
7719170222Sdougb	if (res->spillat > res->spillatmin) {
7720170222Sdougb		res->spillat--;
7721170222Sdougb		logit = ISC_TRUE;
7722170222Sdougb	}
7723170222Sdougb	if (res->spillat <= res->spillatmin) {
7724170222Sdougb		result = isc_timer_reset(res->spillattimer,
7725170222Sdougb					 isc_timertype_inactive, NULL,
7726170222Sdougb					 NULL, ISC_TRUE);
7727170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
7728170222Sdougb	}
7729170222Sdougb	count = res->spillat;
7730170222Sdougb	UNLOCK(&res->lock);
7731170222Sdougb	if (logit)
7732170222Sdougb		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
7733170222Sdougb			      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
7734170222Sdougb			      "clients-per-query decreased to %u", count);
7735170222Sdougb
7736170222Sdougb	isc_event_free(&event);
7737170222Sdougb}
7738170222Sdougb
7739135446Strhodesisc_result_t
7740135446Strhodesdns_resolver_create(dns_view_t *view,
7741254897Serwin		    isc_taskmgr_t *taskmgr,
7742254897Serwin		    unsigned int ntasks, unsigned int ndisp,
7743135446Strhodes		    isc_socketmgr_t *socketmgr,
7744186462Sdougb		    isc_timermgr_t *timermgr,
7745135446Strhodes		    unsigned int options,
7746135446Strhodes		    dns_dispatchmgr_t *dispatchmgr,
7747135446Strhodes		    dns_dispatch_t *dispatchv4,
7748135446Strhodes		    dns_dispatch_t *dispatchv6,
7749135446Strhodes		    dns_resolver_t **resp)
7750135446Strhodes{
7751135446Strhodes	dns_resolver_t *res;
7752135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
7753135446Strhodes	unsigned int i, buckets_created = 0;
7754170222Sdougb	isc_task_t *task = NULL;
7755135446Strhodes	char name[16];
7756186462Sdougb	unsigned dispattr;
7757135446Strhodes
7758135446Strhodes	/*
7759135446Strhodes	 * Create a resolver.
7760135446Strhodes	 */
7761135446Strhodes
7762135446Strhodes	REQUIRE(DNS_VIEW_VALID(view));
7763135446Strhodes	REQUIRE(ntasks > 0);
7764254897Serwin	REQUIRE(ndisp > 0);
7765135446Strhodes	REQUIRE(resp != NULL && *resp == NULL);
7766135446Strhodes	REQUIRE(dispatchmgr != NULL);
7767135446Strhodes	REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL);
7768135446Strhodes
7769135446Strhodes	res = isc_mem_get(view->mctx, sizeof(*res));
7770135446Strhodes	if (res == NULL)
7771135446Strhodes		return (ISC_R_NOMEMORY);
7772135446Strhodes	RTRACE("create");
7773135446Strhodes	res->mctx = view->mctx;
7774135446Strhodes	res->rdclass = view->rdclass;
7775135446Strhodes	res->socketmgr = socketmgr;
7776135446Strhodes	res->timermgr = timermgr;
7777135446Strhodes	res->taskmgr = taskmgr;
7778135446Strhodes	res->dispatchmgr = dispatchmgr;
7779135446Strhodes	res->view = view;
7780135446Strhodes	res->options = options;
7781135446Strhodes	res->lame_ttl = 0;
7782135446Strhodes	ISC_LIST_INIT(res->alternates);
7783135446Strhodes	res->udpsize = RECV_BUFFER_SIZE;
7784135446Strhodes	res->algorithms = NULL;
7785205292Sdougb	res->badcache = NULL;
7786205292Sdougb	res->badcount = 0;
7787205292Sdougb	res->badhash = 0;
7788205292Sdougb	res->badsweep = 0;
7789135446Strhodes	res->mustbesecure = NULL;
7790170222Sdougb	res->spillatmin = res->spillat = 10;
7791170222Sdougb	res->spillatmax = 100;
7792170222Sdougb	res->spillattimer = NULL;
7793170222Sdougb	res->zero_no_soa_ttl = ISC_FALSE;
7794224092Sdougb	res->query_timeout = DEFAULT_QUERY_TIMEOUT;
7795275672Sdelphij	res->maxdepth = DEFAULT_RECURSION_DEPTH;
7796135446Strhodes	res->nbuckets = ntasks;
7797135446Strhodes	res->activebuckets = ntasks;
7798135446Strhodes	res->buckets = isc_mem_get(view->mctx,
7799135446Strhodes				   ntasks * sizeof(fctxbucket_t));
7800135446Strhodes	if (res->buckets == NULL) {
7801135446Strhodes		result = ISC_R_NOMEMORY;
7802135446Strhodes		goto cleanup_res;
7803135446Strhodes	}
7804135446Strhodes	for (i = 0; i < ntasks; i++) {
7805135446Strhodes		result = isc_mutex_init(&res->buckets[i].lock);
7806135446Strhodes		if (result != ISC_R_SUCCESS)
7807135446Strhodes			goto cleanup_buckets;
7808135446Strhodes		res->buckets[i].task = NULL;
7809135446Strhodes		result = isc_task_create(taskmgr, 0, &res->buckets[i].task);
7810135446Strhodes		if (result != ISC_R_SUCCESS) {
7811135446Strhodes			DESTROYLOCK(&res->buckets[i].lock);
7812135446Strhodes			goto cleanup_buckets;
7813135446Strhodes		}
7814170222Sdougb		res->buckets[i].mctx = NULL;
7815193149Sdougb		snprintf(name, sizeof(name), "res%u", i);
7816193149Sdougb#ifdef ISC_PLATFORM_USETHREADS
7817193149Sdougb		/*
7818193149Sdougb		 * Use a separate memory context for each bucket to reduce
7819193149Sdougb		 * contention among multiple threads.  Do this only when
7820193149Sdougb		 * enabling threads because it will be require more memory.
7821193149Sdougb		 */
7822170222Sdougb		result = isc_mem_create(0, 0, &res->buckets[i].mctx);
7823170222Sdougb		if (result != ISC_R_SUCCESS) {
7824170222Sdougb			isc_task_detach(&res->buckets[i].task);
7825170222Sdougb			DESTROYLOCK(&res->buckets[i].lock);
7826170222Sdougb			goto cleanup_buckets;
7827170222Sdougb		}
7828193149Sdougb		isc_mem_setname(res->buckets[i].mctx, name, NULL);
7829193149Sdougb#else
7830193149Sdougb		isc_mem_attach(view->mctx, &res->buckets[i].mctx);
7831193149Sdougb#endif
7832135446Strhodes		isc_task_setname(res->buckets[i].task, name, res);
7833135446Strhodes		ISC_LIST_INIT(res->buckets[i].fctxs);
7834135446Strhodes		res->buckets[i].exiting = ISC_FALSE;
7835135446Strhodes		buckets_created++;
7836135446Strhodes	}
7837135446Strhodes
7838254897Serwin	res->dispatches4 = NULL;
7839186462Sdougb	if (dispatchv4 != NULL) {
7840254897Serwin		dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
7841254897Serwin				       dispatchv4, &res->dispatches4, ndisp);
7842186462Sdougb		dispattr = dns_dispatch_getattributes(dispatchv4);
7843186462Sdougb		res->exclusivev4 =
7844186462Sdougb			ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0);
7845186462Sdougb	}
7846186462Sdougb
7847254897Serwin	res->dispatches6 = NULL;
7848186462Sdougb	if (dispatchv6 != NULL) {
7849254897Serwin		dns_dispatchset_create(view->mctx, socketmgr, taskmgr,
7850254897Serwin				       dispatchv6, &res->dispatches6, ndisp);
7851186462Sdougb		dispattr = dns_dispatch_getattributes(dispatchv6);
7852186462Sdougb		res->exclusivev6 =
7853186462Sdougb			ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0);
7854186462Sdougb	}
7855135446Strhodes
7856135446Strhodes	res->references = 1;
7857135446Strhodes	res->exiting = ISC_FALSE;
7858135446Strhodes	res->frozen = ISC_FALSE;
7859135446Strhodes	ISC_LIST_INIT(res->whenshutdown);
7860135446Strhodes	res->priming = ISC_FALSE;
7861135446Strhodes	res->primefetch = NULL;
7862135446Strhodes	res->nfctx = 0;
7863135446Strhodes
7864135446Strhodes	result = isc_mutex_init(&res->lock);
7865135446Strhodes	if (result != ISC_R_SUCCESS)
7866135446Strhodes		goto cleanup_dispatches;
7867135446Strhodes
7868135446Strhodes	result = isc_mutex_init(&res->nlock);
7869135446Strhodes	if (result != ISC_R_SUCCESS)
7870135446Strhodes		goto cleanup_lock;
7871135446Strhodes
7872135446Strhodes	result = isc_mutex_init(&res->primelock);
7873135446Strhodes	if (result != ISC_R_SUCCESS)
7874135446Strhodes		goto cleanup_nlock;
7875135446Strhodes
7876170222Sdougb	task = NULL;
7877170222Sdougb	result = isc_task_create(taskmgr, 0, &task);
7878170222Sdougb	if (result != ISC_R_SUCCESS)
7879193149Sdougb		goto cleanup_primelock;
7880170222Sdougb
7881170222Sdougb	result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
7882170222Sdougb				  task, spillattimer_countdown, res,
7883170222Sdougb				  &res->spillattimer);
7884170222Sdougb	isc_task_detach(&task);
7885170222Sdougb	if (result != ISC_R_SUCCESS)
7886193149Sdougb		goto cleanup_primelock;
7887170222Sdougb
7888135446Strhodes#if USE_ALGLOCK
7889135446Strhodes	result = isc_rwlock_init(&res->alglock, 0, 0);
7890135446Strhodes	if (result != ISC_R_SUCCESS)
7891170222Sdougb		goto cleanup_spillattimer;
7892135446Strhodes#endif
7893135446Strhodes#if USE_MBSLOCK
7894135446Strhodes	result = isc_rwlock_init(&res->mbslock, 0, 0);
7895135446Strhodes	if (result != ISC_R_SUCCESS)
7896135446Strhodes		goto cleanup_alglock;
7897135446Strhodes#endif
7898135446Strhodes
7899135446Strhodes	res->magic = RES_MAGIC;
7900135446Strhodes
7901135446Strhodes	*resp = res;
7902135446Strhodes
7903135446Strhodes	return (ISC_R_SUCCESS);
7904135446Strhodes
7905135446Strhodes#if USE_MBSLOCK
7906135446Strhodes cleanup_alglock:
7907135446Strhodes#if USE_ALGLOCK
7908135446Strhodes	isc_rwlock_destroy(&res->alglock);
7909135446Strhodes#endif
7910135446Strhodes#endif
7911135446Strhodes#if USE_ALGLOCK || USE_MBSLOCK
7912170222Sdougb cleanup_spillattimer:
7913170222Sdougb	isc_timer_detach(&res->spillattimer);
7914170222Sdougb#endif
7915170222Sdougb
7916135446Strhodes cleanup_primelock:
7917135446Strhodes	DESTROYLOCK(&res->primelock);
7918135446Strhodes
7919135446Strhodes cleanup_nlock:
7920135446Strhodes	DESTROYLOCK(&res->nlock);
7921135446Strhodes
7922135446Strhodes cleanup_lock:
7923135446Strhodes	DESTROYLOCK(&res->lock);
7924135446Strhodes
7925135446Strhodes cleanup_dispatches:
7926254897Serwin	if (res->dispatches6 != NULL)
7927254897Serwin		dns_dispatchset_destroy(&res->dispatches6);
7928254897Serwin	if (res->dispatches4 != NULL)
7929254897Serwin		dns_dispatchset_destroy(&res->dispatches4);
7930135446Strhodes
7931135446Strhodes cleanup_buckets:
7932135446Strhodes	for (i = 0; i < buckets_created; i++) {
7933170222Sdougb		isc_mem_detach(&res->buckets[i].mctx);
7934135446Strhodes		DESTROYLOCK(&res->buckets[i].lock);
7935135446Strhodes		isc_task_shutdown(res->buckets[i].task);
7936135446Strhodes		isc_task_detach(&res->buckets[i].task);
7937135446Strhodes	}
7938135446Strhodes	isc_mem_put(view->mctx, res->buckets,
7939135446Strhodes		    res->nbuckets * sizeof(fctxbucket_t));
7940135446Strhodes
7941135446Strhodes cleanup_res:
7942135446Strhodes	isc_mem_put(view->mctx, res, sizeof(*res));
7943135446Strhodes
7944135446Strhodes	return (result);
7945135446Strhodes}
7946135446Strhodes
7947224092Sdougb#ifdef BIND9
7948135446Strhodesstatic void
7949135446Strhodesprime_done(isc_task_t *task, isc_event_t *event) {
7950135446Strhodes	dns_resolver_t *res;
7951135446Strhodes	dns_fetchevent_t *fevent;
7952135446Strhodes	dns_fetch_t *fetch;
7953170222Sdougb	dns_db_t *db = NULL;
7954135446Strhodes
7955135446Strhodes	REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
7956135446Strhodes	fevent = (dns_fetchevent_t *)event;
7957135446Strhodes	res = event->ev_arg;
7958135446Strhodes	REQUIRE(VALID_RESOLVER(res));
7959135446Strhodes
7960135446Strhodes	UNUSED(task);
7961135446Strhodes
7962135446Strhodes	LOCK(&res->lock);
7963135446Strhodes
7964135446Strhodes	INSIST(res->priming);
7965135446Strhodes	res->priming = ISC_FALSE;
7966135446Strhodes	LOCK(&res->primelock);
7967135446Strhodes	fetch = res->primefetch;
7968135446Strhodes	res->primefetch = NULL;
7969135446Strhodes	UNLOCK(&res->primelock);
7970135446Strhodes
7971135446Strhodes	UNLOCK(&res->lock);
7972186462Sdougb
7973170222Sdougb	if (fevent->result == ISC_R_SUCCESS &&
7974170222Sdougb	    res->view->cache != NULL && res->view->hints != NULL) {
7975170222Sdougb		dns_cache_attachdb(res->view->cache, &db);
7976170222Sdougb		dns_root_checkhints(res->view, res->view->hints, db);
7977170222Sdougb		dns_db_detach(&db);
7978170222Sdougb	}
7979135446Strhodes
7980135446Strhodes	if (fevent->node != NULL)
7981135446Strhodes		dns_db_detachnode(fevent->db, &fevent->node);
7982135446Strhodes	if (fevent->db != NULL)
7983135446Strhodes		dns_db_detach(&fevent->db);
7984135446Strhodes	if (dns_rdataset_isassociated(fevent->rdataset))
7985135446Strhodes		dns_rdataset_disassociate(fevent->rdataset);
7986135446Strhodes	INSIST(fevent->sigrdataset == NULL);
7987135446Strhodes
7988135446Strhodes	isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset));
7989135446Strhodes
7990135446Strhodes	isc_event_free(&event);
7991135446Strhodes	dns_resolver_destroyfetch(&fetch);
7992135446Strhodes}
7993135446Strhodes
7994135446Strhodesvoid
7995135446Strhodesdns_resolver_prime(dns_resolver_t *res) {
7996135446Strhodes	isc_boolean_t want_priming = ISC_FALSE;
7997135446Strhodes	dns_rdataset_t *rdataset;
7998135446Strhodes	isc_result_t result;
7999135446Strhodes
8000135446Strhodes	REQUIRE(VALID_RESOLVER(res));
8001135446Strhodes	REQUIRE(res->frozen);
8002135446Strhodes
8003135446Strhodes	RTRACE("dns_resolver_prime");
8004135446Strhodes
8005135446Strhodes	LOCK(&res->lock);
8006135446Strhodes
8007135446Strhodes	if (!res->exiting && !res->priming) {
8008135446Strhodes		INSIST(res->primefetch == NULL);
8009135446Strhodes		res->priming = ISC_TRUE;
8010135446Strhodes		want_priming = ISC_TRUE;
8011135446Strhodes	}
8012135446Strhodes
8013135446Strhodes	UNLOCK(&res->lock);
8014135446Strhodes
8015135446Strhodes	if (want_priming) {
8016135446Strhodes		/*
8017135446Strhodes		 * To avoid any possible recursive locking problems, we
8018135446Strhodes		 * start the priming fetch like any other fetch, and holding
8019135446Strhodes		 * no resolver locks.  No one else will try to start it
8020135446Strhodes		 * because we're the ones who set res->priming to true.
8021135446Strhodes		 * Any other callers of dns_resolver_prime() while we're
8022135446Strhodes		 * running will see that res->priming is already true and
8023135446Strhodes		 * do nothing.
8024135446Strhodes		 */
8025135446Strhodes		RTRACE("priming");
8026135446Strhodes		rdataset = isc_mem_get(res->mctx, sizeof(*rdataset));
8027135446Strhodes		if (rdataset == NULL) {
8028135446Strhodes			LOCK(&res->lock);
8029135446Strhodes			INSIST(res->priming);
8030135446Strhodes			INSIST(res->primefetch == NULL);
8031135446Strhodes			res->priming = ISC_FALSE;
8032135446Strhodes			UNLOCK(&res->lock);
8033135446Strhodes			return;
8034135446Strhodes		}
8035135446Strhodes		dns_rdataset_init(rdataset);
8036135446Strhodes		LOCK(&res->primelock);
8037135446Strhodes		result = dns_resolver_createfetch(res, dns_rootname,
8038135446Strhodes						  dns_rdatatype_ns,
8039135446Strhodes						  NULL, NULL, NULL, 0,
8040135446Strhodes						  res->buckets[0].task,
8041135446Strhodes						  prime_done,
8042135446Strhodes						  res, rdataset, NULL,
8043135446Strhodes						  &res->primefetch);
8044135446Strhodes		UNLOCK(&res->primelock);
8045135446Strhodes		if (result != ISC_R_SUCCESS) {
8046135446Strhodes			LOCK(&res->lock);
8047135446Strhodes			INSIST(res->priming);
8048135446Strhodes			res->priming = ISC_FALSE;
8049135446Strhodes			UNLOCK(&res->lock);
8050135446Strhodes		}
8051135446Strhodes	}
8052135446Strhodes}
8053224092Sdougb#endif /* BIND9 */
8054135446Strhodes
8055135446Strhodesvoid
8056135446Strhodesdns_resolver_freeze(dns_resolver_t *res) {
8057135446Strhodes	/*
8058135446Strhodes	 * Freeze resolver.
8059135446Strhodes	 */
8060135446Strhodes
8061135446Strhodes	REQUIRE(VALID_RESOLVER(res));
8062135446Strhodes
8063135446Strhodes	res->frozen = ISC_TRUE;
8064135446Strhodes}
8065135446Strhodes
8066135446Strhodesvoid
8067135446Strhodesdns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) {
8068135446Strhodes	REQUIRE(VALID_RESOLVER(source));
8069135446Strhodes	REQUIRE(targetp != NULL && *targetp == NULL);
8070135446Strhodes
8071135446Strhodes	RRTRACE(source, "attach");
8072135446Strhodes	LOCK(&source->lock);
8073135446Strhodes	REQUIRE(!source->exiting);
8074135446Strhodes
8075135446Strhodes	INSIST(source->references > 0);
8076135446Strhodes	source->references++;
8077135446Strhodes	INSIST(source->references != 0);
8078135446Strhodes	UNLOCK(&source->lock);
8079135446Strhodes
8080135446Strhodes	*targetp = source;
8081135446Strhodes}
8082135446Strhodes
8083135446Strhodesvoid
8084135446Strhodesdns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task,
8085135446Strhodes			  isc_event_t **eventp)
8086135446Strhodes{
8087135446Strhodes	isc_task_t *clone;
8088135446Strhodes	isc_event_t *event;
8089135446Strhodes
8090135446Strhodes	REQUIRE(VALID_RESOLVER(res));
8091135446Strhodes	REQUIRE(eventp != NULL);
8092135446Strhodes
8093135446Strhodes	event = *eventp;
8094135446Strhodes	*eventp = NULL;
8095135446Strhodes
8096135446Strhodes	LOCK(&res->lock);
8097135446Strhodes
8098135446Strhodes	if (res->exiting && res->activebuckets == 0) {
8099135446Strhodes		/*
8100135446Strhodes		 * We're already shutdown.  Send the event.
8101135446Strhodes		 */
8102135446Strhodes		event->ev_sender = res;
8103135446Strhodes		isc_task_send(task, &event);
8104135446Strhodes	} else {
8105135446Strhodes		clone = NULL;
8106135446Strhodes		isc_task_attach(task, &clone);
8107135446Strhodes		event->ev_sender = clone;
8108135446Strhodes		ISC_LIST_APPEND(res->whenshutdown, event, ev_link);
8109135446Strhodes	}
8110135446Strhodes
8111135446Strhodes	UNLOCK(&res->lock);
8112135446Strhodes}
8113135446Strhodes
8114135446Strhodesvoid
8115135446Strhodesdns_resolver_shutdown(dns_resolver_t *res) {
8116135446Strhodes	unsigned int i;
8117135446Strhodes	fetchctx_t *fctx;
8118170222Sdougb	isc_result_t result;
8119135446Strhodes
8120135446Strhodes	REQUIRE(VALID_RESOLVER(res));
8121135446Strhodes
8122135446Strhodes	RTRACE("shutdown");
8123135446Strhodes
8124135446Strhodes	LOCK(&res->lock);
8125135446Strhodes
8126135446Strhodes	if (!res->exiting) {
8127135446Strhodes		RTRACE("exiting");
8128135446Strhodes		res->exiting = ISC_TRUE;
8129135446Strhodes
8130135446Strhodes		for (i = 0; i < res->nbuckets; i++) {
8131135446Strhodes			LOCK(&res->buckets[i].lock);
8132135446Strhodes			for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs);
8133135446Strhodes			     fctx != NULL;
8134135446Strhodes			     fctx = ISC_LIST_NEXT(fctx, link))
8135135446Strhodes				fctx_shutdown(fctx);
8136254897Serwin			if (res->dispatches4 != NULL && !res->exclusivev4) {
8137254897Serwin				dns_dispatchset_cancelall(res->dispatches4,
8138254897Serwin							  res->buckets[i].task);
8139135446Strhodes			}
8140254897Serwin			if (res->dispatches6 != NULL && !res->exclusivev6) {
8141254897Serwin				dns_dispatchset_cancelall(res->dispatches6,
8142254897Serwin							  res->buckets[i].task);
8143135446Strhodes			}
8144135446Strhodes			res->buckets[i].exiting = ISC_TRUE;
8145135446Strhodes			if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) {
8146135446Strhodes				INSIST(res->activebuckets > 0);
8147135446Strhodes				res->activebuckets--;
8148135446Strhodes			}
8149135446Strhodes			UNLOCK(&res->buckets[i].lock);
8150135446Strhodes		}
8151135446Strhodes		if (res->activebuckets == 0)
8152135446Strhodes			send_shutdown_events(res);
8153170222Sdougb		result = isc_timer_reset(res->spillattimer,
8154170222Sdougb					 isc_timertype_inactive, NULL,
8155170222Sdougb					 NULL, ISC_TRUE);
8156170222Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
8157135446Strhodes	}
8158135446Strhodes
8159135446Strhodes	UNLOCK(&res->lock);
8160135446Strhodes}
8161135446Strhodes
8162135446Strhodesvoid
8163135446Strhodesdns_resolver_detach(dns_resolver_t **resp) {
8164135446Strhodes	dns_resolver_t *res;
8165135446Strhodes	isc_boolean_t need_destroy = ISC_FALSE;
8166135446Strhodes
8167135446Strhodes	REQUIRE(resp != NULL);
8168135446Strhodes	res = *resp;
8169135446Strhodes	REQUIRE(VALID_RESOLVER(res));
8170135446Strhodes
8171135446Strhodes	RTRACE("detach");
8172135446Strhodes
8173135446Strhodes	LOCK(&res->lock);
8174135446Strhodes
8175135446Strhodes	INSIST(res->references > 0);
8176135446Strhodes	res->references--;
8177135446Strhodes	if (res->references == 0) {
8178135446Strhodes		INSIST(res->exiting && res->activebuckets == 0);
8179135446Strhodes		need_destroy = ISC_TRUE;
8180135446Strhodes	}
8181135446Strhodes
8182135446Strhodes	UNLOCK(&res->lock);
8183135446Strhodes
8184135446Strhodes	if (need_destroy)
8185135446Strhodes		destroy(res);
8186135446Strhodes
8187135446Strhodes	*resp = NULL;
8188135446Strhodes}
8189135446Strhodes
8190135446Strhodesstatic inline isc_boolean_t
8191135446Strhodesfctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
8192135446Strhodes	   unsigned int options)
8193135446Strhodes{
8194218384Sdougb	/*
8195218384Sdougb	 * Don't match fetch contexts that are shutting down.
8196218384Sdougb	 */
8197218384Sdougb	if (fctx->cloned || fctx->state == fetchstate_done ||
8198218384Sdougb	    ISC_LIST_EMPTY(fctx->events))
8199218384Sdougb		return (ISC_FALSE);
8200218384Sdougb
8201135446Strhodes	if (fctx->type != type || fctx->options != options)
8202135446Strhodes		return (ISC_FALSE);
8203135446Strhodes	return (dns_name_equal(&fctx->name, name));
8204135446Strhodes}
8205135446Strhodes
8206135446Strhodesstatic inline void
8207135446Strhodeslog_fetch(dns_name_t *name, dns_rdatatype_t type) {
8208135446Strhodes	char namebuf[DNS_NAME_FORMATSIZE];
8209135446Strhodes	char typebuf[DNS_RDATATYPE_FORMATSIZE];
8210135446Strhodes	int level = ISC_LOG_DEBUG(1);
8211135446Strhodes
8212135446Strhodes	if (! isc_log_wouldlog(dns_lctx, level))
8213135446Strhodes		return;
8214135446Strhodes
8215135446Strhodes	dns_name_format(name, namebuf, sizeof(namebuf));
8216135446Strhodes	dns_rdatatype_format(type, typebuf, sizeof(typebuf));
8217135446Strhodes
8218135446Strhodes	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
8219135446Strhodes		      DNS_LOGMODULE_RESOLVER, level,
8220135446Strhodes		      "createfetch: %s %s", namebuf, typebuf);
8221135446Strhodes}
8222135446Strhodes
8223135446Strhodesisc_result_t
8224135446Strhodesdns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
8225135446Strhodes			 dns_rdatatype_t type,
8226135446Strhodes			 dns_name_t *domain, dns_rdataset_t *nameservers,
8227135446Strhodes			 dns_forwarders_t *forwarders,
8228135446Strhodes			 unsigned int options, isc_task_t *task,
8229135446Strhodes			 isc_taskaction_t action, void *arg,
8230135446Strhodes			 dns_rdataset_t *rdataset,
8231135446Strhodes			 dns_rdataset_t *sigrdataset,
8232135446Strhodes			 dns_fetch_t **fetchp)
8233135446Strhodes{
8234275672Sdelphij	return (dns_resolver_createfetch3(res, name, type, domain,
8235170222Sdougb					  nameservers, forwarders, NULL, 0,
8236275672Sdelphij					  options, 0, task, action, arg,
8237170222Sdougb					  rdataset, sigrdataset, fetchp));
8238170222Sdougb}
8239170222Sdougb
8240170222Sdougbisc_result_t
8241170222Sdougbdns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
8242170222Sdougb			  dns_rdatatype_t type,
8243170222Sdougb			  dns_name_t *domain, dns_rdataset_t *nameservers,
8244170222Sdougb			  dns_forwarders_t *forwarders,
8245170222Sdougb			  isc_sockaddr_t *client, dns_messageid_t id,
8246170222Sdougb			  unsigned int options, isc_task_t *task,
8247170222Sdougb			  isc_taskaction_t action, void *arg,
8248170222Sdougb			  dns_rdataset_t *rdataset,
8249170222Sdougb			  dns_rdataset_t *sigrdataset,
8250170222Sdougb			  dns_fetch_t **fetchp)
8251170222Sdougb{
8252275672Sdelphij	return (dns_resolver_createfetch3(res, name, type, domain,
8253275672Sdelphij					  nameservers, forwarders, client, id,
8254275672Sdelphij					  options, 0, task, action, arg,
8255275672Sdelphij					  rdataset, sigrdataset, fetchp));
8256275672Sdelphij}
8257275672Sdelphij
8258275672Sdelphijisc_result_t
8259275672Sdelphijdns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name,
8260275672Sdelphij			  dns_rdatatype_t type,
8261275672Sdelphij			  dns_name_t *domain, dns_rdataset_t *nameservers,
8262275672Sdelphij			  dns_forwarders_t *forwarders,
8263275672Sdelphij			  isc_sockaddr_t *client, dns_messageid_t id,
8264275672Sdelphij			  unsigned int options, unsigned int depth,
8265275672Sdelphij			  isc_task_t *task,
8266275672Sdelphij			  isc_taskaction_t action, void *arg,
8267275672Sdelphij			  dns_rdataset_t *rdataset,
8268275672Sdelphij			  dns_rdataset_t *sigrdataset,
8269275672Sdelphij			  dns_fetch_t **fetchp)
8270275672Sdelphij{
8271135446Strhodes	dns_fetch_t *fetch;
8272135446Strhodes	fetchctx_t *fctx = NULL;
8273170222Sdougb	isc_result_t result = ISC_R_SUCCESS;
8274135446Strhodes	unsigned int bucketnum;
8275135446Strhodes	isc_boolean_t new_fctx = ISC_FALSE;
8276135446Strhodes	isc_event_t *event;
8277170222Sdougb	unsigned int count = 0;
8278170222Sdougb	unsigned int spillat;
8279186462Sdougb	unsigned int spillatmin;
8280236374Sdougb	isc_boolean_t destroy = ISC_FALSE;
8281135446Strhodes
8282135446Strhodes	UNUSED(forwarders);
8283135446Strhodes
8284135446Strhodes	REQUIRE(VALID_RESOLVER(res));
8285135446Strhodes	REQUIRE(res->frozen);
8286135446Strhodes	/* XXXRTH  Check for meta type */
8287135446Strhodes	if (domain != NULL) {
8288135446Strhodes		REQUIRE(DNS_RDATASET_VALID(nameservers));
8289135446Strhodes		REQUIRE(nameservers->type == dns_rdatatype_ns);
8290135446Strhodes	} else
8291135446Strhodes		REQUIRE(nameservers == NULL);
8292135446Strhodes	REQUIRE(forwarders == NULL);
8293135446Strhodes	REQUIRE(!dns_rdataset_isassociated(rdataset));
8294135446Strhodes	REQUIRE(sigrdataset == NULL ||
8295135446Strhodes		!dns_rdataset_isassociated(sigrdataset));
8296135446Strhodes	REQUIRE(fetchp != NULL && *fetchp == NULL);
8297135446Strhodes
8298135446Strhodes	log_fetch(name, type);
8299135446Strhodes
8300135446Strhodes	/*
8301135446Strhodes	 * XXXRTH  use a mempool?
8302135446Strhodes	 */
8303135446Strhodes	fetch = isc_mem_get(res->mctx, sizeof(*fetch));
8304135446Strhodes	if (fetch == NULL)
8305135446Strhodes		return (ISC_R_NOMEMORY);
8306135446Strhodes
8307170222Sdougb	bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets;
8308135446Strhodes
8309170222Sdougb	LOCK(&res->lock);
8310170222Sdougb	spillat = res->spillat;
8311186462Sdougb	spillatmin = res->spillatmin;
8312170222Sdougb	UNLOCK(&res->lock);
8313135446Strhodes	LOCK(&res->buckets[bucketnum].lock);
8314135446Strhodes
8315135446Strhodes	if (res->buckets[bucketnum].exiting) {
8316135446Strhodes		result = ISC_R_SHUTTINGDOWN;
8317135446Strhodes		goto unlock;
8318135446Strhodes	}
8319135446Strhodes
8320135446Strhodes	if ((options & DNS_FETCHOPT_UNSHARED) == 0) {
8321135446Strhodes		for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs);
8322135446Strhodes		     fctx != NULL;
8323135446Strhodes		     fctx = ISC_LIST_NEXT(fctx, link)) {
8324135446Strhodes			if (fctx_match(fctx, name, type, options))
8325135446Strhodes				break;
8326135446Strhodes		}
8327135446Strhodes	}
8328186462Sdougb
8329170222Sdougb	/*
8330170222Sdougb	 * Is this a duplicate?
8331170222Sdougb	 */
8332170222Sdougb	if (fctx != NULL && client != NULL) {
8333170222Sdougb		dns_fetchevent_t *fevent;
8334170222Sdougb		for (fevent = ISC_LIST_HEAD(fctx->events);
8335170222Sdougb		     fevent != NULL;
8336170222Sdougb		     fevent = ISC_LIST_NEXT(fevent, ev_link)) {
8337170222Sdougb			if (fevent->client != NULL && fevent->id == id &&
8338170222Sdougb			    isc_sockaddr_equal(fevent->client, client)) {
8339170222Sdougb				result = DNS_R_DUPLICATE;
8340170222Sdougb				goto unlock;
8341170222Sdougb			}
8342170222Sdougb			count++;
8343170222Sdougb		}
8344170222Sdougb	}
8345186462Sdougb	if (count >= spillatmin && spillatmin != 0) {
8346186462Sdougb		INSIST(fctx != NULL);
8347170222Sdougb		if (count >= spillat)
8348170222Sdougb			fctx->spilled = ISC_TRUE;
8349170222Sdougb		if (fctx->spilled) {
8350170222Sdougb			result = DNS_R_DROP;
8351170222Sdougb			goto unlock;
8352170222Sdougb		}
8353170222Sdougb	}
8354135446Strhodes
8355218384Sdougb	if (fctx == NULL) {
8356135446Strhodes		result = fctx_create(res, name, type, domain, nameservers,
8357275672Sdelphij				     options, bucketnum, depth, &fctx);
8358135446Strhodes		if (result != ISC_R_SUCCESS)
8359135446Strhodes			goto unlock;
8360135446Strhodes		new_fctx = ISC_TRUE;
8361275672Sdelphij	} else if (fctx->depth > depth)
8362275672Sdelphij		fctx->depth = depth;
8363135446Strhodes
8364170222Sdougb	result = fctx_join(fctx, task, client, id, action, arg,
8365135446Strhodes			   rdataset, sigrdataset, fetch);
8366135446Strhodes	if (new_fctx) {
8367135446Strhodes		if (result == ISC_R_SUCCESS) {
8368135446Strhodes			/*
8369135446Strhodes			 * Launch this fctx.
8370135446Strhodes			 */
8371135446Strhodes			event = &fctx->control_event;
8372135446Strhodes			ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
8373135446Strhodes				       DNS_EVENT_FETCHCONTROL,
8374135446Strhodes				       fctx_start, fctx, NULL,
8375135446Strhodes				       NULL, NULL);
8376135446Strhodes			isc_task_send(res->buckets[bucketnum].task, &event);
8377135446Strhodes		} else {
8378135446Strhodes			/*
8379236374Sdougb			 * We don't care about the result of fctx_unlink()
8380135446Strhodes			 * since we know we're not exiting.
8381135446Strhodes			 */
8382236374Sdougb			(void)fctx_unlink(fctx);
8383236374Sdougb			destroy = ISC_TRUE;
8384135446Strhodes		}
8385135446Strhodes	}
8386135446Strhodes
8387135446Strhodes unlock:
8388135446Strhodes	UNLOCK(&res->buckets[bucketnum].lock);
8389135446Strhodes
8390236374Sdougb	if (destroy)
8391236374Sdougb		fctx_destroy(fctx);
8392236374Sdougb
8393135446Strhodes	if (result == ISC_R_SUCCESS) {
8394135446Strhodes		FTRACE("created");
8395135446Strhodes		*fetchp = fetch;
8396135446Strhodes	} else
8397135446Strhodes		isc_mem_put(res->mctx, fetch, sizeof(*fetch));
8398135446Strhodes
8399135446Strhodes	return (result);
8400135446Strhodes}
8401135446Strhodes
8402135446Strhodesvoid
8403135446Strhodesdns_resolver_cancelfetch(dns_fetch_t *fetch) {
8404135446Strhodes	fetchctx_t *fctx;
8405135446Strhodes	dns_resolver_t *res;
8406135446Strhodes	dns_fetchevent_t *event, *next_event;
8407135446Strhodes	isc_task_t *etask;
8408135446Strhodes
8409135446Strhodes	REQUIRE(DNS_FETCH_VALID(fetch));
8410135446Strhodes	fctx = fetch->private;
8411135446Strhodes	REQUIRE(VALID_FCTX(fctx));
8412135446Strhodes	res = fctx->res;
8413135446Strhodes
8414135446Strhodes	FTRACE("cancelfetch");
8415135446Strhodes
8416135446Strhodes	LOCK(&res->buckets[fctx->bucketnum].lock);
8417135446Strhodes
8418135446Strhodes	/*
8419135446Strhodes	 * Find the completion event for this fetch (as opposed
8420135446Strhodes	 * to those for other fetches that have joined the same
8421135446Strhodes	 * fctx) and send it with result = ISC_R_CANCELED.
8422135446Strhodes	 */
8423135446Strhodes	event = NULL;
8424135446Strhodes	if (fctx->state != fetchstate_done) {
8425135446Strhodes		for (event = ISC_LIST_HEAD(fctx->events);
8426135446Strhodes		     event != NULL;
8427135446Strhodes		     event = next_event) {
8428135446Strhodes			next_event = ISC_LIST_NEXT(event, ev_link);
8429135446Strhodes			if (event->fetch == fetch) {
8430135446Strhodes				ISC_LIST_UNLINK(fctx->events, event, ev_link);
8431135446Strhodes				break;
8432135446Strhodes			}
8433135446Strhodes		}
8434135446Strhodes	}
8435135446Strhodes	if (event != NULL) {
8436135446Strhodes		etask = event->ev_sender;
8437135446Strhodes		event->ev_sender = fctx;
8438135446Strhodes		event->result = ISC_R_CANCELED;
8439135446Strhodes		isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event));
8440135446Strhodes	}
8441135446Strhodes	/*
8442135446Strhodes	 * The fctx continues running even if no fetches remain;
8443135446Strhodes	 * the answer is still cached.
8444135446Strhodes	 */
8445135446Strhodes
8446135446Strhodes	UNLOCK(&res->buckets[fctx->bucketnum].lock);
8447135446Strhodes}
8448135446Strhodes
8449135446Strhodesvoid
8450135446Strhodesdns_resolver_destroyfetch(dns_fetch_t **fetchp) {
8451135446Strhodes	dns_fetch_t *fetch;
8452135446Strhodes	dns_resolver_t *res;
8453135446Strhodes	dns_fetchevent_t *event, *next_event;
8454135446Strhodes	fetchctx_t *fctx;
8455135446Strhodes	unsigned int bucketnum;
8456214586Sdougb	isc_boolean_t bucket_empty;
8457135446Strhodes
8458135446Strhodes	REQUIRE(fetchp != NULL);
8459135446Strhodes	fetch = *fetchp;
8460135446Strhodes	REQUIRE(DNS_FETCH_VALID(fetch));
8461135446Strhodes	fctx = fetch->private;
8462135446Strhodes	REQUIRE(VALID_FCTX(fctx));
8463135446Strhodes	res = fctx->res;
8464135446Strhodes
8465135446Strhodes	FTRACE("destroyfetch");
8466135446Strhodes
8467135446Strhodes	bucketnum = fctx->bucketnum;
8468135446Strhodes	LOCK(&res->buckets[bucketnum].lock);
8469135446Strhodes
8470135446Strhodes	/*
8471135446Strhodes	 * Sanity check: the caller should have gotten its event before
8472135446Strhodes	 * trying to destroy the fetch.
8473135446Strhodes	 */
8474135446Strhodes	event = NULL;
8475135446Strhodes	if (fctx->state != fetchstate_done) {
8476135446Strhodes		for (event = ISC_LIST_HEAD(fctx->events);
8477135446Strhodes		     event != NULL;
8478135446Strhodes		     event = next_event) {
8479135446Strhodes			next_event = ISC_LIST_NEXT(event, ev_link);
8480135446Strhodes			RUNTIME_CHECK(event->fetch != fetch);
8481135446Strhodes		}
8482135446Strhodes	}
8483135446Strhodes
8484214586Sdougb	bucket_empty = fctx_decreference(fctx);
8485135446Strhodes
8486135446Strhodes	UNLOCK(&res->buckets[bucketnum].lock);
8487135446Strhodes
8488135446Strhodes	isc_mem_put(res->mctx, fetch, sizeof(*fetch));
8489135446Strhodes	*fetchp = NULL;
8490135446Strhodes
8491135446Strhodes	if (bucket_empty)
8492135446Strhodes		empty_bucket(res);
8493135446Strhodes}
8494135446Strhodes
8495193149Sdougbvoid
8496193149Sdougbdns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx,
8497193149Sdougb		      isc_logcategory_t *category, isc_logmodule_t *module,
8498193149Sdougb		      int level, isc_boolean_t duplicateok)
8499193149Sdougb{
8500193149Sdougb	fetchctx_t *fctx;
8501193149Sdougb	dns_resolver_t *res;
8502193149Sdougb	char domainbuf[DNS_NAME_FORMATSIZE];
8503193149Sdougb
8504193149Sdougb	REQUIRE(DNS_FETCH_VALID(fetch));
8505193149Sdougb	fctx = fetch->private;
8506193149Sdougb	REQUIRE(VALID_FCTX(fctx));
8507193149Sdougb	res = fctx->res;
8508193149Sdougb
8509193149Sdougb	LOCK(&res->buckets[fctx->bucketnum].lock);
8510193149Sdougb
8511193149Sdougb	INSIST(fctx->exitline >= 0);
8512193149Sdougb	if (!fctx->logged || duplicateok) {
8513193149Sdougb		dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
8514193149Sdougb		isc_log_write(lctx, category, module, level,
8515193149Sdougb			      "fetch completed at %s:%d for %s in "
8516193149Sdougb			      "%" ISC_PRINT_QUADFORMAT "u."
8517193149Sdougb			      "%06" ISC_PRINT_QUADFORMAT "u: %s/%s "
8518193149Sdougb			      "[domain:%s,referral:%u,restart:%u,qrysent:%u,"
8519193149Sdougb			      "timeout:%u,lame:%u,neterr:%u,badresp:%u,"
8520193149Sdougb			      "adberr:%u,findfail:%u,valfail:%u]",
8521193149Sdougb			      __FILE__, fctx->exitline, fctx->info,
8522245163Serwin			      fctx->duration / US_PER_SEC,
8523245163Serwin			      fctx->duration % US_PER_SEC,
8524193149Sdougb			      isc_result_totext(fctx->result),
8525193149Sdougb			      isc_result_totext(fctx->vresult), domainbuf,
8526193149Sdougb			      fctx->referrals, fctx->restarts,
8527193149Sdougb			      fctx->querysent, fctx->timeouts, fctx->lamecount,
8528193149Sdougb			      fctx->neterr, fctx->badresp, fctx->adberr,
8529193149Sdougb			      fctx->findfail, fctx->valfail);
8530193149Sdougb		fctx->logged = ISC_TRUE;
8531193149Sdougb	}
8532193149Sdougb
8533193149Sdougb	UNLOCK(&res->buckets[fctx->bucketnum].lock);
8534193149Sdougb}
8535193149Sdougb
8536135446Strhodesdns_dispatchmgr_t *
8537135446Strhodesdns_resolver_dispatchmgr(dns_resolver_t *resolver) {
8538135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8539135446Strhodes	return (resolver->dispatchmgr);
8540135446Strhodes}
8541135446Strhodes
8542135446Strhodesdns_dispatch_t *
8543135446Strhodesdns_resolver_dispatchv4(dns_resolver_t *resolver) {
8544135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8545254897Serwin	return (dns_dispatchset_get(resolver->dispatches4));
8546135446Strhodes}
8547135446Strhodes
8548135446Strhodesdns_dispatch_t *
8549135446Strhodesdns_resolver_dispatchv6(dns_resolver_t *resolver) {
8550135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8551254897Serwin	return (dns_dispatchset_get(resolver->dispatches6));
8552135446Strhodes}
8553135446Strhodes
8554135446Strhodesisc_socketmgr_t *
8555135446Strhodesdns_resolver_socketmgr(dns_resolver_t *resolver) {
8556135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8557135446Strhodes	return (resolver->socketmgr);
8558135446Strhodes}
8559135446Strhodes
8560135446Strhodesisc_taskmgr_t *
8561135446Strhodesdns_resolver_taskmgr(dns_resolver_t *resolver) {
8562135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8563135446Strhodes	return (resolver->taskmgr);
8564135446Strhodes}
8565135446Strhodes
8566135446Strhodesisc_uint32_t
8567135446Strhodesdns_resolver_getlamettl(dns_resolver_t *resolver) {
8568135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8569135446Strhodes	return (resolver->lame_ttl);
8570135446Strhodes}
8571135446Strhodes
8572135446Strhodesvoid
8573135446Strhodesdns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) {
8574135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8575135446Strhodes	resolver->lame_ttl = lame_ttl;
8576135446Strhodes}
8577135446Strhodes
8578135446Strhodesunsigned int
8579135446Strhodesdns_resolver_nrunning(dns_resolver_t *resolver) {
8580135446Strhodes	unsigned int n;
8581135446Strhodes	LOCK(&resolver->nlock);
8582135446Strhodes	n = resolver->nfctx;
8583135446Strhodes	UNLOCK(&resolver->nlock);
8584135446Strhodes	return (n);
8585135446Strhodes}
8586135446Strhodes
8587135446Strhodesisc_result_t
8588135446Strhodesdns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt,
8589135446Strhodes			  dns_name_t *name, in_port_t port) {
8590135446Strhodes	alternate_t *a;
8591135446Strhodes	isc_result_t result;
8592135446Strhodes
8593135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8594135446Strhodes	REQUIRE(!resolver->frozen);
8595135446Strhodes	REQUIRE((alt == NULL) ^ (name == NULL));
8596135446Strhodes
8597135446Strhodes	a = isc_mem_get(resolver->mctx, sizeof(*a));
8598135446Strhodes	if (a == NULL)
8599135446Strhodes		return (ISC_R_NOMEMORY);
8600135446Strhodes	if (alt != NULL) {
8601135446Strhodes		a->isaddress = ISC_TRUE;
8602135446Strhodes		a->_u.addr = *alt;
8603135446Strhodes	} else {
8604135446Strhodes		a->isaddress = ISC_FALSE;
8605135446Strhodes		a->_u._n.port = port;
8606135446Strhodes		dns_name_init(&a->_u._n.name, NULL);
8607135446Strhodes		result = dns_name_dup(name, resolver->mctx, &a->_u._n.name);
8608135446Strhodes		if (result != ISC_R_SUCCESS) {
8609135446Strhodes			isc_mem_put(resolver->mctx, a, sizeof(*a));
8610135446Strhodes			return (result);
8611135446Strhodes		}
8612135446Strhodes	}
8613135446Strhodes	ISC_LINK_INIT(a, link);
8614135446Strhodes	ISC_LIST_APPEND(resolver->alternates, a, link);
8615135446Strhodes
8616135446Strhodes	return (ISC_R_SUCCESS);
8617135446Strhodes}
8618135446Strhodes
8619135446Strhodesvoid
8620135446Strhodesdns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) {
8621135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8622135446Strhodes	resolver->udpsize = udpsize;
8623135446Strhodes}
8624135446Strhodes
8625135446Strhodesisc_uint16_t
8626135446Strhodesdns_resolver_getudpsize(dns_resolver_t *resolver) {
8627135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8628135446Strhodes	return (resolver->udpsize);
8629135446Strhodes}
8630135446Strhodes
8631205292Sdougbvoid
8632205292Sdougbdns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name) {
8633205292Sdougb	unsigned int i;
8634205292Sdougb	dns_badcache_t *bad, *prev, *next;
8635205292Sdougb
8636205292Sdougb	REQUIRE(VALID_RESOLVER(resolver));
8637205292Sdougb
8638205292Sdougb	LOCK(&resolver->lock);
8639205292Sdougb	if (resolver->badcache == NULL)
8640205292Sdougb		goto unlock;
8641205292Sdougb
8642205292Sdougb	if (name != NULL) {
8643205292Sdougb		isc_time_t now;
8644205292Sdougb		isc_result_t result;
8645205292Sdougb		result = isc_time_now(&now);
8646205292Sdougb		if (result != ISC_R_SUCCESS)
8647205292Sdougb			isc_time_settoepoch(&now);
8648205292Sdougb		i = dns_name_hash(name, ISC_FALSE) % resolver->badhash;
8649205292Sdougb		prev = NULL;
8650205292Sdougb		for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
8651205292Sdougb			int n;
8652205292Sdougb			next = bad->next;
8653205292Sdougb			n = isc_time_compare(&bad->expire, &now);
8654205292Sdougb			if (n < 0 || dns_name_equal(name, &bad->name)) {
8655205292Sdougb				if (prev == NULL)
8656205292Sdougb					resolver->badcache[i] = bad->next;
8657205292Sdougb				else
8658205292Sdougb					prev->next = bad->next;
8659205292Sdougb				isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
8660205292Sdougb					    bad->name.length);
8661205292Sdougb				resolver->badcount--;
8662205292Sdougb			} else
8663205292Sdougb				prev = bad;
8664205292Sdougb		}
8665205292Sdougb	} else
8666205292Sdougb		destroy_badcache(resolver);
8667205292Sdougb
8668205292Sdougb unlock:
8669205292Sdougb	UNLOCK(&resolver->lock);
8670205292Sdougb
8671205292Sdougb}
8672205292Sdougb
8673135446Strhodesstatic void
8674205292Sdougbresizehash(dns_resolver_t *resolver, isc_time_t *now, isc_boolean_t grow) {
8675205292Sdougb	unsigned int newsize;
8676205292Sdougb	dns_badcache_t **new, *bad, *next;
8677205292Sdougb	unsigned int i;
8678205292Sdougb
8679205292Sdougb	if (grow)
8680205292Sdougb		newsize = resolver->badhash * 2 + 1;
8681205292Sdougb	else
8682205292Sdougb		newsize = (resolver->badhash - 1) / 2;
8683205292Sdougb
8684205292Sdougb	new = isc_mem_get(resolver->mctx,
8685205292Sdougb			  sizeof(*resolver->badcache) * newsize);
8686205292Sdougb	if (new == NULL)
8687205292Sdougb		return;
8688205292Sdougb	memset(new, 0, sizeof(*resolver->badcache) * newsize);
8689205292Sdougb	for (i = 0; i < resolver->badhash; i++) {
8690205292Sdougb		for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
8691205292Sdougb			next = bad->next;
8692205292Sdougb			if (isc_time_compare(&bad->expire, now) < 0) {
8693205292Sdougb				isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
8694205292Sdougb					    bad->name.length);
8695205292Sdougb				resolver->badcount--;
8696205292Sdougb			} else {
8697205292Sdougb				bad->next = new[bad->hashval % newsize];
8698205292Sdougb				new[bad->hashval % newsize] = bad;
8699205292Sdougb			}
8700205292Sdougb		}
8701205292Sdougb	}
8702205292Sdougb	isc_mem_put(resolver->mctx, resolver->badcache,
8703205292Sdougb		    sizeof(*resolver->badcache) * resolver->badhash);
8704205292Sdougb	resolver->badhash = newsize;
8705205292Sdougb	resolver->badcache = new;
8706205292Sdougb}
8707205292Sdougb
8708205292Sdougbvoid
8709205292Sdougbdns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
8710205292Sdougb			 dns_rdatatype_t type, isc_time_t *expire)
8711205292Sdougb{
8712205292Sdougb	isc_time_t now;
8713205292Sdougb	isc_result_t result = ISC_R_SUCCESS;
8714205292Sdougb	unsigned int i, hashval;
8715205292Sdougb	dns_badcache_t *bad, *prev, *next;
8716205292Sdougb
8717205292Sdougb	REQUIRE(VALID_RESOLVER(resolver));
8718205292Sdougb
8719205292Sdougb	LOCK(&resolver->lock);
8720205292Sdougb	if (resolver->badcache == NULL) {
8721205292Sdougb		resolver->badcache = isc_mem_get(resolver->mctx,
8722205292Sdougb						 sizeof(*resolver->badcache) *
8723205292Sdougb						 DNS_BADCACHE_SIZE);
8724225361Sdougb		if (resolver->badcache == NULL)
8725205292Sdougb			goto cleanup;
8726205292Sdougb		resolver->badhash = DNS_BADCACHE_SIZE;
8727205292Sdougb		memset(resolver->badcache, 0, sizeof(*resolver->badcache) *
8728205292Sdougb		       resolver->badhash);
8729205292Sdougb	}
8730205292Sdougb
8731205292Sdougb	result = isc_time_now(&now);
8732205292Sdougb	if (result != ISC_R_SUCCESS)
8733205292Sdougb		isc_time_settoepoch(&now);
8734205292Sdougb	hashval = dns_name_hash(name, ISC_FALSE);
8735205292Sdougb	i = hashval % resolver->badhash;
8736205292Sdougb	prev = NULL;
8737205292Sdougb	for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
8738205292Sdougb		next = bad->next;
8739205292Sdougb		if (bad->type == type && dns_name_equal(name, &bad->name))
8740205292Sdougb			break;
8741205292Sdougb		if (isc_time_compare(&bad->expire, &now) < 0) {
8742205292Sdougb			if (prev == NULL)
8743205292Sdougb				resolver->badcache[i] = bad->next;
8744205292Sdougb			else
8745205292Sdougb				prev->next = bad->next;
8746205292Sdougb			isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
8747205292Sdougb				    bad->name.length);
8748205292Sdougb			resolver->badcount--;
8749205292Sdougb		} else
8750205292Sdougb			prev = bad;
8751205292Sdougb	}
8752205292Sdougb	if (bad == NULL) {
8753205292Sdougb		isc_buffer_t buffer;
8754205292Sdougb		bad = isc_mem_get(resolver->mctx, sizeof(*bad) + name->length);
8755225361Sdougb		if (bad == NULL)
8756205292Sdougb			goto cleanup;
8757205292Sdougb		bad->type = type;
8758205292Sdougb		bad->hashval = hashval;
8759238756Sdougb		bad->expire = *expire;
8760205292Sdougb		isc_buffer_init(&buffer, bad + 1, name->length);
8761205292Sdougb		dns_name_init(&bad->name, NULL);
8762205292Sdougb		dns_name_copy(name, &bad->name, &buffer);
8763205292Sdougb		bad->next = resolver->badcache[i];
8764205292Sdougb		resolver->badcache[i] = bad;
8765205292Sdougb		resolver->badcount++;
8766205292Sdougb		if (resolver->badcount > resolver->badhash * 8)
8767205292Sdougb			resizehash(resolver, &now, ISC_TRUE);
8768205292Sdougb		if (resolver->badcount < resolver->badhash * 2 &&
8769205292Sdougb		    resolver->badhash > DNS_BADCACHE_SIZE)
8770205292Sdougb			resizehash(resolver, &now, ISC_FALSE);
8771238756Sdougb	} else
8772238756Sdougb		bad->expire = *expire;
8773205292Sdougb cleanup:
8774205292Sdougb	UNLOCK(&resolver->lock);
8775205292Sdougb}
8776205292Sdougb
8777205292Sdougbisc_boolean_t
8778205292Sdougbdns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name,
8779205292Sdougb			 dns_rdatatype_t type, isc_time_t *now)
8780205292Sdougb{
8781205292Sdougb	dns_badcache_t *bad, *prev, *next;
8782205292Sdougb	isc_boolean_t answer = ISC_FALSE;
8783205292Sdougb	unsigned int i;
8784205292Sdougb
8785205292Sdougb	REQUIRE(VALID_RESOLVER(resolver));
8786205292Sdougb
8787205292Sdougb	LOCK(&resolver->lock);
8788205292Sdougb	if (resolver->badcache == NULL)
8789205292Sdougb		goto unlock;
8790205292Sdougb
8791205292Sdougb	i = dns_name_hash(name, ISC_FALSE) % resolver->badhash;
8792205292Sdougb	prev = NULL;
8793205292Sdougb	for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
8794205292Sdougb		next = bad->next;
8795205292Sdougb		/*
8796205292Sdougb		 * Search the hash list. Clean out expired records as we go.
8797205292Sdougb		 */
8798205292Sdougb		if (isc_time_compare(&bad->expire, now) < 0) {
8799205292Sdougb			if (prev != NULL)
8800205292Sdougb				prev->next = bad->next;
8801205292Sdougb			else
8802205292Sdougb				resolver->badcache[i] = bad->next;
8803205292Sdougb			isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
8804205292Sdougb				    bad->name.length);
8805205292Sdougb			resolver->badcount--;
8806205292Sdougb			continue;
8807205292Sdougb		}
8808205292Sdougb		if (bad->type == type && dns_name_equal(name, &bad->name)) {
8809205292Sdougb			answer = ISC_TRUE;
8810205292Sdougb			break;
8811205292Sdougb		}
8812205292Sdougb		prev = bad;
8813205292Sdougb	}
8814205292Sdougb
8815205292Sdougb	/*
8816205292Sdougb	 * Slow sweep to clean out stale records.
8817205292Sdougb	 */
8818205292Sdougb	i = resolver->badsweep++ % resolver->badhash;
8819205292Sdougb	bad = resolver->badcache[i];
8820205292Sdougb	if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) {
8821205292Sdougb		resolver->badcache[i] = bad->next;
8822205292Sdougb		isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
8823205292Sdougb			    bad->name.length);
8824205292Sdougb		resolver->badcount--;
8825205292Sdougb	}
8826205292Sdougb
8827205292Sdougb unlock:
8828205292Sdougb	UNLOCK(&resolver->lock);
8829205292Sdougb	return (answer);
8830205292Sdougb}
8831205292Sdougb
8832205292Sdougbvoid
8833205292Sdougbdns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) {
8834205292Sdougb	char namebuf[DNS_NAME_FORMATSIZE];
8835205292Sdougb	char typebuf[DNS_RDATATYPE_FORMATSIZE];
8836205292Sdougb	dns_badcache_t *bad, *next, *prev;
8837205292Sdougb	isc_time_t now;
8838205292Sdougb	unsigned int i;
8839205292Sdougb	isc_uint64_t t;
8840205292Sdougb
8841205292Sdougb	LOCK(&resolver->lock);
8842205292Sdougb	fprintf(fp, ";\n; Bad cache\n;\n");
8843205292Sdougb
8844205292Sdougb	if (resolver->badcache == NULL)
8845205292Sdougb		goto unlock;
8846205292Sdougb
8847205292Sdougb	TIME_NOW(&now);
8848205292Sdougb	for (i = 0; i < resolver->badhash; i++) {
8849205292Sdougb		prev = NULL;
8850205292Sdougb		for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
8851205292Sdougb			next = bad->next;
8852205292Sdougb			if (isc_time_compare(&bad->expire, &now) < 0) {
8853205292Sdougb				if (prev != NULL)
8854205292Sdougb					prev->next = bad->next;
8855205292Sdougb				else
8856205292Sdougb					resolver->badcache[i] = bad->next;
8857205292Sdougb				isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
8858205292Sdougb					    bad->name.length);
8859205292Sdougb				resolver->badcount--;
8860205292Sdougb				continue;
8861205292Sdougb			}
8862205292Sdougb			prev = bad;
8863205292Sdougb			dns_name_format(&bad->name, namebuf, sizeof(namebuf));
8864205292Sdougb			dns_rdatatype_format(bad->type, typebuf,
8865205292Sdougb					     sizeof(typebuf));
8866205292Sdougb			t = isc_time_microdiff(&bad->expire, &now);
8867205292Sdougb			t /= 1000;
8868205292Sdougb			fprintf(fp, "; %s/%s [ttl "
8869205292Sdougb				"%" ISC_PLATFORM_QUADFORMAT "u]\n",
8870205292Sdougb				namebuf, typebuf, t);
8871205292Sdougb		}
8872205292Sdougb	}
8873205292Sdougb
8874205292Sdougb unlock:
8875205292Sdougb	UNLOCK(&resolver->lock);
8876205292Sdougb}
8877205292Sdougb
8878205292Sdougbstatic void
8879135446Strhodesfree_algorithm(void *node, void *arg) {
8880135446Strhodes	unsigned char *algorithms = node;
8881135446Strhodes	isc_mem_t *mctx = arg;
8882135446Strhodes
8883135446Strhodes	isc_mem_put(mctx, algorithms, *algorithms);
8884135446Strhodes}
8885186462Sdougb
8886135446Strhodesvoid
8887135446Strhodesdns_resolver_reset_algorithms(dns_resolver_t *resolver) {
8888135446Strhodes
8889135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8890135446Strhodes
8891135446Strhodes#if USE_ALGLOCK
8892135446Strhodes	RWLOCK(&resolver->alglock, isc_rwlocktype_write);
8893135446Strhodes#endif
8894135446Strhodes	if (resolver->algorithms != NULL)
8895135446Strhodes		dns_rbt_destroy(&resolver->algorithms);
8896135446Strhodes#if USE_ALGLOCK
8897135446Strhodes	RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
8898135446Strhodes#endif
8899135446Strhodes}
8900135446Strhodes
8901135446Strhodesisc_result_t
8902135446Strhodesdns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
8903135446Strhodes			       unsigned int alg)
8904135446Strhodes{
8905135446Strhodes	unsigned int len, mask;
8906135446Strhodes	unsigned char *new;
8907135446Strhodes	unsigned char *algorithms;
8908135446Strhodes	isc_result_t result;
8909135446Strhodes	dns_rbtnode_t *node = NULL;
8910135446Strhodes
8911135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8912135446Strhodes	if (alg > 255)
8913135446Strhodes		return (ISC_R_RANGE);
8914135446Strhodes
8915135446Strhodes#if USE_ALGLOCK
8916135446Strhodes	RWLOCK(&resolver->alglock, isc_rwlocktype_write);
8917135446Strhodes#endif
8918135446Strhodes	if (resolver->algorithms == NULL) {
8919135446Strhodes		result = dns_rbt_create(resolver->mctx, free_algorithm,
8920135446Strhodes					resolver->mctx, &resolver->algorithms);
8921135446Strhodes		if (result != ISC_R_SUCCESS)
8922135446Strhodes			goto cleanup;
8923135446Strhodes	}
8924135446Strhodes
8925135446Strhodes	len = alg/8 + 2;
8926135446Strhodes	mask = 1 << (alg%8);
8927135446Strhodes
8928135446Strhodes	result = dns_rbt_addnode(resolver->algorithms, name, &node);
8929186462Sdougb
8930135446Strhodes	if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
8931135446Strhodes		algorithms = node->data;
8932135446Strhodes		if (algorithms == NULL || len > *algorithms) {
8933135446Strhodes			new = isc_mem_get(resolver->mctx, len);
8934135446Strhodes			if (new == NULL) {
8935135446Strhodes				result = ISC_R_NOMEMORY;
8936135446Strhodes				goto cleanup;
8937135446Strhodes			}
8938135446Strhodes			memset(new, 0, len);
8939135446Strhodes			if (algorithms != NULL)
8940262706Serwin				memmove(new, algorithms, *algorithms);
8941135446Strhodes			new[len-1] |= mask;
8942135446Strhodes			*new = len;
8943135446Strhodes			node->data = new;
8944135446Strhodes			if (algorithms != NULL)
8945186462Sdougb				isc_mem_put(resolver->mctx, algorithms,
8946135446Strhodes					    *algorithms);
8947135446Strhodes		} else
8948135446Strhodes			algorithms[len-1] |= mask;
8949135446Strhodes	}
8950135446Strhodes	result = ISC_R_SUCCESS;
8951135446Strhodes cleanup:
8952135446Strhodes#if USE_ALGLOCK
8953135446Strhodes	RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
8954135446Strhodes#endif
8955135446Strhodes	return (result);
8956135446Strhodes}
8957135446Strhodes
8958135446Strhodesisc_boolean_t
8959135446Strhodesdns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
8960135446Strhodes				 unsigned int alg)
8961135446Strhodes{
8962135446Strhodes	unsigned int len, mask;
8963135446Strhodes	unsigned char *algorithms;
8964135446Strhodes	void *data = NULL;
8965135446Strhodes	isc_result_t result;
8966135446Strhodes	isc_boolean_t found = ISC_FALSE;
8967135446Strhodes
8968135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
8969135446Strhodes
8970287410Sdelphij	/*
8971287410Sdelphij	 * DH is unsupported for DNSKEYs, see RFC 4034 sec. A.1.
8972287410Sdelphij	 */
8973287410Sdelphij	if ((alg == DST_ALG_DH) || (alg == DST_ALG_INDIRECT))
8974287410Sdelphij		return (ISC_FALSE);
8975287410Sdelphij
8976135446Strhodes#if USE_ALGLOCK
8977135446Strhodes	RWLOCK(&resolver->alglock, isc_rwlocktype_read);
8978135446Strhodes#endif
8979135446Strhodes	if (resolver->algorithms == NULL)
8980135446Strhodes		goto unlock;
8981135446Strhodes	result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data);
8982135446Strhodes	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
8983135446Strhodes		len = alg/8 + 2;
8984135446Strhodes		mask = 1 << (alg%8);
8985135446Strhodes		algorithms = data;
8986135446Strhodes		if (len <= *algorithms && (algorithms[len-1] & mask) != 0)
8987135446Strhodes			found = ISC_TRUE;
8988135446Strhodes	}
8989135446Strhodes unlock:
8990135446Strhodes#if USE_ALGLOCK
8991135446Strhodes	RWUNLOCK(&resolver->alglock, isc_rwlocktype_read);
8992135446Strhodes#endif
8993135446Strhodes	if (found)
8994135446Strhodes		return (ISC_FALSE);
8995287410Sdelphij
8996135446Strhodes	return (dst_algorithm_supported(alg));
8997135446Strhodes}
8998135446Strhodes
8999170222Sdougbisc_boolean_t
9000170222Sdougbdns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) {
9001170222Sdougb
9002170222Sdougb	UNUSED(resolver);
9003170222Sdougb	return (dns_ds_digest_supported(digest));
9004170222Sdougb}
9005170222Sdougb
9006135446Strhodesvoid
9007135446Strhodesdns_resolver_resetmustbesecure(dns_resolver_t *resolver) {
9008135446Strhodes
9009135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
9010135446Strhodes
9011135446Strhodes#if USE_MBSLOCK
9012135446Strhodes	RWLOCK(&resolver->mbslock, isc_rwlocktype_write);
9013135446Strhodes#endif
9014135446Strhodes	if (resolver->mustbesecure != NULL)
9015135446Strhodes		dns_rbt_destroy(&resolver->mustbesecure);
9016135446Strhodes#if USE_MBSLOCK
9017135446Strhodes	RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write);
9018135446Strhodes#endif
9019135446Strhodes}
9020186462Sdougb
9021135446Strhodesstatic isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE;
9022135446Strhodes
9023135446Strhodesisc_result_t
9024135446Strhodesdns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name,
9025174187Sdougb			     isc_boolean_t value)
9026135446Strhodes{
9027135446Strhodes	isc_result_t result;
9028135446Strhodes
9029135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
9030135446Strhodes
9031135446Strhodes#if USE_MBSLOCK
9032135446Strhodes	RWLOCK(&resolver->mbslock, isc_rwlocktype_write);
9033135446Strhodes#endif
9034135446Strhodes	if (resolver->mustbesecure == NULL) {
9035135446Strhodes		result = dns_rbt_create(resolver->mctx, NULL, NULL,
9036135446Strhodes					&resolver->mustbesecure);
9037135446Strhodes		if (result != ISC_R_SUCCESS)
9038135446Strhodes			goto cleanup;
9039135446Strhodes	}
9040186462Sdougb	result = dns_rbt_addname(resolver->mustbesecure, name,
9041135446Strhodes				 value ? &yes : &no);
9042135446Strhodes cleanup:
9043135446Strhodes#if USE_MBSLOCK
9044135446Strhodes	RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write);
9045135446Strhodes#endif
9046135446Strhodes	return (result);
9047135446Strhodes}
9048135446Strhodes
9049135446Strhodesisc_boolean_t
9050135446Strhodesdns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) {
9051135446Strhodes	void *data = NULL;
9052135446Strhodes	isc_boolean_t value = ISC_FALSE;
9053135446Strhodes	isc_result_t result;
9054135446Strhodes
9055135446Strhodes	REQUIRE(VALID_RESOLVER(resolver));
9056135446Strhodes
9057135446Strhodes#if USE_MBSLOCK
9058135446Strhodes	RWLOCK(&resolver->mbslock, isc_rwlocktype_read);
9059135446Strhodes#endif
9060135446Strhodes	if (resolver->mustbesecure == NULL)
9061135446Strhodes		goto unlock;
9062135446Strhodes	result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data);
9063135446Strhodes	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
9064135446Strhodes		value = *(isc_boolean_t*)data;
9065135446Strhodes unlock:
9066135446Strhodes#if USE_MBSLOCK
9067135446Strhodes	RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read);
9068135446Strhodes#endif
9069135446Strhodes	return (value);
9070135446Strhodes}
9071170222Sdougb
9072170222Sdougbvoid
9073170222Sdougbdns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur,
9074170222Sdougb				isc_uint32_t *min, isc_uint32_t *max)
9075170222Sdougb{
9076170222Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9077170222Sdougb
9078170222Sdougb	LOCK(&resolver->lock);
9079170222Sdougb	if (cur != NULL)
9080170222Sdougb		*cur = resolver->spillat;
9081170222Sdougb	if (min != NULL)
9082170222Sdougb		*min = resolver->spillatmin;
9083170222Sdougb	if (max != NULL)
9084170222Sdougb		*max = resolver->spillatmax;
9085170222Sdougb	UNLOCK(&resolver->lock);
9086170222Sdougb}
9087170222Sdougb
9088170222Sdougbvoid
9089170222Sdougbdns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min,
9090170222Sdougb				isc_uint32_t max)
9091170222Sdougb{
9092170222Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9093170222Sdougb
9094170222Sdougb	LOCK(&resolver->lock);
9095170222Sdougb	resolver->spillatmin = resolver->spillat = min;
9096170222Sdougb	resolver->spillatmax = max;
9097170222Sdougb	UNLOCK(&resolver->lock);
9098170222Sdougb}
9099170222Sdougb
9100170222Sdougbisc_boolean_t
9101170222Sdougbdns_resolver_getzeronosoattl(dns_resolver_t *resolver) {
9102170222Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9103170222Sdougb
9104170222Sdougb	return (resolver->zero_no_soa_ttl);
9105170222Sdougb}
9106170222Sdougb
9107170222Sdougbvoid
9108170222Sdougbdns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) {
9109170222Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9110170222Sdougb
9111170222Sdougb	resolver->zero_no_soa_ttl = state;
9112170222Sdougb}
9113193149Sdougb
9114193149Sdougbunsigned int
9115193149Sdougbdns_resolver_getoptions(dns_resolver_t *resolver) {
9116193149Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9117193149Sdougb
9118193149Sdougb	return (resolver->options);
9119193149Sdougb}
9120224092Sdougb
9121224092Sdougbunsigned int
9122224092Sdougbdns_resolver_gettimeout(dns_resolver_t *resolver) {
9123224092Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9124224092Sdougb
9125224092Sdougb	return (resolver->query_timeout);
9126224092Sdougb}
9127224092Sdougb
9128224092Sdougbvoid
9129224092Sdougbdns_resolver_settimeout(dns_resolver_t *resolver, unsigned int seconds) {
9130224092Sdougb	REQUIRE(VALID_RESOLVER(resolver));
9131224092Sdougb
9132224092Sdougb	if (seconds == 0)
9133224092Sdougb		seconds = DEFAULT_QUERY_TIMEOUT;
9134224092Sdougb	if (seconds > MAXIMUM_QUERY_TIMEOUT)
9135224092Sdougb		seconds = MAXIMUM_QUERY_TIMEOUT;
9136245163Serwin	if (seconds < MINIMUM_QUERY_TIMEOUT)
9137245163Serwin		seconds =  MINIMUM_QUERY_TIMEOUT;
9138224092Sdougb
9139224092Sdougb	resolver->query_timeout = seconds;
9140224092Sdougb}
9141275672Sdelphij
9142275672Sdelphijvoid
9143275672Sdelphijdns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) {
9144275672Sdelphij	REQUIRE(VALID_RESOLVER(resolver));
9145275672Sdelphij	resolver->maxdepth = maxdepth;
9146275672Sdelphij}
9147275672Sdelphij
9148275672Sdelphijunsigned int
9149275672Sdelphijdns_resolver_getmaxdepth(dns_resolver_t *resolver) {
9150275672Sdelphij	REQUIRE(VALID_RESOLVER(resolver));
9151275672Sdelphij	return (resolver->maxdepth);
9152275672Sdelphij}
9153