resolver.c 107 KB
Newer Older
<
Michael Graff's avatar
Michael Graff committed
1
/*
Bob Halley's avatar
Bob Halley committed
2
 * Copyright (C) 1999, 2000  Internet Software Consortium.
Michael Graff's avatar
Michael Graff committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
Bob Halley's avatar
Bob Halley committed
17

18
19
#include <config.h>

Bob Halley's avatar
Bob Halley committed
20
#include <isc/assertions.h>
21
#include <isc/boolean.h>
Bob Halley's avatar
Bob Halley committed
22
#include <isc/error.h>
Bob Halley's avatar
Bob Halley committed
23
#include <isc/result.h>
24
#include <isc/types.h>
Bob Halley's avatar
Bob Halley committed
25
26
27
28
29
#include <isc/timer.h>
#include <isc/mutex.h>
#include <isc/event.h>
#include <isc/task.h>
#include <isc/stdtime.h>
Michael Graff's avatar
Michael Graff committed
30
#include <isc/util.h>
31
#include <isc/netaddr.h>
Bob Halley's avatar
Bob Halley committed
32
33

#include <dns/types.h>
Bob Halley's avatar
Bob Halley committed
34
#include <dns/adb.h>
Bob Halley's avatar
Bob Halley committed
35
36
#include <dns/result.h>
#include <dns/name.h>
Bob Halley's avatar
Bob Halley committed
37
#include <dns/db.h>
Bob Halley's avatar
Bob Halley committed
38
#include <dns/events.h>
Bob Halley's avatar
Bob Halley committed
39
#include <dns/keytable.h>
Bob Halley's avatar
Bob Halley committed
40
#include <dns/message.h>
41
#include <dns/ncache.h>
Bob Halley's avatar
Bob Halley committed
42
43
#include <dns/dispatch.h>
#include <dns/resolver.h>
44
#include <dns/rdata.h>
Bob Halley's avatar
Bob Halley committed
45
#include <dns/rdatalist.h>
Bob Halley's avatar
Bob Halley committed
46
#include <dns/rdataset.h>
47
#include <dns/rdatatype.h>
48
#include <dns/tsig.h>
49
#include <dns/validator.h>
50
#include <dns/view.h>
51
#include <dns/log.h>
Bob Halley's avatar
Bob Halley committed
52

53
#include <dst/dst.h>
54
#include <dns/peer.h>
Bob Halley's avatar
Bob Halley committed
55
56
57

#define DNS_RESOLVER_TRACE
#ifdef DNS_RESOLVER_TRACE
58
59
60
#define RTRACE(m)	isc_log_write(dns_lctx, \
				      DNS_LOGCATEGORY_RESOLVER, \
				      DNS_LOGMODULE_RESOLVER, \
61
				      ISC_LOG_DEBUG(3), \
62
63
64
65
				      "res %p: %s", res, (m))
#define RRTRACE(r, m)	isc_log_write(dns_lctx, \
				      DNS_LOGCATEGORY_RESOLVER, \
				      DNS_LOGMODULE_RESOLVER, \
66
				      ISC_LOG_DEBUG(3), \
67
68
69
70
				      "res %p: %s", (r), (m))
#define FCTXTRACE(m)	isc_log_write(dns_lctx, \
				      DNS_LOGCATEGORY_RESOLVER, \
				      DNS_LOGMODULE_RESOLVER, \
71
				      ISC_LOG_DEBUG(3), \
72
73
74
75
				      "fctx %p: %s", fctx, (m))
#define FTRACE(m)	isc_log_write(dns_lctx, \
				      DNS_LOGCATEGORY_RESOLVER, \
				      DNS_LOGMODULE_RESOLVER, \
76
				      ISC_LOG_DEBUG(3), \
77
78
79
80
81
				      "fetch %p (fctx %p): %s", \
				      fetch, fetch->private, (m))
#define QTRACE(m)	isc_log_write(dns_lctx, \
				      DNS_LOGCATEGORY_RESOLVER, \
				      DNS_LOGMODULE_RESOLVER, \
82
				      ISC_LOG_DEBUG(3), \
83
84
				      "resquery %p (fctx %p): %s", \
				      query, query->fctx, (m))
Bob Halley's avatar
Bob Halley committed
85
86
87
88
89
#else
#define RTRACE(m)
#define RRTRACE(r, m)
#define FCTXTRACE(m)
#define FTRACE(m)
Bob Halley's avatar
Bob Halley committed
90
#define QTRACE(m)
Bob Halley's avatar
Bob Halley committed
91
92
#endif

Bob Halley's avatar
Bob Halley committed
93
94
95
/*
 * Maximum EDNS0 input packet size.
 */
Bob Halley's avatar
Bob Halley committed
96
#define SEND_BUFFER_SIZE		2048		/* XXXRTH  Constant. */
97

Bob Halley's avatar
Bob Halley committed
98
99
typedef struct fetchctx fetchctx_t;

Bob Halley's avatar
Bob Halley committed
100
typedef struct query {
Bob Halley's avatar
Bob Halley committed
101
	/* Locked by task event serialization. */
Bob Halley's avatar
Bob Halley committed
102
103
104
	unsigned int			magic;
	fetchctx_t *			fctx;
	dns_dispatch_t *		dispatch;
Bob Halley's avatar
Bob Halley committed
105
106
	dns_adbaddrinfo_t *		addrinfo;
	isc_time_t			start;
Bob Halley's avatar
Bob Halley committed
107
	dns_messageid_t			id;
Bob Halley's avatar
Bob Halley committed
108
	dns_dispentry_t *		dispentry;
Bob Halley's avatar
Bob Halley committed
109
	ISC_LINK(struct query)		link;
Bob Halley's avatar
Bob Halley committed
110
	isc_buffer_t			buffer;
111
	dns_rdata_any_tsig_t		*tsig;
112
	dns_tsigkey_t			*tsigkey;
Bob Halley's avatar
Bob Halley committed
113
	unsigned int			options;
114
	unsigned int			attributes;
Bob Halley's avatar
Bob Halley committed
115
	unsigned char			data[512];
Bob Halley's avatar
Bob Halley committed
116
117
} resquery_t;

Bob Halley's avatar
Bob Halley committed
118
119
120
121
#define QUERY_MAGIC			0x51212121U	/* Q!!! */
#define VALID_QUERY(query)		((query) != NULL && \
					 (query)->magic == QUERY_MAGIC)

122
123
124
125
126
127
128
129
#define RESQUERY_ATTR_CONNECTING	0x01
#define RESQUERY_ATTR_CANCELED		0x02

#define RESQUERY_CONNECTING(q)		(((q)->attributes & \
					  RESQUERY_ATTR_CONNECTING) != 0)
#define RESQUERY_CANCELED(q)		(((q)->attributes & \
					  RESQUERY_ATTR_CANCELED) != 0)

Bob Halley's avatar
Bob Halley committed
130
typedef enum {
Bob Halley's avatar
Bob Halley committed
131
	fetchstate_init = 0,		/* Start event has not run yet. */
Bob Halley's avatar
Bob Halley committed
132
	fetchstate_active,
Bob Halley's avatar
Bob Halley committed
133
	fetchstate_done			/* FETCHDONE events posted. */
Bob Halley's avatar
Bob Halley committed
134
135
} fetchstate;

