resolver.c 73 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 * Copyright (C) 1999  Internet Software Consortium.
 * 
 * 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
24
25
26
27
28
29
30
#include <isc/result.h>
#include <isc/timer.h>
#include <isc/mutex.h>
#include <isc/event.h>
#include <isc/task.h>
#include <isc/stdtime.h>

#include <dns/types.h>
Bob Halley's avatar
Bob Halley committed
31
#include <dns/adb.h>
Bob Halley's avatar
Bob Halley committed
32
33
#include <dns/result.h>
#include <dns/name.h>
Bob Halley's avatar
Bob Halley committed
34
#include <dns/db.h>
Bob Halley's avatar
Bob Halley committed
35
36
#include <dns/events.h>
#include <dns/message.h>
37
#include <dns/ncache.h>
Bob Halley's avatar
Bob Halley committed
38
39
#include <dns/dispatch.h>
#include <dns/resolver.h>
40
#include <dns/rdata.h>
Bob Halley's avatar
Bob Halley committed
41
#include <dns/rdataset.h>
42
#include <dns/tsig.h>
43
#include <dns/view.h>
44
#include <dns/log.h>
45
46

#include <dst/dst.h>
Bob Halley's avatar
Bob Halley committed
47
48
49
50
51

#include "../isc/util.h"		/* XXX */

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

87

Bob Halley's avatar
Bob Halley committed
88
89
typedef struct fetchctx fetchctx_t;

Bob Halley's avatar
Bob Halley committed
90
typedef struct query {
Bob Halley's avatar
Bob Halley committed
91
92
93
94
95
	/* Not locked. */
	unsigned int			magic;
	fetchctx_t *			fctx;
	dns_dispatch_t *		dispatch;
	/* Locked by fctx lock. */
Bob Halley's avatar
Bob Halley committed
96
	dns_messageid_t			id;
Bob Halley's avatar
Bob Halley committed
97
	dns_dispentry_t *		dispentry;	/* XXX name */
Bob Halley's avatar
Bob Halley committed
98
	ISC_LINK(struct query)		link;
Bob Halley's avatar
Bob Halley committed
99
	isc_buffer_t			buffer;
100
	dns_rdata_any_tsig_t		*tsig;
101
	dns_tsigkey_t			*tsigkey;
Bob Halley's avatar
Bob Halley committed
102
	unsigned char			data[512];
Bob Halley's avatar
Bob Halley committed
103
104
} resquery_t;

Bob Halley's avatar
Bob Halley committed
105
106
107
108
#define QUERY_MAGIC			0x51212121U	/* Q!!! */
#define VALID_QUERY(query)		((query) != NULL && \
					 (query)->magic == QUERY_MAGIC)

Bob Halley's avatar
Bob Halley committed
109
110
111
112
113
114
typedef enum {
	fetchstate_init = 0,
	fetchstate_active,
	fetchstate_done
} fetchstate;

Bob Halley's avatar
Bob Halley committed
115
116
struct fetchctx {
	/* Not locked. */
Bob Halley's avatar
Bob Halley committed
117
118
119
	unsigned int			magic;
	dns_resolver_t *		res;
	dns_name_t			name;
Bob Halley's avatar
Bob Halley committed
120
	dns_rdatatype_t			type;
Bob Halley's avatar
Bob Halley committed
121
	unsigned int			options;
Bob Halley's avatar
Bob Halley committed
122
	isc_task_t *			task;
Bob Halley's avatar
Bob Halley committed
123
	unsigned int			bucketnum;
Bob Halley's avatar
Bob Halley committed
124
	/* Locked by lock. */
Bob Halley's avatar
Bob Halley committed
125
	fetchstate			state;
Bob Halley's avatar
Bob Halley committed
126
	isc_boolean_t			want_shutdown;
Bob Halley's avatar
Bob Halley committed
127
128
129
130
131
	unsigned int			references;
	isc_event_t			control_event;
	ISC_LINK(struct fetchctx)	link;
	ISC_LIST(dns_fetchevent_t)	events;
	/* Only changable by event actions running in the context's task */
Bob Halley's avatar
Bob Halley committed
132
133
	dns_name_t			domain;
	dns_rdataset_t			nameservers;
Bob Halley's avatar
Bob Halley committed
134
	unsigned int			attributes;
Bob Halley's avatar
Bob Halley committed
135
136
137
	isc_timer_t *			timer;
	isc_time_t			expires;
	isc_interval_t			interval;
Bob Halley's avatar
Bob Halley committed
138
139
	dns_message_t *			qmessage;
	dns_message_t *			rmessage;
Bob Halley's avatar
Bob Halley committed
140
	ISC_LIST(resquery_t)		queries;
Bob Halley's avatar
Bob Halley committed
141
142
143
	ISC_LIST(dns_adbfind_t)		finds;
	dns_adbfind_t *			find;
	unsigned int			pending;
Bob Halley's avatar
Bob Halley committed
144
};
Bob Halley's avatar
Bob Halley committed
145
146
147
148
149

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

Bob Halley's avatar
Bob Halley committed
150
151
#define FCTX_ATTR_HAVEANSWER		0x01
#define FCTX_ATTR_GLUING		0x02
Bob Halley's avatar
Bob Halley committed
152
#define FCTX_ATTR_ADDRWAIT		0x04
Bob Halley's avatar
Bob Halley committed
153
#define FCTX_ATTR_SHUTTINGDOWN		0x08
Bob Halley's avatar
Bob Halley committed
154
155
#define FCTX_ATTR_WANTCACHE		0x10
#define FCTX_ATTR_WANTNCACHE		0x20
Bob Halley's avatar
Bob Halley committed
156
157
158
159
160

#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
161
162
#define ADDRWAIT(f)		(((f)->attributes & FCTX_ATTR_ADDRWAIT) != \
				 0)
Bob Halley's avatar
Bob Halley committed
163
164
#define SHUTTINGDOWN(f)		(((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \
 				 != 0)
Bob Halley's avatar
Bob Halley committed
165
166
#define WANTCACHE(f)		(((f)->attributes & FCTX_ATTR_WANTCACHE) != 0)
#define WANTNCACHE(f)		(((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)
Bob Halley's avatar
Bob Halley committed
167