Bob Halley's avatar
Bob Halley committed
136
137
struct fetchctx {
	/* Not locked. */
Bob Halley's avatar
Bob Halley committed
138
139
140
	unsigned int			magic;
	dns_resolver_t *		res;
	dns_name_t			name;
Bob Halley's avatar
Bob Halley committed
141
	dns_rdatatype_t			type;
Bob Halley's avatar
Bob Halley committed
142
	unsigned int			options;
Bob Halley's avatar
Bob Halley committed
143
	unsigned int			bucketnum;
Bob Halley's avatar
Bob Halley committed
144
	/* Locked by appropriate bucket lock. */
Bob Halley's avatar
Bob Halley committed
145
	fetchstate			state;
Bob Halley's avatar
Bob Halley committed
146
	isc_boolean_t			want_shutdown;
Bob Halley's avatar
Bob Halley committed
147
	unsigned int			references;
Bob Halley's avatar
lint    
Bob Halley committed
148
	isc_event_t			control_event;
Bob Halley's avatar
Bob Halley committed
149
150
	ISC_LINK(struct fetchctx)	link;
	ISC_LIST(dns_fetchevent_t)	events;
Bob Halley's avatar
Bob Halley committed
151
	/* Locked by task event serialization. */
Bob Halley's avatar
Bob Halley committed
152
153
	dns_name_t			domain;
	dns_rdataset_t			nameservers;
Bob Halley's avatar
Bob Halley committed
154
	unsigned int			attributes;
Bob Halley's avatar
Bob Halley committed
155
156
157
	isc_timer_t *			timer;
	isc_time_t			expires;
	isc_interval_t			interval;
Bob Halley's avatar
Bob Halley committed
158
159
	dns_message_t *			qmessage;
	dns_message_t *			rmessage;
Bob Halley's avatar
Bob Halley committed
160
	ISC_LIST(resquery_t)		queries;
Bob Halley's avatar
Bob Halley committed
161
	dns_adbfindlist_t		finds;
Bob Halley's avatar
Bob Halley committed
162
	dns_adbfind_t *			find;
163
164
	dns_adbaddrinfolist_t		forwaddrs;
	isc_sockaddrlist_t		forwarders;
Bob Halley's avatar
Bob Halley committed
165
	isc_sockaddrlist_t		bad;
Bob Halley's avatar
Bob Halley committed
166
167
168
	/*
	 * # of events we're waiting for.
	 */
Bob Halley's avatar
Bob Halley committed
169
	unsigned int			pending;
170
	unsigned int			validating;
Bob Halley's avatar
Bob Halley committed
171
	unsigned int			restarts;
Bob Halley's avatar
Bob Halley committed
172
};
Bob Halley's avatar
Bob Halley committed
173
174
175
176
177

#define FCTX_MAGIC			0x46212121U	/* F!!! */
#define VALID_FCTX(fctx)		((fctx) != NULL && \
					 (fctx)->magic == FCTX_MAGIC)

Bob Halley's avatar
Bob Halley committed
178
179
#define FCTX_ATTR_HAVEANSWER		0x01
#define FCTX_ATTR_GLUING		0x02
Bob Halley's avatar
Bob Halley committed
180
#define FCTX_ATTR_ADDRWAIT		0x04
Bob Halley's avatar
Bob Halley committed
181
#define FCTX_ATTR_SHUTTINGDOWN		0x08
Bob Halley's avatar
Bob Halley committed
182
183
#define FCTX_ATTR_WANTCACHE		0x10
#define FCTX_ATTR_WANTNCACHE		0x20
184
#define FCTX_ATTR_NEEDEDNS0		0x40
Bob Halley's avatar
Bob Halley committed
185
186
187
188
189

#define HAVE_ANSWER(f)		(((f)->attributes & FCTX_ATTR_HAVEANSWER) != \
				 0)
#define GLUING(f)		(((f)->attributes & FCTX_ATTR_GLUING) != \
				 0)
Bob Halley's avatar
Bob Halley committed
190
191
#define ADDRWAIT(f)		(((f)->attributes & FCTX_ATTR_ADDRWAIT) != \
				 0)
Bob Halley's avatar
Bob Halley committed
192
193
#define SHUTTINGDOWN(f)		(((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \
 				 != 0)
Bob Halley's avatar
Bob Halley committed
194
195
#define WANTCACHE(f)		(((f)->attributes & FCTX_ATTR_WANTCACHE) != 0)
#define WANTNCACHE(f)		(((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)
196
#define NEEDEDNS0(f)		(((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0)
Bob Halley's avatar
Bob Halley committed
197

Bob Halley's avatar
Bob Halley committed
198
199
200
201
202
203
204
205
206
207
208
209
210
struct dns_fetch {
	unsigned int			magic;
	void *				private;
};

#define DNS_FETCH_MAGIC			0x46746368U	/* Ftch */
#define DNS_FETCH_VALID(fetch)		((fetch) != NULL && \
					 (fetch)->magic == DNS_FETCH_MAGIC)

typedef struct fctxbucket {
	isc_task_t *			task;
	isc_mutex_t			lock;
	ISC_LIST(fetchctx_t)		fctxs;
Bob Halley's avatar
Bob Halley committed
211
	isc_boolean_t			exiting;
Bob Halley's avatar
Bob Halley committed
212
213
} fctxbucket_t;

Bob Halley's avatar
Bob Halley committed
214
struct dns_resolver {
Bob Halley's avatar
Bob Halley committed
215
	/* Unlocked. */
Bob Halley's avatar
Bob Halley committed
216
217
218
219
	unsigned int			magic;
	isc_mem_t *			mctx;
	isc_mutex_t			lock;
	dns_rdataclass_t		rdclass;
Bob Halley's avatar
Bob Halley committed
220
	isc_socketmgr_t *		socketmgr;
Bob Halley's avatar
Bob Halley committed
221
	isc_timermgr_t *		timermgr;
222
	dns_view_t *			view;
223
224
225
	isc_boolean_t			frozen;
	isc_sockaddrlist_t		forwarders;
	dns_fwdpolicy_t			fwdpolicy;
226
227
228
229
230
	unsigned int			options;
	isc_socket_t *			udpsocketv4;
	isc_socket_t *			udpsocketv6;
	dns_dispatch_t *		dispatchv4;
	dns_dispatch_t *		dispatchv6;
Bob Halley's avatar
Bob Halley committed
231
232
	unsigned int			nbuckets;
	fctxbucket_t *			buckets;
233
234
235
236
237
	/* Locked by lock. */
	unsigned int			references;
	isc_boolean_t			exiting;
	isc_eventlist_t			whenshutdown;
	unsigned int			activebuckets;
Bob Halley's avatar
Bob Halley committed
238
239
	isc_boolean_t			priming;
	dns_fetch_t *			primefetch;
Bob Halley's avatar
Bob Halley committed
240
241
242
243
244
245
};

#define RES_MAGIC			0x52657321U	/* Res! */
#define VALID_RESOLVER(res)		((res) != NULL && \
					 (res)->magic == RES_MAGIC)

246
247
248
249
250
251
252
253
254
255
/*
 * Private addrinfo flags.  These must not conflict with DNS_FETCHOPT_NOEDNS0,
 * which we also use as an addrinfo flag.
 */
#define FCTX_ADDRINFO_MARK		0x0001
#define FCTX_ADDRINFO_FORWARDER		0x1000
#define UNMARKED(a)			(((a)->flags & FCTX_ADDRINFO_MARK) \
					 == 0)
#define ISFORWARDER(a)			(((a)->flags & \
					 FCTX_ADDRINFO_FORWARDER) != 0)
Bob Halley's avatar
Bob Halley committed
256
257

static void destroy(dns_resolver_t *res);
Bob Halley's avatar
Bob Halley committed
258
static void empty_bucket(dns_resolver_t *res);
Bob Halley's avatar
Bob Halley committed
259
static isc_result_t resquery_send(resquery_t *query);
Bob Halley's avatar
Bob Halley committed
260
static void resquery_response(isc_task_t *task, isc_event_t *event);
Bob Halley's avatar
Bob Halley committed
261
static void resquery_connected(isc_task_t *task, isc_event_t *event);
Bob Halley's avatar
Bob Halley committed
262
static void fctx_try(fetchctx_t *fctx);
Bob Halley's avatar
Bob Halley committed
263
static isc_boolean_t fctx_destroy(fetchctx_t *fctx);
Bob Halley's avatar
Bob Halley committed
264

Bob Halley's avatar
Bob Halley committed
265
static inline isc_result_t
Bob Halley's avatar
Bob Halley committed
266
fctx_starttimer(fetchctx_t *fctx) {
267
268
269
	/*
	 * Start the lifetime timer for fctx.
	 */
Bob Halley's avatar
Bob Halley committed
270
271
272
273
274
	return (isc_timer_reset(fctx->timer, isc_timertype_once,
				&fctx->expires, NULL,
				ISC_FALSE));
}