Bob Halley's avatar
Bob Halley committed
168
169
170
171
172
173
174
175
176
177
178
179
180
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
181
	isc_boolean_t			exiting;
Bob Halley's avatar
Bob Halley committed
182
183
} fctxbucket_t;

Bob Halley's avatar
Bob Halley committed
184
185
186
187
188
189
struct dns_resolver {
	/* Unlocked */
	unsigned int			magic;
	isc_mem_t *			mctx;
	isc_mutex_t			lock;
	dns_rdataclass_t		rdclass;
Bob Halley's avatar
Bob Halley committed
190
	isc_socketmgr_t *		socketmgr;
Bob Halley's avatar
Bob Halley committed
191
	isc_timermgr_t *		timermgr;
192
	dns_view_t *			view;
Bob Halley's avatar
Bob Halley committed
193
194
195
	/* Locked by lock. */
	unsigned int			references;
	isc_boolean_t			exiting;
Bob Halley's avatar
Bob Halley committed
196
197
198
199
200
	isc_socket_t *			udpsocket4;
	isc_socket_t *			udpsocket6;
	dns_dispatch_t *		dispatch4;
	dns_dispatch_t *		dispatch6;
	unsigned int			nbuckets;
Bob Halley's avatar
Bob Halley committed
201
	unsigned int			activebuckets;
Bob Halley's avatar
Bob Halley committed
202
	fctxbucket_t *			buckets;
Bob Halley's avatar
Bob Halley committed
203
204
205
206
207
208
209
210
};

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


static void destroy(dns_resolver_t *res);
Bob Halley's avatar
Bob Halley committed
211
static void empty_bucket(dns_resolver_t *res);
Bob Halley's avatar
Bob Halley committed
212
static void resquery_response(isc_task_t *task, isc_event_t *event);
Bob Halley's avatar
Bob Halley committed
213
static void fctx_try(fetchctx_t *fctx);
Bob Halley's avatar
Bob Halley committed
214
static isc_boolean_t fctx_destroy(fetchctx_t *fctx);
Bob Halley's avatar
Bob Halley committed
215

Bob Halley's avatar
Bob Halley committed
216
static inline isc_result_t
Bob Halley's avatar
Bob Halley committed
217
fctx_starttimer(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
218
219
220
	return (isc_timer_reset(fctx->timer, isc_timertype_once,
				&fctx->expires, &fctx->interval,
				ISC_FALSE));
Bob Halley's avatar
Bob Halley committed
221
222
}

Bob Halley's avatar
Bob Halley committed
223
224
225
226
227
228
229
static inline isc_result_t
fctx_stopidletimer(fetchctx_t *fctx) {
	return (isc_timer_reset(fctx->timer, isc_timertype_once,
				&fctx->expires, NULL,
				ISC_FALSE));
}

Bob Halley's avatar
Bob Halley committed
230
231
static inline void
fctx_stoptimer(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
232
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
233
234
235
236
237
238
239

	/*
	 * 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
240
	result = isc_timer_reset(fctx->timer, isc_timertype_inactive,
Bob Halley's avatar
Bob Halley committed
241
				  NULL, NULL, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
242
	if (result != ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
243
244
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_timer_reset(): %s",
Bob Halley's avatar
Bob Halley committed
245
				 isc_result_totext(result));
Bob Halley's avatar
Bob Halley committed
246
247
248
	}
}

Bob Halley's avatar
Bob Halley committed
249
250

static inline void
Bob Halley's avatar
Bob Halley committed
251
252
253
fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp) {
	fetchctx_t *fctx;
	resquery_t *query;
Bob Halley's avatar
Bob Halley committed
254

Bob Halley's avatar
Bob Halley committed
255
256
257
	query = *queryp;
	fctx = query->fctx;

Bob Halley's avatar
Bob Halley committed
258
259
	FCTXTRACE("cancelquery");

Bob Halley's avatar
Bob Halley committed
260
261
262
263
264
265
266
	dns_dispatch_removeresponse(query->dispatch, &query->dispentry,
				    deventp);
	ISC_LIST_UNLINK(fctx->queries, query, link);
	query->magic = 0;
	if (query->tsig != NULL)
		dns_rdata_freestruct(query->tsig);
	isc_mem_put(fctx->res->mctx, query, sizeof *query);
Bob Halley's avatar
Bob Halley committed
267
	*queryp = NULL;
Bob Halley's avatar
Bob Halley committed
268
269
270
271
272
273
274
275
276
277
278
279
}

static void
fctx_cancelqueries(fetchctx_t *fctx) {
	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);
Bob Halley's avatar
Bob Halley committed
280
		fctx_cancelquery(&query, NULL);
Bob Halley's avatar
Bob Halley committed
281
282
283
	}
}

Bob Halley's avatar
Bob Halley committed
284
static void
Bob Halley's avatar
Bob Halley committed
285
286
287
288
289
290
291
292
293
fctx_cleanupfinds(fetchctx_t *fctx) {
	dns_adbfind_t *find, *next_find;

	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
294
	}
Bob Halley's avatar
Bob Halley committed
295
	fctx->find = NULL;
Bob Halley's avatar
Bob Halley committed
296
297
}

Bob Halley's avatar
Bob Halley committed
298
299
300
301
static inline void
fctx_stopeverything(fetchctx_t *fctx) {
	FCTXTRACE("stopeverything");
	fctx_cleanupfinds(fctx);
Bob Halley's avatar
Bob Halley committed
302
	fctx_cancelqueries(fctx);
Bob Halley's avatar
Bob Halley committed
303
	fctx_stoptimer(fctx);
Bob Halley's avatar
Bob Halley committed
304
}
Bob Halley's avatar
Bob Halley committed
305

Bob Halley's avatar
Bob Halley committed
306
307
308
309
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
310

Bob Halley's avatar
Bob Halley committed
311
312
313
314
315
316
	/*
	 * Caller must be holding the appropriate bucket lock.
	 */
	REQUIRE(fctx->state == fetchstate_done);

	FCTXTRACE("sendevents");
Bob Halley's avatar
Bob Halley committed
317
318
319
320
321
322
323

	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
324
		if (!HAVE_ANSWER(fctx))
325
			event->result = result;
326
		isc_task_sendanddetach(&task, (isc_event_t **)&event);
Bob Halley's avatar
Bob Halley committed
327
328
	}
	ISC_LIST_INIT(fctx->events);