Bob Halley's avatar
Bob Halley committed
275
276
static inline void
fctx_stoptimer(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
277
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
278
279
280
281
282
283
284

	/*
	 * We don't return a result if resetting the timer to inactive fails
	 * since there's nothing to be done about it.  Resetting to inactive
	 * should never fail anyway, since the code as currently written
	 * cannot fail in that case.
	 */
Bob Halley's avatar
Bob Halley committed
285
	result = isc_timer_reset(fctx->timer, isc_timertype_inactive,
Bob Halley's avatar
Bob Halley committed
286
				  NULL, NULL, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
287
	if (result != ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
288
289
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_timer_reset(): %s",
Bob Halley's avatar
Bob Halley committed
290
				 isc_result_totext(result));
Bob Halley's avatar
Bob Halley committed
291
292
293
	}
}

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312

static inline isc_result_t
fctx_startidletimer(fetchctx_t *fctx) {
	/*
	 * Start the idle timer for fctx.  The lifetime timer continues
	 * to be in effect.
	 */
	return (isc_timer_reset(fctx->timer, isc_timertype_once,
				&fctx->expires, &fctx->interval,
				ISC_FALSE));
}

/*
 * Stopping the idle timer is equivalent to calling fctx_starttimer(), but
 * we use fctx_stopidletimer for readability in the code below.
 */
#define fctx_stopidletimer	fctx_starttimer


313
314
315
316
317
318
319
320
321
322
323
324
325
static inline void
resquery_destroy(resquery_t **queryp) {
	resquery_t *query;
	
	REQUIRE(queryp != NULL);
	query = *queryp;
	REQUIRE(!ISC_LINK_LINKED(query, link));

	query->magic = 0;
	isc_mem_put(query->fctx->res->mctx, query, sizeof *query);
	*queryp = NULL;
}

Bob Halley's avatar
Bob Halley committed
326
static void
Bob Halley's avatar
Bob Halley committed
327
fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
328
		 isc_time_t *finish, isc_boolean_t no_response)
Bob Halley's avatar
Bob Halley committed
329
{
Bob Halley's avatar
Bob Halley committed
330
331
	fetchctx_t *fctx;
	resquery_t *query;
Bob Halley's avatar
Bob Halley committed
332
333
	unsigned int rtt;
	unsigned int factor;
334
	isc_socket_t *socket;
Bob Halley's avatar
Bob Halley committed
335

Bob Halley's avatar
Bob Halley committed
336
337
338
	query = *queryp;
	fctx = query->fctx;

Bob Halley's avatar
Bob Halley committed
339
340
	FCTXTRACE("cancelquery");

341
342
343
344
	REQUIRE(!RESQUERY_CANCELED(query));

	query->attributes |= RESQUERY_ATTR_CANCELED;

Bob Halley's avatar
Bob Halley committed
345
	/*
346
	 * Should we update the RTT?
Bob Halley's avatar
Bob Halley committed
347
	 */
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
	if (finish != NULL || no_response) {
		if (finish != NULL) {
			/*
			 * We have both the start and finish times for this
			 * packet, so we can compute a real RTT.
			 */
			rtt = (unsigned int)isc_time_microdiff(finish,
							       &query->start);
			factor = DNS_ADB_RTTADJDEFAULT;
		} else {
			/*
			 * We don't have an RTT for this query.  Maybe the
			 * packet was lost, or maybe this server is very
			 * slow.  We don't know.  Increase the RTT.
			 */
			INSIST(no_response);
			rtt = query->addrinfo->srtt +
				(100000 * fctx->restarts);
			if (rtt > 10000000)
				rtt = 10000000;
			/*
			 * Replace the current RTT with our value.
			 */
			factor = DNS_ADB_RTTADJREPLACE;
		}
		dns_adb_adjustsrtt(fctx->res->view->adb, query->addrinfo, rtt,
				   factor);
Bob Halley's avatar
Bob Halley committed
375
376
	}

Bob Halley's avatar
Bob Halley committed
377
378
379
	if (query->dispentry != NULL)
		dns_dispatch_removeresponse(query->dispatch, &query->dispentry,
					    deventp);
Bob Halley's avatar
Bob Halley committed
380
	ISC_LIST_UNLINK(fctx->queries, query, link);
381
	if (query->tsig != NULL) {
Bob Halley's avatar
Bob Halley committed
382
		dns_rdata_freestruct(query->tsig);
383
384
385
		isc_mem_put(query->fctx->res->mctx, query->tsig,
			    sizeof(*query->tsig));
	}
386
387
388
389
390
391
392
	if (RESQUERY_CONNECTING(query)) {
		/*
		 * Cancel the connect.
		 */
		socket = dns_dispatch_getsocket(query->dispatch);
		isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_CONNECT);
	}
Bob Halley's avatar
Bob Halley committed
393
	dns_dispatch_detach(&query->dispatch);
394
395
396
397
398
399
	if (!RESQUERY_CONNECTING(query)) {
		/*
		 * It's safe to destroy the query now.
		 */
		resquery_destroy(&query);
	}
Bob Halley's avatar
Bob Halley committed
400
401
402
}

static void
403
fctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) {
Bob Halley's avatar
Bob Halley committed
404
405
406
407
408
409
410
411
	resquery_t *query, *next_query;

	FCTXTRACE("cancelqueries");

	for (query = ISC_LIST_HEAD(fctx->queries);
	     query != NULL;
	     query = next_query) {
		next_query = ISC_LIST_NEXT(query, link);
412
		fctx_cancelquery(&query, NULL, NULL, no_response);
Bob Halley's avatar
Bob Halley committed
413
414
415
	}
}

Bob Halley's avatar
Bob Halley committed
416
static void
Bob Halley's avatar
Bob Halley committed
417
418
419
fctx_cleanupfinds(fetchctx_t *fctx) {
	dns_adbfind_t *find, *next_find;

Bob Halley's avatar
Bob Halley committed
420
421
	REQUIRE(ISC_LIST_EMPTY(fctx->queries));

Bob Halley's avatar
Bob Halley committed
422
423
424
425
426
427
	for (find = ISC_LIST_HEAD(fctx->finds);
	     find != NULL;
	     find = next_find) {
		next_find = ISC_LIST_NEXT(find, publink);
		ISC_LIST_UNLINK(fctx->finds, find, publink);
		dns_adb_destroyfind(&find);
Bob Halley's avatar
Bob Halley committed
428
	}
Bob Halley's avatar
Bob Halley committed
429
	fctx->find = NULL;
Bob Halley's avatar
Bob Halley committed
430
431
}

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
static void
fctx_cleanupforwaddrs(fetchctx_t *fctx) {
	dns_adbaddrinfo_t *addr, *next_addr;

	REQUIRE(ISC_LIST_EMPTY(fctx->queries));

	for (addr = ISC_LIST_HEAD(fctx->forwaddrs);
	     addr != NULL;
	     addr = next_addr) {
		next_addr = ISC_LIST_NEXT(addr, publink);
		ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink);
		dns_adb_freeaddrinfo(fctx->res->view->adb, &addr);
	}
}

Bob Halley's avatar
Bob Halley committed
447
static inline void
448
fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
Bob Halley's avatar
Bob Halley committed
449
	FCTXTRACE("stopeverything");
450
	fctx_cancelqueries(fctx, no_response);
Bob Halley's avatar
Bob Halley committed
451
	fctx_cleanupfinds(fctx);
452
	fctx_cleanupforwaddrs(fctx);
Bob Halley's avatar
Bob Halley committed
453
	fctx_stoptimer(fctx);
Bob Halley's avatar
Bob Halley committed
454
}
Bob Halley's avatar
Bob Halley committed
455

Bob Halley's avatar
Bob Halley committed
456
457
458
459
static inline void
fctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
	dns_fetchevent_t *event, *next_event;
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
460

Bob Halley's avatar
Bob Halley committed
461
462
463
464
465
466
	/*
	 * Caller must be holding the appropriate bucket lock.
	 */
	REQUIRE(fctx->state == fetchstate_done);

	FCTXTRACE("sendevents");
Bob Halley's avatar
Bob Halley committed
467
468
469
470
471
472
473

	for (event = ISC_LIST_HEAD(fctx->events);
	     event != NULL;
	     event = next_event) {
		next_event = ISC_LIST_NEXT(event, link);
		task = event->sender;
		event->sender = fctx;
Bob Halley's avatar
Bob Halley committed
474
		if (!HAVE_ANSWER(fctx))
475
			event->result = result;
476
		isc_task_sendanddetach(&task, (isc_event_t **)&event);
Bob Halley's avatar
Bob Halley committed
477
478
	}
	ISC_LIST_INIT(fctx->events);
Bob Halley's avatar
Bob Halley committed
479
}
Bob Halley's avatar
Bob Halley committed
480

Bob Halley's avatar
Bob Halley committed
481
482
483
static void
fctx_done(fetchctx_t *fctx, isc_result_t result) {
	dns_resolver_t *res;
484
	isc_boolean_t no_response;
Bob Halley's avatar
Bob Halley committed
485
486
487
488
489

	FCTXTRACE("done");

	res = fctx->res;

490
491
492
493
494
	if (result == ISC_R_SUCCESS)
		no_response = ISC_TRUE;
	else
		no_response = ISC_FALSE;
	fctx_stopeverything(fctx, no_response);
Bob Halley's avatar
Bob Halley committed
495
496
497
498
499

	LOCK(&res->buckets[fctx->bucketnum].lock);

	fctx->state = fetchstate_done;
	fctx_sendevents(fctx, result);
Bob Halley's avatar
Bob Halley committed
500
501

	UNLOCK(&res->buckets[fctx->bucketnum].lock);
Bob Halley's avatar
Bob Halley committed
502
503
}

Bob Halley's avatar
Bob Halley committed
504
static void
Bob Halley's avatar
Bob Halley committed
505
resquery_senddone(isc_task_t *task, isc_event_t *event) {
Bob Halley's avatar
Bob Halley committed
506
507
508
509
510
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
	resquery_t *query = event->arg;

	REQUIRE(event->type == ISC_SOCKEVENT_SENDDONE);

Bob Halley's avatar
Bob Halley committed
511
512
	QTRACE("senddone");

Bob Halley's avatar
Bob Halley committed
513
514
515
516
517
518
519
520
	/*
	 * XXXRTH
	 *
	 * Currently we don't wait for the senddone event before retrying
	 * a query.  This means that if we get really behind, we may end
	 * up doing extra work!
	 */

Andreas Gustafsson's avatar
Andreas Gustafsson committed
521
	UNUSED(task);
Bob Halley's avatar
Bob Halley committed
522

Bob Halley's avatar
Bob Halley committed
523
	if (sevent->result != ISC_R_SUCCESS)
524
		fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
525
				 
Bob Halley's avatar
Bob Halley committed
526
527
528
	isc_event_free(&event);
}

Bob Halley's avatar
Bob Halley committed
529
530
531
532
533
static inline isc_result_t
fctx_addopt(dns_message_t *message) {
	dns_rdataset_t *rdataset;
	dns_rdatalist_t *rdatalist;
	dns_rdata_t *rdata;
Bob Halley's avatar
Bob Halley committed
534
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567

	rdatalist = NULL;
	result = dns_message_gettemprdatalist(message, &rdatalist);
	if (result != ISC_R_SUCCESS)
		return (result);
	rdata = NULL;
	result = dns_message_gettemprdata(message, &rdata);
	if (result != ISC_R_SUCCESS)
		return (result);
	rdataset = NULL;
	result = dns_message_gettemprdataset(message, &rdataset);
	if (result != ISC_R_SUCCESS)
		return (result);
	dns_rdataset_init(rdataset);

	rdatalist->type = dns_rdatatype_opt;
	rdatalist->covers = 0;

	/*
	 * Set Maximum UDP buffer size.
	 */
	rdatalist->rdclass = SEND_BUFFER_SIZE;

	/*
	 * Set EXTENDED-RCODE, VERSION, and Z to 0.
	 */
	rdatalist->ttl = 0;

	/*
	 * No ENDS options.
	 */
	rdata->data = NULL;
	rdata->length = 0;
568
569
	rdata->rdclass = rdatalist->rdclass;
	rdata->type = rdatalist->type;
Bob Halley's avatar
Bob Halley committed
570
571
572
573
574
575
576
577

	ISC_LIST_INIT(rdatalist->rdata);
	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
	dns_rdatalist_tordataset(rdatalist, rdataset);

	return (dns_message_setopt(message, rdataset));
}

578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
static inline void
fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
	unsigned int seconds;

	/*
	 * We retry every 2 seconds the first two times through the address
	 * list, and then we do exponential back-off.
	 */
	if (fctx->restarts < 3)
		seconds = 2;
	else
		seconds = (2 << (fctx->restarts - 1));

	/*
	 * Double the round-trip time and convert to seconds.
	 */
	rtt /= 500000;
	
	/*
	 * Always wait for at least the doubled round-trip time.
	 */
	if (seconds < rtt)
		seconds = rtt;

	/*
	 * But don't ever wait for more than 30 seconds.
	 */
	if (seconds > 30)
		seconds = 30;

	isc_interval_set(&fctx->interval, seconds, 0);
}

Bob Halley's avatar
Bob Halley committed
611
612
613
614
static isc_result_t
fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
	   unsigned int options)
{
Bob Halley's avatar
Bob Halley committed
615
616
	dns_resolver_t *res;
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
617
618
619
	isc_result_t result;
	resquery_t *query;
	isc_socket_t *socket;
Bob Halley's avatar
Bob Halley committed
620

Bob Halley's avatar
Bob Halley committed
621
	FCTXTRACE("query");
Bob Halley's avatar
Bob Halley committed
622

Bob Halley's avatar
Bob Halley committed
623
624
	res = fctx->res;
	task = res->buckets[fctx->bucketnum].task;
Bob Halley's avatar
Bob Halley committed
625

626
	fctx_setretryinterval(fctx, addrinfo->srtt);
627
	result = fctx_startidletimer(fctx);
Bob Halley's avatar
Bob Halley committed
628
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
629
630
		return (result);

Bob Halley's avatar
Bob Halley committed
631
	dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
Bob Halley's avatar
Bob Halley committed
632

Bob Halley's avatar
Bob Halley committed
633
	query = isc_mem_get(res->mctx, sizeof *query);
Bob Halley's avatar
Bob Halley committed
634
635
	if (query == NULL) {
		result = ISC_R_NOMEMORY; 
636
		goto stop_idle_timer;
Bob Halley's avatar
Bob Halley committed
637
	}
Bob Halley's avatar
Bob Halley committed
638
	query->options = options;
639
	query->attributes = 0;
Bob Halley's avatar
Bob Halley committed
640
641
642
643
644
645
646
647
	/*
	 * Note that the caller MUST guarantee that 'addrinfo' will remain
	 * valid until this query is canceled.
	 */
	query->addrinfo = addrinfo;
	result = isc_time_now(&query->start);
	if (result != ISC_R_SUCCESS)
		goto cleanup_query;
Bob Halley's avatar
Bob Halley committed
648
649
650
651
	
	/*
	 * If this is a TCP query, then we need to make a socket and
	 * a dispatch for it here.  Otherwise we use the resolver's
Bob Halley's avatar
Bob Halley committed
652
653
654
655
656
657
658
659
660
661
662
663
	 * shared dispatch.
	 */
	query->dispatch = NULL;
	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
		socket = NULL;
		result = isc_socket_create(res->socketmgr,
					   isc_sockaddr_pf(addrinfo->sockaddr),
					   isc_sockettype_tcp,
					   &socket);
		if (result != ISC_R_SUCCESS)
			goto cleanup_query;
		result = dns_dispatch_create(res->mctx, socket, task,
664
					     4096, 2, 1, 1, 3, NULL,
665
					     &query->dispatch);
Bob Halley's avatar
Bob Halley committed
666
667
668
669
670
671
672
		/*
		 * Regardless of whether dns_dispatch_create() succeeded or
		 * not, we don't need our reference to the socket anymore.
		 */
		isc_socket_detach(&socket);
		if (result != ISC_R_SUCCESS)
			goto cleanup_dispatch;
Bob Halley's avatar
Bob Halley committed
673
	} else {
Bob Halley's avatar
Bob Halley committed
674
		switch (isc_sockaddr_pf(addrinfo->sockaddr)) {
Bob Halley's avatar
Bob Halley committed
675
		case PF_INET:
676
			dns_dispatch_attach(res->dispatchv4, &query->dispatch);
Bob Halley's avatar
Bob Halley committed
677
			break;
Bob Halley's avatar
Bob Halley committed
678
		case PF_INET6:
679
			dns_dispatch_attach(res->dispatchv6, &query->dispatch);
Bob Halley's avatar
Bob Halley committed
680
681
			break;
		default:
682
			result = ISC_R_NOTIMPLEMENTED;
Bob Halley's avatar
Bob Halley committed
683
			goto cleanup_dispatch;
Bob Halley's avatar
Bob Halley committed
684
685
686
687
688
689
690
691
692
693
		}
		/*
		 * We should always have a valid dispatcher here.  If we
		 * don't support a protocol family, then its dispatcher
		 * will be NULL, but we shouldn't be finding addresses for
		 * protocol types we don't support, so the dispatcher
		 * we found should never be NULL.
		 */
		INSIST(query->dispatch != NULL);
	}