Bob Halley's avatar
Bob Halley committed
329
}
Bob Halley's avatar
Bob Halley committed
330

Bob Halley's avatar
Bob Halley committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
static void
fctx_done(fetchctx_t *fctx, isc_result_t result) {
	dns_resolver_t *res;

	FCTXTRACE("done");

	res = fctx->res;

	fctx_stopeverything(fctx);

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

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

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

Bob Halley's avatar
Bob Halley committed
349
static void
Bob Halley's avatar
Bob Halley committed
350
resquery_senddone(isc_task_t *task, isc_event_t *event) {
Bob Halley's avatar
Bob Halley committed
351
352
353
354
355
	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
356
357
358
359
360
361
362
363
	/*
	 * 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!
	 */

Bob Halley's avatar
Bob Halley committed
364
365
	(void)task;

Bob Halley's avatar
Bob Halley committed
366
	if (sevent->result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
367
		fctx_cancelquery(&query, NULL);
Bob Halley's avatar
Bob Halley committed
368
				 
Bob Halley's avatar
Bob Halley committed
369
370
371
	isc_event_free(&event);
}

Bob Halley's avatar
Bob Halley committed
372
373
static isc_result_t
fctx_sendquery(fetchctx_t *fctx, isc_sockaddr_t *address) {
Bob Halley's avatar
Bob Halley committed
374
	resquery_t *query;
Bob Halley's avatar
Bob Halley committed
375
376
377
	isc_result_t result;
	dns_rdataset_t *qrdataset;
	dns_name_t *qname;
Bob Halley's avatar
Bob Halley committed
378
	isc_region_t r;
Bob Halley's avatar
Bob Halley committed
379
380
	dns_resolver_t *res;
	isc_task_t *task;
Bob Halley's avatar
Bob Halley committed
381
382
383

	FCTXTRACE("sendquery");

Bob Halley's avatar
Bob Halley committed
384
385
	res = fctx->res;
	task = res->buckets[fctx->bucketnum].task;
Bob Halley's avatar
Bob Halley committed
386

Bob Halley's avatar
Bob Halley committed
387
	result = fctx_starttimer(fctx);
Bob Halley's avatar
Bob Halley committed
388
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
389
390
		return (result);

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

Bob Halley's avatar
Bob Halley committed
393
394
395
396
397
398
399
400
401
402
	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;

	query = isc_mem_get(res->mctx, sizeof *query);
Bob Halley's avatar
Bob Halley committed
403
	if (query == NULL)
Bob Halley's avatar
Bob Halley committed
404
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
Bob Halley committed
405
406
	isc_buffer_init(&query->buffer, query->data, sizeof query->data,
			ISC_BUFFERTYPE_BINARY);
Bob Halley's avatar
Bob Halley committed
407
408
409
410
411
412
	
	/*
	 * 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
	 * shared dispatch.  We do not attach to the resolver's shared
	 * dispatch if we use it, so the resolver MUST ensure that no
Bob Halley's avatar
Bob Halley committed
413
	 * fetches are running before changing the shared dispatch.
Bob Halley's avatar
Bob Halley committed
414
415
416
417
418
	 */
	if ((fctx->options & DNS_FETCHOPT_TCP) != 0) {
		/* XXXRTH */
		result = DNS_R_NOTIMPLEMENTED;
		goto cleanup_query;
Bob Halley's avatar
Bob Halley committed
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
	} else {
		switch (isc_sockaddr_pf(address)) {
		case AF_INET:
			query->dispatch = res->dispatch4;
			break;
		case AF_INET6:
			query->dispatch = res->dispatch6;
			break;
		default:
			result = DNS_R_NOTIMPLEMENTED;
			goto cleanup_query;
		}
		/*
		 * 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
440
	
Bob Halley's avatar
Bob Halley committed
441
	/*
Bob Halley's avatar
Bob Halley committed
442
	 * Get a query id from the dispatch.
Bob Halley's avatar
Bob Halley committed
443
	 */
Bob Halley's avatar
Bob Halley committed
444
445
446
	query->dispentry = NULL;
	result = dns_dispatch_addresponse(query->dispatch,
					  address,
Bob Halley's avatar
Bob Halley committed
447
					  task,
Bob Halley's avatar
Bob Halley committed
448
					  resquery_response,
Bob Halley's avatar
Bob Halley committed
449
450
451
					  query,
					  &query->id,
					  &query->dispentry);
Bob Halley's avatar
Bob Halley committed
452
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
453
454
		goto cleanup_query;
	query->fctx = fctx;
455
456
	query->tsig = NULL;
	query->tsigkey = NULL;
Bob Halley's avatar
Bob Halley committed
457
	query->magic = QUERY_MAGIC;
Bob Halley's avatar
Bob Halley committed
458
459
460
461
462
463

	fctx->qmessage->opcode = dns_opcode_query;

	/*
	 * Set up question.
	 */
Bob Halley's avatar
Bob Halley committed
464
465
466
467
468
469
	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);
Bob Halley's avatar
Bob Halley committed
470
471
472
473
474
	if ((fctx->options & DNS_FETCHOPT_RECURSIVE) != 0)
		fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
	/*
	 * We don't have to set opcode because it defaults to query.
	 */
Bob Halley's avatar
Bob Halley committed
475
	fctx->qmessage->id = query->id;
Bob Halley's avatar
Bob Halley committed
476
477
478
479
480
	/*
	 * XXXRTH  Add TSIG and/or ENDS0 OPT record tailored to the current
	 *         recipient.
	 */

Bob Halley's avatar
Bob Halley committed
481
482
483
	/*
	 * Convert the question to wire format.
	 */
Bob Halley's avatar
Bob Halley committed
484
	result = dns_message_renderbegin(fctx->qmessage, &query->buffer);
Bob Halley's avatar
Bob Halley committed
485
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
486
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
487
488
	result = dns_message_rendersection(fctx->qmessage,
					   DNS_SECTION_QUESTION, 0, 0);
Bob Halley's avatar
Bob Halley committed
489
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
490
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
491
492
	result = dns_message_rendersection(fctx->qmessage,
					   DNS_SECTION_ADDITIONAL, 0, 0);
Bob Halley's avatar
Bob Halley committed
493
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
494
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
495
	result = dns_message_renderend(fctx->qmessage);
Bob Halley's avatar
Bob Halley committed
496
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
497
498
		goto cleanup_message;

499
500
501
502
503
504
	if (fctx->qmessage->tsigkey != NULL) {
		query->tsigkey = fctx->qmessage->tsigkey;
		query->tsig = fctx->qmessage->tsig;
		fctx->qmessage->tsig = NULL;
	}

Bob Halley's avatar
Bob Halley committed
505
506
507
508
	/*
	 * We're now done with the query message.
	 */
	dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
Bob Halley's avatar
Bob Halley committed
509

Bob Halley's avatar
Bob Halley committed
510
511
512
513
514
	/*
	 * Send the query!
	 */
	isc_buffer_used(&query->buffer, &r);
	result = isc_socket_sendto(dns_dispatch_getsocket(query->dispatch),
Bob Halley's avatar
Bob Halley committed
515
				   &r, task, resquery_senddone,
Bob Halley's avatar
Bob Halley committed
516
517
518
				   query, address);
	if (result != ISC_R_SUCCESS)
		goto cleanup_message;
Bob Halley's avatar
Bob Halley committed
519

Bob Halley's avatar
Bob Halley committed
520
521
522
523
524
	/*
	 * Finally, we've got everything going!
	 */
	ISC_LIST_APPEND(fctx->queries, query, link);

Bob Halley's avatar
Bob Halley committed
525
	QTRACE("sent");
Bob Halley's avatar
Bob Halley committed
526

Bob Halley's avatar
Bob Halley committed
527
	return (ISC_R_SUCCESS);
Bob Halley's avatar
Bob Halley committed
528
529
530

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

Bob Halley's avatar
Bob Halley committed
532
	/*
Bob Halley's avatar
Bob Halley committed
533
	 * Stop the dispatcher from listening.
Bob Halley's avatar
Bob Halley committed
534
535
536
537
538
539
540
541
542
543
544
545
	 */
	dns_dispatch_removeresponse(query->dispatch,
				    &query->dispentry,
				    NULL);

	/* 
	 * XXXRTH will need to cleanup a nonshared dispatch and TCP socket
	 * here.
	 */

 cleanup_query:
	query->magic = 0;
Bob Halley's avatar
Bob Halley committed
546
	isc_mem_put(res->mctx, query, sizeof *query);
Bob Halley's avatar
Bob Halley committed
547

Bob Halley's avatar
Bob Halley committed
548
549
550
551
552
 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
553

Bob Halley's avatar
Bob Halley committed
554
	fctx_stoptimer(fctx);
Bob Halley's avatar
Bob Halley committed
555

Bob Halley's avatar
Bob Halley committed
556
	return (result);
Bob Halley's avatar
Bob Halley committed
557
558
}

Bob Halley's avatar
Bob Halley committed
559
static void
Bob Halley's avatar
Bob Halley committed
560
fctx_finddone(isc_task_t *task, isc_event_t *event) {
Bob Halley's avatar
Bob Halley committed
561
	fetchctx_t *fctx;
Bob Halley's avatar
Bob Halley committed
562
563
	dns_adbfind_t *find;
	dns_resolver_t *res;
Bob Halley's avatar
Bob Halley committed
564
565
	isc_boolean_t want_try = ISC_FALSE;
	isc_boolean_t want_done = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
566
567
	isc_boolean_t bucket_empty = ISC_FALSE;
	unsigned int bucketnum;
Bob Halley's avatar
Bob Halley committed
568

Bob Halley's avatar
Bob Halley committed
569
	find = event->sender;
Bob Halley's avatar
Bob Halley committed
570
571
	fctx = event->arg;
	REQUIRE(VALID_FCTX(fctx));
Bob Halley's avatar
Bob Halley committed
572
	res = fctx->res;
Bob Halley's avatar
Bob Halley committed
573
574
575

	(void)task;

Bob Halley's avatar
Bob Halley committed
576
577
578
579
	FCTXTRACE("finddone");

	INSIST(fctx->pending > 0);
	fctx->pending--;
Bob Halley's avatar
Bob Halley committed
580
581

	if (ADDRWAIT(fctx)) {
Bob Halley's avatar
Bob Halley committed
582
583
584
		/*
		 * The fetch is waiting for a name to be found.
		 */
Bob Halley's avatar
Bob Halley committed
585
586
587
		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
		if (event->type == DNS_EVENT_ADBMOREADDRESSES)
			want_try = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
588
589
590
591
592
593
		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
594
			want_done = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
595
596
597
598
599
600
601
		}
	} else if (fctx->pending == 0 && fctx->references == 0 &&
		   SHUTTINGDOWN(fctx)) {
		bucketnum = fctx->bucketnum;
		LOCK(&res->buckets[bucketnum].lock);
		bucket_empty = fctx_destroy(fctx);
		UNLOCK(&res->buckets[bucketnum].lock);
Bob Halley's avatar
Bob Halley committed
602
	}
Bob Halley's avatar
Bob Halley committed
603

Bob Halley's avatar
Bob Halley committed
604
	isc_event_free(&event);
Bob Halley's avatar
Bob Halley committed
605
	dns_adb_destroyfind(&find);
Bob Halley's avatar
Bob Halley committed
606
607
608
609

	if (want_try)
		fctx_try(fctx);
	else if (want_done)
Bob Halley's avatar
Bob Halley committed
610
611
612
		fctx_done(fctx, ISC_R_FAILURE);
	else if (bucket_empty)
		empty_bucket(res);
Bob Halley's avatar
Bob Halley committed
613
614
}

Bob Halley's avatar
Bob Halley committed
615
static isc_result_t
Bob Halley's avatar
Bob Halley committed
616
fctx_getaddresses(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
617
618
619
620
	dns_rdata_t rdata;
	isc_region_t r;
	dns_name_t name;
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
621
622
	dns_resolver_t *res;
	isc_stdtime_t now;
Bob Halley's avatar
Bob Halley committed
623
	dns_adbfind_t *find;
Bob Halley's avatar
Bob Halley committed
624
	unsigned int options;
Bob Halley's avatar
Bob Halley committed
625
626
627

	FCTXTRACE("getaddresses");

Bob Halley's avatar
Bob Halley committed
628
	res = fctx->res;
629
630
631
632
633
	options = DNS_ADBFIND_WANTEVENT|DNS_ADBFIND_EMPTYEVENT;
	if (res->dispatch4 != NULL)
		options |= DNS_ADBFIND_INET;
	if (res->dispatch6 != NULL)
		options |= DNS_ADBFIND_INET6;
Bob Halley's avatar
Bob Halley committed
634
635
636
	result = isc_stdtime_get(&now);
	if (result != ISC_R_SUCCESS)
		return (result);
Bob Halley's avatar
Bob Halley committed
637

Bob Halley's avatar
Bob Halley committed
638
	fctx_cleanupfinds(fctx);
Bob Halley's avatar
Bob Halley committed
639
640
641
642

	result = dns_rdataset_first(&fctx->nameservers);
	while (result == ISC_R_SUCCESS) {
		dns_rdataset_current(&fctx->nameservers, &rdata);
Bob Halley's avatar
Bob Halley committed
643
644
645
		/*
		 * Extract the name from the NS record.
		 */
Bob Halley's avatar
Bob Halley committed
646
647
648
		dns_rdata_toregion(&rdata, &r);
		dns_name_init(&name, NULL);
		dns_name_fromregion(&name, &r);
Bob Halley's avatar
Bob Halley committed
649
650
651
652
653
654
655
656
		/*
		 * XXXRTH  If this name is the same as QNAME, remember
		 *         skip it, and remember that we did so so we can
		 *         use an ancestor QDOMAIN if we find no addresses.
		 */
		/*
		 * See what we know about this address.
		 */
Bob Halley's avatar
Bob Halley committed
657
		find = NULL;
658
659
		result = dns_adb_createfind(res->view->adb,
					    res->buckets[fctx->bucketnum].task,
Bob Halley's avatar
Bob Halley committed
660
					    fctx_finddone, fctx, &name,
661
					    &fctx->domain, options, now,
Bob Halley's avatar
Bob Halley committed
662
					    &find);
Bob Halley's avatar
Bob Halley committed
663
664
		if (result != ISC_R_SUCCESS)
			return (result);
Bob Halley's avatar
Bob Halley committed
665
		if (!ISC_LIST_EMPTY(find->list)) {
Bob Halley's avatar
Bob Halley committed
666
667
668
669
			/*
			 * We have at least some of the addresses for the
			 * name.
			 */
Bob Halley's avatar
Bob Halley committed
670
			INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
Bob Halley's avatar
Bob Halley committed
671
672
673
			/*
			 * XXXRTH  Sort.
			 */
Bob Halley's avatar
Bob Halley committed
674
			ISC_LIST_APPEND(fctx->finds, find, publink);
Bob Halley's avatar
Bob Halley committed
675
676
677
678
679
		} else {
			/*
			 * We don't know any of the addresses for this
			 * name.
			 */
Bob Halley's avatar
Bob Halley committed
680
			if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
Bob Halley's avatar
Bob Halley committed
681
				/*
Bob Halley's avatar
Bob Halley committed
682
683
				 * We're looking for them and will get an
				 * event about it later.
Bob Halley's avatar
Bob Halley committed
684
				 */
Bob Halley's avatar
Bob Halley committed
685
686
687
688
689
690
691
				fctx->pending++;
			} else {
				/*
				 * And ADB isn't going to send us any events
				 * either.  This query loses.
				 */
				dns_adb_destroyfind(&find);
Bob Halley's avatar
Bob Halley committed
692
693
694
695
			}
		}
		result = dns_rdataset_next(&fctx->nameservers);
	}
Bob Halley's avatar
Bob Halley committed
696
697
698
	if (result != DNS_R_NOMORE)
		return (result);

Bob Halley's avatar
Bob Halley committed
699
	if (ISC_LIST_EMPTY(fctx->finds) && fctx->pending > 0) {
Bob Halley's avatar
Bob Halley committed
700
701
702
703
704
		/*
		 * We're fetching the addresses, but don't have any yet.
		 * Tell the caller to wait for an answer.
		 */
		result = DNS_R_WAIT;
Bob Halley's avatar
Bob Halley committed
705
706
707
708
709
710
	} else if (ISC_LIST_EMPTY(fctx->finds)) {
		/*
		 * We've lost completely.  We don't know any addresses, and
		 * the ADB has told us it can't get them.
		 */
		result = ISC_R_FAILURE;
Bob Halley's avatar
Bob Halley committed
711
712
713
714
715
716
717
718
719
720
	} else {
		/*
		 * We've found some addresses.  We might still be looking
		 * for more addresses.
		 */

		/*
		 * XXXRTH  Sort.
		 */

Bob Halley's avatar
Bob Halley committed
721
		result = ISC_R_SUCCESS;
Bob Halley's avatar
Bob Halley committed
722
	}
Bob Halley's avatar
Bob Halley committed
723
724

	return (result);
Bob Halley's avatar
Bob Halley committed
725
726
}