Bob Halley's avatar
Bob Halley committed
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711

	query->dispentry = NULL;
	query->fctx = fctx;
	query->tsig = NULL;
	query->tsigkey = NULL;
	query->magic = QUERY_MAGIC;

	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
		/*
		 * Connect to the remote server.
		 *
		 * XXXRTH  Should we attach to the socket?
		 */
		socket = dns_dispatch_getsocket(query->dispatch);
		result = isc_socket_connect(socket, addrinfo->sockaddr,
					    task, resquery_connected, query);
		if (result != ISC_R_SUCCESS)
			goto cleanup_dispatch;
712
		query->attributes |= RESQUERY_ATTR_CONNECTING;
Bob Halley's avatar
Bob Halley committed
713
714
715
716
717
718
719
		QTRACE("connecting via TCP");
	} else {
		result = resquery_send(query);
		if (result != ISC_R_SUCCESS)
			goto cleanup_dispatch;
	}

720
721
	ISC_LIST_APPEND(fctx->queries, query, link);

Bob Halley's avatar
Bob Halley committed
722
723
724
725
726
727
728
729
730
	return (ISC_R_SUCCESS);

 cleanup_dispatch:
	dns_dispatch_detach(&query->dispatch);

 cleanup_query:
	query->magic = 0;
	isc_mem_put(res->mctx, query, sizeof *query);

731
732
 stop_idle_timer:
	fctx_stopidletimer(fctx);
Bob Halley's avatar
Bob Halley committed
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749

	return (result);
}

static isc_result_t
resquery_send(resquery_t *query) {
	fetchctx_t *fctx;
	isc_result_t result;
	dns_rdataset_t *qrdataset, *trdataset;
	dns_name_t *qname;
	isc_region_t r;
	dns_resolver_t *res;
	isc_task_t *task;
	isc_socket_t *socket;
	isc_buffer_t tcpbuffer;
	isc_sockaddr_t *address;
	isc_buffer_t *buffer;
750
751
752
	dns_peer_t *peer = NULL;
	dns_name_t *keyname = NULL;
	isc_netaddr_t ipaddr;
Bob Halley's avatar
Bob Halley committed
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785

	fctx = query->fctx;
	QTRACE("send");

	res = fctx->res;
	task = res->buckets[fctx->bucketnum].task;
	address = NULL;

	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
		/*
		 * Reserve space for the TCP message length.
		 */
		isc_buffer_init(&tcpbuffer, query->data,
				sizeof query->data, ISC_BUFFERTYPE_BINARY);
		isc_buffer_init(&query->buffer, query->data + 2,
				sizeof query->data - 2,
				ISC_BUFFERTYPE_BINARY);
		buffer = &tcpbuffer;
	} else {
		isc_buffer_init(&query->buffer, query->data,
				sizeof query->data, ISC_BUFFERTYPE_BINARY);
		buffer = &query->buffer;
	}

	qname = NULL;
	result = dns_message_gettempname(fctx->qmessage, &qname);
	if (result != ISC_R_SUCCESS)
		goto cleanup_temps;
	qrdataset = NULL;
	result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset);
	if (result != ISC_R_SUCCESS)
		goto cleanup_temps;

Bob Halley's avatar
Bob Halley committed
786
	/*
Bob Halley's avatar
Bob Halley committed
787
	 * Get a query id from the dispatch.
Bob Halley's avatar
Bob Halley committed
788
	 */
Bob Halley's avatar
Bob Halley committed
789
	result = dns_dispatch_addresponse(query->dispatch,
Bob Halley's avatar
Bob Halley committed
790
					  query->addrinfo->sockaddr,
Bob Halley's avatar
Bob Halley committed
791
					  task,
Bob Halley's avatar
Bob Halley committed
792
					  resquery_response,
Bob Halley's avatar
Bob Halley committed
793
794
795
					  query,
					  &query->id,
					  &query->dispentry);
Bob Halley's avatar
Bob Halley committed
796
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
797
		goto cleanup_temps;
Bob Halley's avatar
Bob Halley committed
798
799
800
801
802
803

	fctx->qmessage->opcode = dns_opcode_query;

	/*
	 * Set up question.
	 */
Bob Halley's avatar
Bob Halley committed
804
805
806
807
808
809
	dns_name_init(qname, NULL);
	dns_name_clone(&fctx->name, qname);
	dns_rdataset_init(qrdataset);
	dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type);
	ISC_LIST_APPEND(qname->list, qrdataset, link);
	dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION);
810
811
	qname = NULL;
	qrdataset = NULL;
812
813
814
815
816
817
818

	/*
	 * Set RD if the client has requested that we do a recursive query,
	 * or if we're sending to a forwarder.
	 */
	if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 ||
	    ISFORWARDER(query->addrinfo))
Bob Halley's avatar
Bob Halley committed
819
		fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
820

Bob Halley's avatar
Bob Halley committed
821
822
823
	/*
	 * We don't have to set opcode because it defaults to query.
	 */
Bob Halley's avatar
Bob Halley committed
824
	fctx->qmessage->id = query->id;
Bob Halley's avatar
Bob Halley committed
825

Bob Halley's avatar
Bob Halley committed
826
827
828
	/*
	 * Convert the question to wire format.
	 */
Bob Halley's avatar
Bob Halley committed
829
	result = dns_message_renderbegin(fctx->qmessage, &query->buffer);
Bob Halley's avatar
Bob Halley committed
830
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
831
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
832

Bob Halley's avatar
Bob Halley committed
833
	result = dns_message_rendersection(fctx->qmessage,
Michael Graff's avatar
Michael Graff committed
834
					   DNS_SECTION_QUESTION, 0);
Bob Halley's avatar
Bob Halley committed
835
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
836
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863

	/*
	 * Use EDNS0, unless the caller doesn't want it, or we know that
	 * the remote server doesn't like it.
	 */
	if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
		if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
			trdataset = NULL;
			result = fctx_addopt(fctx->qmessage);
			if (result != ISC_R_SUCCESS) {
				/*
				 * We couldn't add the OPT, but we'll press on.
				 * We're not using EDNS0, so set the NOEDNS0
				 * bit.
				 */
				query->options |= DNS_FETCHOPT_NOEDNS0;
			}
		} else {
			/*
			 * We know this server doesn't like EDNS0, so we
			 * won't use it.  Set the NOEDNS0 bit since we're
			 * not using EDNS0.
			 */
			query->options |= DNS_FETCHOPT_NOEDNS0;
		}
	}

864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
	/*
	 * If we need EDNS0 to do this query and aren't using it, we lose.
	 */
	if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) {
		result = DNS_R_SERVFAIL;
		goto cleanup_message;
	}

	/*
	 * If we're using EDNS, set AD and CD so we'll get DNSSEC data.
	 */
	if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
		fctx->qmessage->flags |=
			(DNS_MESSAGEFLAG_AD|DNS_MESSAGEFLAG_CD);

Bob Halley's avatar
Bob Halley committed
879
	/*
880
	 * Add TSIG record tailored to the current recipient.
Bob Halley's avatar
Bob Halley committed
881
	 */
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
	isc_netaddr_fromsockaddr(&ipaddr, query->addrinfo->sockaddr);
	result = dns_peerlist_peerbyaddr(fctx->res->view->peers,
					 &ipaddr, &peer);

	if (result == ISC_R_SUCCESS &&
	    dns_peer_getkey(peer, &keyname) == ISC_R_SUCCESS)
	{
		result = dns_tsigkey_find(&fctx->qmessage->tsigkey,
					  keyname, NULL,
					  fctx->res->view->statickeys);
		if (result == ISC_R_NOTFOUND)
			result = dns_tsigkey_find(&fctx->qmessage->tsigkey,
						  keyname, NULL,
						  fctx->res->view->dynamickeys);
		if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
			goto cleanup_message;
	}
Bob Halley's avatar
Bob Halley committed
899

Bob Halley's avatar
Bob Halley committed
900
	result = dns_message_rendersection(fctx->qmessage,
Michael Graff's avatar
Michael Graff committed
901
					   DNS_SECTION_ADDITIONAL, 0);
Bob Halley's avatar
Bob Halley committed
902
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
903
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
904

Bob Halley's avatar
Bob Halley committed
905
	result = dns_message_renderend(fctx->qmessage);
Bob Halley's avatar
Bob Halley committed
906
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
907
908
		goto cleanup_message;

909
910
911
912
	if (fctx->qmessage->tsigkey != NULL) {
		query->tsigkey = fctx->qmessage->tsigkey;
		query->tsig = fctx->qmessage->tsig;
		fctx->qmessage->tsig = NULL;
913
		fctx->qmessage->tsigkey = NULL;
914
915
	}

Bob Halley's avatar
Bob Halley committed
916
917
918
919
920
921
922
923
924
925
	/*
	 * If using TCP, write the length of the message at the beginning
	 * of the buffer.
	 */
	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
		isc_buffer_used(&query->buffer, &r);
		isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length);
		isc_buffer_add(&tcpbuffer, r.length);
	}

Bob Halley's avatar
Bob Halley committed
926
927
928
929
	/*
	 * We're now done with the query message.
	 */
	dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
Bob Halley's avatar
Bob Halley committed
930

Bob Halley's avatar
Bob Halley committed
931
	socket = dns_dispatch_getsocket(query->dispatch);
Bob Halley's avatar
Bob Halley committed
932
933
934
	/*
	 * Send the query!
	 */
Bob Halley's avatar
Bob Halley committed
935
936
937
938
	if ((query->options & DNS_FETCHOPT_TCP) == 0)
		address = query->addrinfo->sockaddr;
	isc_buffer_used(buffer, &r);
	result = isc_socket_sendto(socket, &r, task, resquery_senddone,
Michael Graff's avatar
Michael Graff committed
939
				   query, address, NULL);
Bob Halley's avatar
Bob Halley committed
940
941
	if (result != ISC_R_SUCCESS)
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
942
	QTRACE("sent");
Bob Halley's avatar
Bob Halley committed
943

Bob Halley's avatar
Bob Halley committed
944
	return (ISC_R_SUCCESS);
Bob Halley's avatar
Bob Halley committed
945
946
947

 cleanup_message:
	dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
Bob Halley's avatar
Bob Halley committed
948

Bob Halley's avatar
Bob Halley committed
949
	/*
Bob Halley's avatar
Bob Halley committed
950
	 * Stop the dispatcher from listening.
Bob Halley's avatar
Bob Halley committed
951
952
953
954
955
	 */
	dns_dispatch_removeresponse(query->dispatch,
				    &query->dispentry,
				    NULL);

Bob Halley's avatar
Bob Halley committed
956
957
958
959
960
 cleanup_temps:
	if (qname != NULL)
		dns_message_puttempname(fctx->qmessage, &qname);
	if (qrdataset != NULL)
		dns_message_puttemprdataset(fctx->qmessage, &qrdataset);
Bob Halley's avatar
Bob Halley committed
961

Bob Halley's avatar
Bob Halley committed
962
	return (result);
Bob Halley's avatar
Bob Halley committed
963
964
}

Bob Halley's avatar
Bob Halley committed
965
966
967
968
969
970
971
static void
resquery_connected(isc_task_t *task, isc_event_t *event) {
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
	resquery_t *query = event->arg;
	isc_result_t result;

	REQUIRE(event->type == ISC_SOCKEVENT_CONNECT);
972
	REQUIRE(VALID_QUERY(query));
Bob Halley's avatar
Bob Halley committed
973
974
975

	QTRACE("connected");

Andreas Gustafsson's avatar
Andreas Gustafsson committed
976
	UNUSED(task);
Bob Halley's avatar
Bob Halley committed
977
978
979
980
981
982
983
984
985

	/*
	 * XXXRTH
	 *
	 * Currently we don't wait for the connect event before retrying
	 * a query.  This means that if we get really behind, we may end
	 * up doing extra work!
	 */

986
987
988
989
990
991
992
993
994
995
996
997
998
999
	query->attributes &= ~RESQUERY_ATTR_CONNECTING;

	if (RESQUERY_CANCELED(query)) {
		/*
		 * This query was canceled while the connect() was in
		 * progress.
		 */
		resquery_destroy(&query);
	} else {
		if (sevent->result == ISC_R_SUCCESS) {
			/*
			 * We are connected.  Send the query.
			 */
			result = resquery_send(query);
1000
			if (result != ISC_R_SUCCESS) {
1001
1002
				fctx_cancelquery(&query, NULL, NULL,
						 ISC_FALSE);
1003
1004
				fctx_done(query->fctx, result);
			}
1005
1006
1007
		} else
			fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
	}
Bob Halley's avatar
Bob Halley committed
1008
1009
1010
1011
				 
	isc_event_free(&event);
}

Bob Halley's avatar
Bob Halley committed
1012
static void
Bob Halley's avatar
Bob Halley committed
1013
fctx_finddone(isc_task_t *task, isc_event_t *event) {
Bob Halley's avatar
Bob Halley committed
1014
	fetchctx_t *fctx;
Bob Halley's avatar
Bob Halley committed
1015
1016
	dns_adbfind_t *find;
	dns_resolver_t *res;
Bob Halley's avatar
Bob Halley committed
1017
1018
	isc_boolean_t want_try = ISC_FALSE;
	isc_boolean_t want_done = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
1019
1020
	isc_boolean_t bucket_empty = ISC_FALSE;
	unsigned int bucketnum;
Bob Halley's avatar
Bob Halley committed
1021

Bob Halley's avatar
Bob Halley committed
1022
	find = event->sender;
Bob Halley's avatar
Bob Halley committed
1023
1024
	fctx = event->arg;
	REQUIRE(VALID_FCTX(fctx));
Bob Halley's avatar
Bob Halley committed
1025
	res = fctx->res;
Bob Halley's avatar
Bob Halley committed
1026

Andreas Gustafsson's avatar
Andreas Gustafsson committed
1027
	UNUSED(task);
Bob Halley's avatar
Bob Halley committed
1028

Bob Halley's avatar
Bob Halley committed
1029
1030
1031
1032
	FCTXTRACE("finddone");

	INSIST(fctx->pending > 0);
	fctx->pending--;
Bob Halley's avatar
Bob Halley committed
1033
1034

	if (ADDRWAIT(fctx)) {
Bob Halley's avatar
Bob Halley committed
1035
1036
1037
		/*
		 * The fetch is waiting for a name to be found.
		 */
1038
		INSIST(!SHUTTINGDOWN(fctx));
Bob Halley's avatar
Bob Halley committed
1039
1040
1041
		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
		if (event->type == DNS_EVENT_ADBMOREADDRESSES)
			want_try = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
1042
1043
1044
1045
1046
1047
		else if (fctx->pending == 0) {
			/*
			 * We've got nothing else to wait for and don't
			 * know the answer.  There's nothing to do but
			 * fail the fctx.
			 */
Bob Halley's avatar
Bob Halley committed
1048
			want_done = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
1049
		}
1050
1051
	} else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 &&
		   fctx->validating == 0) {
Bob Halley's avatar
Bob Halley committed
1052
1053
		bucketnum = fctx->bucketnum;
		LOCK(&res->buckets[bucketnum].lock);
1054
1055
1056
1057
1058
1059
		/*
		 * Note that we had to wait until we had the lock before
		 * looking at fctx->references.
		 */
		if (fctx->references == 0)
			bucket_empty = fctx_destroy(fctx);
Bob Halley's avatar
Bob Halley committed
1060
		UNLOCK(&res->buckets[bucketnum].lock);
Bob Halley's avatar
Bob Halley committed
1061
	}