Bob Halley's avatar
Bob Halley committed
727
728
729
730
731
732
#define FCTX_ADDRINFO_MARK		0x01
#define UNMARKED(a)			(((a)->flags & FCTX_ADDRINFO_MARK) \
					 == 0)

static inline dns_adbaddrinfo_t *
fctx_nextaddress(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
733
	dns_adbfind_t *find;
Bob Halley's avatar
Bob Halley committed
734
735
	dns_adbaddrinfo_t *addrinfo;
	int count = 0;
Bob Halley's avatar
Bob Halley committed
736
737

	/*
Bob Halley's avatar
Bob Halley committed
738
	 * Return the next untried address, if any.
Bob Halley's avatar
Bob Halley committed
739
740
	 */

Bob Halley's avatar
Bob Halley committed
741
	/*
Bob Halley's avatar
Bob Halley committed
742
	 * Move to the next find.
Bob Halley's avatar
Bob Halley committed
743
	 */
Bob Halley's avatar
Bob Halley committed
744
745
746
	find = fctx->find;
	if (find == NULL)
		find = ISC_LIST_HEAD(fctx->finds);
Bob Halley's avatar
Bob Halley committed
747
	else {
Bob Halley's avatar
Bob Halley committed
748
749
750
		find = ISC_LIST_NEXT(find, publink);
		if (find == NULL)
			find = ISC_LIST_HEAD(fctx->finds);
Bob Halley's avatar
Bob Halley committed
751
752
753
754
755
756
	}

	/*
	 * Find the first unmarked addrinfo.
	 */
	addrinfo = NULL;
Bob Halley's avatar
Bob Halley committed
757
	while (find != fctx->find) {
Bob Halley's avatar
Bob Halley committed
758
759
		count++;
		INSIST(count < 1000);
Bob Halley's avatar
Bob Halley committed
760
		for (addrinfo = ISC_LIST_HEAD(find->list);
Bob Halley's avatar
Bob Halley committed
761
762
763
764
765
766
767
768
769
		     addrinfo != NULL;
		     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
			if (UNMARKED(addrinfo)) {
				addrinfo->flags |= FCTX_ADDRINFO_MARK;
				break;
			}
		}
		if (addrinfo != NULL)
			break;
Bob Halley's avatar
Bob Halley committed
770
771
772
		find = ISC_LIST_NEXT(find, publink);
		if (find != fctx->find && find == NULL)
			find = ISC_LIST_HEAD(fctx->finds);
Bob Halley's avatar
Bob Halley committed
773
774
	}

Bob Halley's avatar
Bob Halley committed
775
	fctx->find = find;
Bob Halley's avatar
Bob Halley committed
776
777
778
779
780
781
782
783
784

	return (addrinfo);
}

static void
fctx_try(fetchctx_t *fctx) {
	isc_result_t result;
	dns_adbaddrinfo_t *addrinfo;

Bob Halley's avatar
Bob Halley committed
785
	FCTXTRACE("try");
Bob Halley's avatar
Bob Halley committed
786

Bob Halley's avatar
Bob Halley committed
787
788
789
790
791
	REQUIRE(!ADDRWAIT(fctx));

	/*
	 * XXXRTH  We don't try to handle forwarding yet.
	 */
Bob Halley's avatar
Bob Halley committed
792

Bob Halley's avatar
Bob Halley committed
793
794
795
796
797
	addrinfo = fctx_nextaddress(fctx);
	if (addrinfo == NULL) {
		/*
		 * We have no more addresses.  Start over.
		 */
Bob Halley's avatar
Bob Halley committed
798
		fctx_cancelqueries(fctx);
Bob Halley's avatar
Bob Halley committed
799
		result = fctx_getaddresses(fctx);
Bob Halley's avatar
Bob Halley committed
800
801
802
803
804
805
806
807
808
809
810
		if (result == DNS_R_WAIT) {
			/*
			 * Sleep waiting for addresses.
			 */
			FCTXTRACE("addrwait");
			fctx->attributes |= FCTX_ATTR_ADDRWAIT; 
			return;
		} else if (result != ISC_R_SUCCESS) {
			/*
			 * Something bad happened.
			 */
Bob Halley's avatar
Bob Halley committed
811
812
813
			fctx_done(fctx, result);
			return;
		}
Bob Halley's avatar
Bob Halley committed
814

Bob Halley's avatar
Bob Halley committed
815
816
817
		addrinfo = fctx_nextaddress(fctx);
		/*
		 * fctx_getaddresses() returned success, so at least one
Bob Halley's avatar
Bob Halley committed
818
		 * of the find lists should be nonempty.
Bob Halley's avatar
Bob Halley committed
819
		 */
Bob Halley's avatar
Bob Halley committed
820
		INSIST(addrinfo != NULL);
Bob Halley's avatar
Bob Halley committed
821
822
	}

Bob Halley's avatar
Bob Halley committed
823
	/*
Bob Halley's avatar
Bob Halley committed
824
825
826
	 * XXXRTH  This is the place where a try strategy routine would
	 *         be called to send one or more queries.  Instead, we
	 *	   just send a single query.
Bob Halley's avatar
Bob Halley committed
827
	 */
Bob Halley's avatar
Bob Halley committed
828

Bob Halley's avatar
Bob Halley committed
829
	result = fctx_sendquery(fctx, addrinfo->sockaddr);
Bob Halley's avatar
Bob Halley committed
830
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
831
832
833
		fctx_done(fctx, result);
}