Bob Halley's avatar
Bob Halley committed
1062

Bob Halley's avatar
Bob Halley committed
1063
	isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
1064
	dns_adb_destroyfind(&find);
Bob Halley's avatar
Bob Halley committed
1065
1066
1067
1068

	if (want_try)
		fctx_try(fctx);
	else if (want_done)
Bob Halley's avatar
Bob Halley committed
1069
1070
1071
		fctx_done(fctx, ISC_R_FAILURE);
	else if (bucket_empty)
		empty_bucket(res);
Bob Halley's avatar
Bob Halley committed
1072
1073
}

Bob Halley's avatar
Bob Halley committed
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151

static inline isc_boolean_t
bad_server(fetchctx_t *fctx, isc_sockaddr_t *address) {
	isc_sockaddr_t *sa;

	for (sa = ISC_LIST_HEAD(fctx->bad);
	     sa != NULL;
	     sa = ISC_LIST_NEXT(sa, link)) {
		if (isc_sockaddr_equal(sa, address))
			return (ISC_TRUE);
	}
	
	return (ISC_FALSE);
}

static inline isc_boolean_t
mark_bad(fetchctx_t *fctx) {
	dns_adbfind_t *curr;
	dns_adbaddrinfo_t *addrinfo;
	isc_boolean_t all_bad = ISC_TRUE;

	/*
	 * Mark all known bad servers, so we don't try to talk to them
	 * again.
	 */

	/*
	 * Mark any bad nameservers.
	 */
	for (curr = ISC_LIST_HEAD(fctx->finds);
	     curr != NULL;
	     curr = ISC_LIST_NEXT(curr, publink)) {
		for (addrinfo = ISC_LIST_HEAD(curr->list);
		     addrinfo != NULL;
		     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
			if (bad_server(fctx, addrinfo->sockaddr))
				addrinfo->flags |= FCTX_ADDRINFO_MARK;
			else
				all_bad = ISC_FALSE;
		}
	}

	/*
	 * Mark any bad forwarders.
	 */
	for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
	     addrinfo != NULL;
	     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
		if (bad_server(fctx, addrinfo->sockaddr))
			addrinfo->flags |= FCTX_ADDRINFO_MARK;
		else
			all_bad = ISC_FALSE;
	}
	
	return (all_bad);
}

static void
add_bad(fetchctx_t *fctx, isc_sockaddr_t *address) {
	isc_sockaddr_t *sa;

	if (bad_server(fctx, address)) {
		/*
		 * We already know this server is bad.
		 */
		return;
	}

	FCTXTRACE("add_bad");

	sa = isc_mem_get(fctx->res->mctx, sizeof *sa);
	if (sa == NULL)
		return;
	*sa = *address;
	ISC_LINK_INIT(sa, link);
	ISC_LIST_APPEND(fctx->bad, sa, link);
}

Bob Halley's avatar
Bob Halley committed
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
static void
sort_adbfind(dns_adbfind_t *find) {
	dns_adbaddrinfo_t *best, *curr;
	dns_adbaddrinfolist_t sorted;

	/*
	 * Lame N^2 bubble sort.
	 */

	ISC_LIST_INIT(sorted);
	while (!ISC_LIST_EMPTY(find->list)) {
		best = ISC_LIST_HEAD(find->list);
		curr = ISC_LIST_NEXT(best, publink);
		while (curr != NULL) {
			if (curr->srtt < best->srtt)
				best = curr;
			curr = ISC_LIST_NEXT(curr, publink);
		}
		ISC_LIST_UNLINK(find->list, best, publink);
		ISC_LIST_APPEND(sorted, best, publink);
	} 
	find->list = sorted;
}

static void
sort_finds(fetchctx_t *fctx) {
	dns_adbfind_t *best, *curr;
	dns_adbfindlist_t sorted;
	dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;

	/*
	 * Lame N^2 bubble sort.
	 */

	ISC_LIST_INIT(sorted);
	while (!ISC_LIST_EMPTY(fctx->finds)) {
		best = ISC_LIST_HEAD(fctx->finds);
		bestaddrinfo = ISC_LIST_HEAD(best->list);
		INSIST(bestaddrinfo != NULL);
		curr = ISC_LIST_NEXT(best, publink);
		while (curr != NULL) {
			addrinfo = ISC_LIST_HEAD(curr->list);
			INSIST(addrinfo != NULL);
			if (addrinfo->srtt < bestaddrinfo->srtt) {
				best = curr;
				bestaddrinfo = addrinfo;
			}
			curr = ISC_LIST_NEXT(curr, publink);
		}
		ISC_LIST_UNLINK(fctx->finds, best, publink);
		ISC_LIST_APPEND(sorted, best, publink);
	}
	fctx->finds = sorted;
}