Bob Halley's avatar
Bob Halley committed
834
static isc_boolean_t
Bob Halley's avatar
Bob Halley committed
835
fctx_destroy(fetchctx_t *fctx) {
Bob Halley's avatar
Bob Halley committed
836
837
	dns_resolver_t *res;
	unsigned int bucketnum;
Bob Halley's avatar
Bob Halley committed
838
839
840
841
842

	/*
	 * Caller must be holding the bucket lock.
	 */

Bob Halley's avatar
Bob Halley committed
843
844
845
846
	REQUIRE(VALID_FCTX(fctx));
	REQUIRE(fctx->state == fetchstate_done);
	REQUIRE(ISC_LIST_EMPTY(fctx->events));
	REQUIRE(ISC_LIST_EMPTY(fctx->queries));
Bob Halley's avatar
Bob Halley committed
847
848
	REQUIRE(ISC_LIST_EMPTY(fctx->finds));
	REQUIRE(fctx->pending == 0);
Bob Halley's avatar
Bob Halley committed
849
850
851

	FCTXTRACE("destroy");

Bob Halley's avatar
Bob Halley committed
852
853
854
855
	res = fctx->res;
	bucketnum = fctx->bucketnum;

	ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
Bob Halley's avatar
Bob Halley committed
856

Bob Halley's avatar
Bob Halley committed
857
	isc_timer_detach(&fctx->timer);
Bob Halley's avatar
Bob Halley committed
858
859
	dns_message_destroy(&fctx->rmessage);
	dns_message_destroy(&fctx->qmessage);
Bob Halley's avatar
Bob Halley committed
860
	if (dns_name_countlabels(&fctx->domain) > 0) {
Bob Halley's avatar
Bob Halley committed
861
862
		if (dns_rdataset_isassociated(&fctx->nameservers))
			dns_rdataset_disassociate(&fctx->nameservers);
Bob Halley's avatar
Bob Halley committed
863
		dns_name_free(&fctx->domain, res->mctx);
Bob Halley's avatar
Bob Halley committed
864
	}
Bob Halley's avatar
Bob Halley committed
865
	dns_name_free(&fctx->name, fctx->res->mctx);
Bob Halley's avatar
Bob Halley committed
866
867
868
869
870
871
872
	isc_mem_put(res->mctx, fctx, sizeof *fctx);

	if (res->buckets[bucketnum].exiting &&
	    ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs))
		return (ISC_TRUE);

	return (ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
}

/*
 * Fetch event handlers.
 */

static void
fctx_timeout(isc_task_t *task, isc_event_t *event) {
	fetchctx_t *fctx = event->arg;

	REQUIRE(VALID_FCTX(fctx));

	(void)task;	/* Keep compiler quiet. */

	FCTXTRACE("timeout");

	if (event->type == ISC_TIMEREVENT_LIFE) {
		fctx_done(fctx, DNS_R_TIMEDOUT);
	} else {
		/*
		 * We could cancel the running queries here, or we could let
		 * them keep going.  Right now we choose the latter...
		 */
Bob Halley's avatar
Bob Halley committed
896
		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
Bob Halley's avatar
Bob Halley committed
897
		fctx_try(fctx);
Bob Halley's avatar
Bob Halley committed
898
899
900
901
902
	}

	isc_event_free(&event);
}

Bob Halley's avatar
Bob Halley committed
903
904
905
static void
fctx_shutdown(isc_task_t *task, isc_event_t *event) {
	fetchctx_t *fctx = event->arg;
Bob Halley's avatar
Bob Halley committed
906
	isc_boolean_t bucket_empty = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
907
908
909
910
911
912
913
914
915
916
917
	dns_resolver_t *res;
	unsigned int bucketnum;

	REQUIRE(VALID_FCTX(fctx));

	res = fctx->res;
	bucketnum = fctx->bucketnum;
	(void)task;	/* Keep compiler quiet. */
	
	FCTXTRACE("shutdown");

Bob Halley's avatar
Bob Halley committed
918
919
	fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;

Bob Halley's avatar
Bob Halley committed
920
921
922
923
	LOCK(&res->buckets[bucketnum].lock);
	
	INSIST(fctx->state == fetchstate_active ||
	       fctx->state == fetchstate_done);
Bob Halley's avatar
Bob Halley committed
924
	INSIST(fctx->want_shutdown);
Bob Halley's avatar
Bob Halley committed
925

Bob Halley's avatar
Bob Halley committed
926
927
928
929
930
931
932
933
	if (fctx->state != fetchstate_done) {
		fctx_stopeverything(fctx);
		fctx->state = fetchstate_done;
		fctx_sendevents(fctx, ISC_R_CANCELED);
	}

	if (fctx->references == 0 && fctx->pending == 0)
		bucket_empty = fctx_destroy(fctx);
Bob Halley's avatar
Bob Halley committed
934
935
936

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

Bob Halley's avatar
Bob Halley committed
937
	if (bucket_empty)
Bob Halley's avatar
Bob Halley committed
938
		empty_bucket(res);
Bob Halley's avatar
Bob Halley committed
939
940
}

Bob Halley's avatar
Bob Halley committed
941
942
943
static void
fctx_start(isc_task_t *task, isc_event_t *event) {
	fetchctx_t *fctx = event->arg;
Bob Halley's avatar
Bob Halley committed
944
	isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
945
	dns_resolver_t *res;
Bob Halley's avatar
Bob Halley committed
946
	unsigned int bucketnum;
Bob Halley's avatar
Bob Halley committed
947
948
949
950

	REQUIRE(VALID_FCTX(fctx));

	res = fctx->res;
Bob Halley's avatar
Bob Halley committed
951
	bucketnum = fctx->bucketnum;
Bob Halley's avatar
Bob Halley committed
952
953
954
955
	(void)task;	/* Keep compiler quiet. */

	FCTXTRACE("start");

Bob Halley's avatar
Bob Halley committed
956
	LOCK(&res->buckets[bucketnum].lock);
Bob Halley's avatar
Bob Halley committed
957

Bob Halley's avatar
Bob Halley committed
958
	INSIST(fctx->state == fetchstate_init);
Bob Halley's avatar
Bob Halley committed
959
	if (fctx->want_shutdown) {
Bob Halley's avatar
Bob Halley committed
960
		/*
Bob Halley's avatar
Bob Halley committed
961
962
963
		 * We haven't started this fctx yet, and we've been requested
		 * to shut it down.
		 *
Bob Halley's avatar
Bob Halley committed
964
965
966
		 * The events list should be empty, so we INSIST on it.
		 */
		INSIST(ISC_LIST_EMPTY(fctx->events));
Bob Halley's avatar
Bob Halley committed
967
		bucket_empty = fctx_destroy(fctx);
Bob Halley's avatar
Bob Halley committed
968
969
970
971
972
973
974
975
976
977
978
979
980
		done = ISC_TRUE;
	} else {
		/*
		 * Normal fctx startup.
		 */
		fctx->state = fetchstate_active;
		/*
		 * Reset the control event for later use in shutting down
		 * the fctx.
		 */
		ISC_EVENT_INIT(event, sizeof *event, 0, NULL,
			       DNS_EVENT_FETCHCONTROL, fctx_shutdown, fctx,
			       (void *)fctx_shutdown, NULL, NULL);
Bob Halley's avatar
Bob Halley committed
981
982
	}

Bob Halley's avatar
Bob Halley committed
983
	UNLOCK(&res->buckets[bucketnum].lock);
Bob Halley's avatar
Bob Halley committed
984

Bob Halley's avatar
Bob Halley committed
985
986
987
988
989
	if (!done) {
		/*
		 * All is well.  Start working on the fetch.
		 */
		fctx_try(fctx);
Bob Halley's avatar
Bob Halley committed
990
991
	} else if (bucket_empty)
		empty_bucket(res);
Bob Halley's avatar
Bob Halley committed
992
993
994
995
996
997
}

/*
 * Fetch Creation, Joining, and Cancelation.
 */

Bob Halley's avatar
Bob Halley committed
998
static inline isc_result_t
Bob Halley's avatar
Bob Halley committed
999
fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_taskaction_t action,
Bob Halley's avatar
Bob Halley committed
1000
1001
	  void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
	  dns_fetch_t *fetch)