Bob Halley's avatar
Bob Halley committed
1207
static isc_result_t
Bob Halley's avatar
Bob Halley committed
1208
fctx_getaddresses(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
1209
1210
1211
1212
	dns_rdata_t rdata;
	isc_region_t r;
	dns_name_t name;
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
1213
1214
	dns_resolver_t *res;
	isc_stdtime_t now;
Bob Halley's avatar
Bob Halley committed
1215
	dns_adbfind_t *find;
1216
	unsigned int stdoptions, options;
1217
1218
	isc_sockaddr_t *sa;
	dns_adbaddrinfo_t *ai;
Bob Halley's avatar
Bob Halley committed
1219
	isc_boolean_t pruned, all_bad;
Bob Halley's avatar
Bob Halley committed
1220
1221
1222

	FCTXTRACE("getaddresses");

Bob Halley's avatar
Bob Halley committed
1223
	/*
Bob Halley's avatar
Bob Halley committed
1224
	 * Don't pound on remote servers.  (Failsafe!)
Bob Halley's avatar
Bob Halley committed
1225
1226
	 */
	fctx->restarts++;
1227
1228
	if (fctx->restarts > 10) {
		FCTXTRACE("too many restarts");
Bob Halley's avatar
Bob Halley committed
1229
		return (DNS_R_SERVFAIL);
1230
	}
Bob Halley's avatar
Bob Halley committed
1231

Bob Halley's avatar
Bob Halley committed
1232
	res = fctx->res;
1233
1234
	pruned = ISC_FALSE;
	stdoptions = 0;		/* Keep compiler happy. */
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252

	/*
	 * Forwarders.
	 */

	INSIST(ISC_LIST_EMPTY(fctx->forwaddrs));

	/*
	 * If this fctx has forwarders, use them; otherwise the use
	 * resolver's forwarders (if any).
	 */
	sa = ISC_LIST_HEAD(fctx->forwarders);
	if (sa == NULL)
		sa = ISC_LIST_HEAD(res->forwarders);

	while (sa != NULL) {
		ai = NULL;
		result = dns_adb_findaddrinfo(fctx->res->view->adb,
1253
					      sa, &ai, 0);  /* XXXMLG */
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
		if (result == ISC_R_SUCCESS) {
			ai->flags |= FCTX_ADDRINFO_FORWARDER;
			ISC_LIST_APPEND(fctx->forwaddrs, ai, publink);
		}
		sa = ISC_LIST_NEXT(sa, link);
	}

	/*
	 * If the forwarding policy is "only", we don't need the addresses
	 * of the nameservers.
	 */
	if (res->fwdpolicy == dns_fwdpolicy_only)
		goto out;

	/*
	 * Normal nameservers.
	 */

1272
	stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT |
Bob Halley's avatar
Bob Halley committed
1273
		DNS_ADBFIND_AVOIDFETCHES;
1274
	if (res->dispatchv4 != NULL)
1275
		stdoptions |= DNS_ADBFIND_INET;
1276
	if (res->dispatchv6 != NULL)
1277
		stdoptions |= DNS_ADBFIND_INET6;
1278
	isc_stdtime_get(&now);
Bob Halley's avatar
Bob Halley committed
1279

1280
 restart:
Bob Halley's avatar
Bob Halley committed
1281
	INSIST(ISC_LIST_EMPTY(fctx->finds));
Bob Halley's avatar
Bob Halley committed
1282
1283
1284
1285

	result = dns_rdataset_first(&fctx->nameservers);
	while (result == ISC_R_SUCCESS) {
		dns_rdataset_current(&fctx->nameservers, &rdata);
Bob Halley's avatar
Bob Halley committed
1286
1287
1288
		/*
		 * Extract the name from the NS record.
		 */
Bob Halley's avatar
Bob Halley committed
1289
1290
1291
		dns_rdata_toregion(&rdata, &r);
		dns_name_init(&name, NULL);
		dns_name_fromregion(&name, &r);
1292
		options = stdoptions;
Bob Halley's avatar
Bob Halley committed
1293
		/*
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
		 * If this name is a subdomain of the query domain, tell
		 * the ADB to start looking at "." if it doesn't know the
		 * address.  This keeps us from getting stuck if the
		 * nameserver is beneath the zone cut and we don't know its
		 * address (e.g. because the A record has expired).
		 * By restarting from ".", we ensure that any missing glue
		 * will be reestablished.
		 *
		 * A further optimization would be to get the ADB to start
		 * looking at the most enclosing zone cut above fctx->domain.
		 * We don't expect this situation to happen very frequently,
		 * so we've chosen the simple solution.
Bob Halley's avatar
Bob Halley committed
1306
		 */
1307
1308
		if (dns_name_issubdomain(&name, &fctx->domain))
			options |= DNS_ADBFIND_STARTATROOT;
1309
1310
1311
		options |= DNS_ADBFIND_GLUEOK;
		options |= DNS_ADBFIND_HINTOK;

Bob Halley's avatar
Bob Halley committed
1312
1313
1314
		/*
		 * See what we know about this address.
		 */
Bob Halley's avatar
Bob Halley committed
1315
		find = NULL;
1316
1317
		result = dns_adb_createfind(res->view->adb,
					    res->buckets[fctx->bucketnum].task,
Bob Halley's avatar
Bob Halley committed
1318
					    fctx_finddone, fctx, &name,
1319
					    &fctx->domain, options, now, NULL,
Bob Halley's avatar
Bob Halley committed
1320
					    &find);
1321
1322
1323
1324
1325
1326
1327
1328
		if (result != ISC_R_SUCCESS) {
			if (result == DNS_R_ALIAS) {
				/*
				 * XXXRTH  Follow the CNAME/DNAME chain?
				 */
				dns_adb_destroyfind(&find);
			}
		} else if (!ISC_LIST_EMPTY(find->list)) {
Bob Halley's avatar
Bob Halley committed
1329
1330
1331
1332
			/*
			 * We have at least some of the addresses for the
			 * name.
			 */
Bob Halley's avatar
Bob Halley committed
1333
			INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
Bob Halley's avatar
Bob Halley committed
1334
			sort_adbfind(find);
Bob Halley's avatar
Bob Halley committed
1335
			ISC_LIST_APPEND(fctx->finds, find, publink);
Bob Halley's avatar
Bob Halley committed
1336
1337
1338
1339
1340
		} else {
			/*
			 * We don't know any of the addresses for this
			 * name.
			 */
Bob Halley's avatar
Bob Halley committed
1341
			if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
Bob Halley's avatar
Bob Halley committed
1342
				/*
Bob Halley's avatar
Bob Halley committed
1343
1344
				 * We're looking for them and will get an
				 * event about it later.
Bob Halley's avatar
Bob Halley committed
1345
				 */
Bob Halley's avatar
Bob Halley committed
1346
1347
1348
1349
				fctx->pending++;
			} else {
				/*
				 * And ADB isn't going to send us any events
1350
				 * either.  This find loses.
Bob Halley's avatar
Bob Halley committed
1351
				 */
1352
1353
1354
1355
1356
1357
1358
1359
1360
				if ((find->options & DNS_ADBFIND_LAMEPRUNED)
				    != 0) {
					/*
					 * The ADB pruned lame servers for
					 * this name.  Remember that in case
					 * we get desperate later on.
					 */
					pruned = ISC_TRUE;
				}
Bob Halley's avatar
Bob Halley committed
1361
				dns_adb_destroyfind(&find);
Bob Halley's avatar
Bob Halley committed
1362
1363
1364
1365
			}
		}
		result = dns_rdataset_next(&fctx->nameservers);
	}
1366
	if (result != ISC_R_NOMORE)
Bob Halley's avatar
Bob Halley committed
1367
1368
		return (result);

1369
 out:
Bob Halley's avatar
Bob Halley committed
1370
1371
1372
1373
1374
1375
1376
1377
1378
	/*
	 * Mark all known bad servers.
	 */
	all_bad = mark_bad(fctx);

	/*
	 * How are we doing?
	 */
	if (all_bad) {
Bob Halley's avatar
Bob Halley committed
1379
		/*
1380
		 * We've got no addresses.
Bob Halley's avatar
Bob Halley committed
1381
		 */
1382
1383
1384
1385
1386
1387
		if (fctx->pending > 0) {
			/*
			 * We're fetching the addresses, but don't have any
			 * yet.   Tell the caller to wait for an answer.
			 */
			result = DNS_R_WAIT;
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
		} else if (pruned) {
			/*
			 * Some addresses were removed by lame pruning.
			 * Turn pruning off and try again.
			 */
			FCTXTRACE("restarting with returnlame");
			INSIST((stdoptions & DNS_ADBFIND_RETURNLAME) == 0);
			stdoptions |= DNS_ADBFIND_RETURNLAME;
			pruned = ISC_FALSE;
			goto restart;
1398
1399
1400
1401
1402
1403
		} else {
			/*
			 * We've lost completely.  We don't know any
			 * addresses, and the ADB has told us it can't get
			 * them.
			 */
1404
			FCTXTRACE("no addresses");
1405
1406
			result = ISC_R_FAILURE;
		}
Bob Halley's avatar
Bob Halley committed
1407
1408
1409
1410
1411
	} else {
		/*
		 * We've found some addresses.  We might still be looking
		 * for more addresses.
		 */
1412
1413
1414
1415
1416
		/*
		 * XXXRTH  We could sort the forwaddrs here if the caller
		 *         wants to use the forwaddrs in "best order" as
		 *         opposed to "fixed order".
		 */
Bob Halley's avatar
Bob Halley committed
1417
		sort_finds(fctx);
Bob Halley's avatar
Bob Halley committed
1418
		result = ISC_R_SUCCESS;
Bob Halley's avatar
Bob Halley committed
1419
	}
Bob Halley's avatar
Bob Halley committed
1420
1421

	return (result);
Bob Halley's avatar
Bob Halley committed
1422
1423
}

Bob Halley's avatar
Bob Halley committed
1424
1425
static inline dns_adbaddrinfo_t *
fctx_nextaddress(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
1426
	dns_adbfind_t *find;
Bob Halley's avatar
Bob Halley committed
1427
	dns_adbaddrinfo_t *addrinfo;
Bob Halley's avatar
Bob Halley committed