Bob Halley's avatar
Bob Halley committed
1002
1003
{
	isc_task_t *clone;
Bob Halley's avatar
Bob Halley committed
1004
	dns_fetchevent_t *event;
Bob Halley's avatar
Bob Halley committed
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014

	FCTXTRACE("join");

	/*
	 * We store the task we're going to send this event to in the
	 * sender field.  We'll make the fetch the sender when we actually
	 * send the event.
	 */
	clone = NULL;
	isc_task_attach(task, &clone);
Bob Halley's avatar
Bob Halley committed
1015
	event = (dns_fetchevent_t *)
Bob Halley's avatar
Bob Halley committed
1016
1017
1018
1019
		isc_event_allocate(fctx->res->mctx, clone,
				   DNS_EVENT_FETCHDONE,
				   action, arg, sizeof *event);
	if (event == NULL)
Bob Halley's avatar
Bob Halley committed
1020
1021
1022
1023
1024
1025
1026
		return (ISC_R_NOMEMORY);
	event->result = DNS_R_SERVFAIL;
	event->qtype = fctx->type;
	event->db = NULL;
	event->node = NULL;
	event->rdataset = rdataset;
	event->sigrdataset = sigrdataset;
Bob Halley's avatar
Bob Halley committed
1027
	event->fetch = fetch;
Bob Halley's avatar
Bob Halley committed
1028
	dns_fixedname_init(&event->foundname);
Bob Halley's avatar
Bob Halley committed
1029
1030
1031
1032
1033
1034
1035
	ISC_LIST_APPEND(fctx->events, event, link);

	fctx->references++;

	fetch->magic = DNS_FETCH_MAGIC;
	fetch->private = fctx;
	
Bob Halley's avatar
Bob Halley committed
1036
	return (ISC_R_SUCCESS);
Bob Halley's avatar
Bob Halley committed
1037
1038
}

Bob Halley's avatar
Bob Halley committed
1039
static isc_result_t
Bob Halley's avatar
Bob Halley committed
1040
fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
Bob Halley's avatar
Bob Halley committed
1041
	    dns_name_t *domain, dns_rdataset_t *nameservers,
Bob Halley's avatar
Bob Halley committed
1042
	    unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp)
Bob Halley's avatar
Bob Halley committed
1043
1044
{
	fetchctx_t *fctx;
Bob Halley's avatar
Bob Halley committed
1045
	isc_result_t result = ISC_R_SUCCESS;
Bob Halley's avatar
Bob Halley committed
1046
1047
1048
	isc_result_t iresult;
	isc_interval_t interval;

Bob Halley's avatar
Bob Halley committed
1049
1050
1051
1052
1053
	/*
	 * Caller must be holding the lock for bucket number 'bucketnum'.
	 */
	REQUIRE(fctxp != NULL && *fctxp == NULL);

Bob Halley's avatar
Bob Halley committed
1054
1055
	fctx = isc_mem_get(res->mctx, sizeof *fctx);
	if (fctx == NULL)
Bob Halley's avatar
Bob Halley committed
1056
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
Bob Halley committed
1057
	FCTXTRACE("create");
Bob Halley's avatar
Bob Halley committed
1058
1059
	dns_name_init(&fctx->name, NULL);
	result = dns_name_dup(name, res->mctx, &fctx->name);
Bob Halley's avatar
Bob Halley committed
1060
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
1061
		goto cleanup_fetch;
Bob Halley's avatar
Bob Halley committed
1062
1063
1064
1065
	dns_name_init(&fctx->domain, NULL);
	dns_rdataset_init(&fctx->nameservers);
	if (domain != NULL) {
		result = dns_name_dup(domain, res->mctx, &fctx->domain);
Bob Halley's avatar
Bob Halley committed
1066
		if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
1067
1068
1069
			goto cleanup_name;
		dns_rdataset_clone(nameservers, &fctx->nameservers);
	}
Bob Halley's avatar
Bob Halley committed
1070
1071
	fctx->type = type;
	fctx->options = options;
Bob Halley's avatar
Bob Halley committed
1072
1073
1074
1075
1076
	/*
	 * Note!  We do not attach to the task.  We are relying on the
	 * resolver to ensure that this task doesn't go away while we are
	 * using it.
	 */
Bob Halley's avatar
Bob Halley committed
1077
1078
	fctx->res = res;
	fctx->references = 0;
Bob Halley's avatar
Bob Halley committed
1079
	fctx->bucketnum = bucketnum;
Bob Halley's avatar
Bob Halley committed
1080
	fctx->state = fetchstate_init;
Bob Halley's avatar
Bob Halley committed
1081
	fctx->want_shutdown = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
1082
	ISC_LIST_INIT(fctx->queries);
Bob Halley's avatar
Bob Halley committed
1083
1084
1085
	ISC_LIST_INIT(fctx->finds);
	fctx->find = NULL;
	fctx->pending = 0;
Bob Halley's avatar
Bob Halley committed
1086
	fctx->attributes = 0;
Bob Halley's avatar
Bob Halley committed
1087

Bob Halley's avatar
Bob Halley committed
1088
	fctx->qmessage = NULL;
Bob Halley's avatar
Bob Halley committed
1089
1090
1091
	result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTRENDER,
				    &fctx->qmessage);
				    
Bob Halley's avatar
Bob Halley committed
1092
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
1093
		goto cleanup_domain;
Bob Halley's avatar
Bob Halley committed
1094

Bob Halley's avatar
Bob Halley committed
1095
	fctx->rmessage = NULL;
Bob Halley's avatar
Bob Halley committed
1096
1097
1098
	result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTPARSE,
				    &fctx->rmessage);
				    
Bob Halley's avatar
Bob Halley committed
1099
	if (result != ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
1100
1101
		goto cleanup_qmessage;

Bob Halley's avatar
Bob Halley committed
1102
1103
1104
	/*
	 * Compute an expiration time for the entire fetch.
	 */
Bob Halley's avatar
Bob Halley committed
1105
	isc_interval_set(&interval, 30, 0);		/* XXXRTH constant */
Bob Halley's avatar
Bob Halley committed
1106
1107
1108
	iresult = isc_time_nowplusinterval(&fctx->expires, &interval);
	if (iresult != ISC_R_SUCCESS) {
		UNEXPECTED_ERROR(__FILE__, __LINE__,