query.c 101 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 1999-2001  Internet Software Consortium.
3
 *
Bob Halley's avatar
add  
Bob Halley committed
4
5
6
 * 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.
7
 *
8
9
10
11
12
13
14
15
 * 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
add  
Bob Halley committed
16
17
 */

18
/* $Id: query.c,v 1.191 2001/03/20 00:00:43 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
19

Bob Halley's avatar
add  
Bob Halley committed
20
21
#include <config.h>

Brian Wellington's avatar
Brian Wellington committed
22
23
#include <string.h>

Bob Halley's avatar
add  
Bob Halley committed
24
#include <isc/mem.h>
Michael Graff's avatar
Michael Graff committed
25
#include <isc/util.h>
Bob Halley's avatar
add  
Bob Halley committed
26

27
#include <dns/adb.h>
28
#include <dns/byaddr.h>
Bob Halley's avatar
Bob Halley committed
29
#include <dns/db.h>
Bob Halley's avatar
add  
Bob Halley committed
30
#include <dns/events.h>
Bob Halley's avatar
Bob Halley committed
31
#include <dns/message.h>
32
#ifdef DNS_OPT_NEWCODES
33
#include <dns/opt.h>
34
#endif /* DNS_OPT_NEWCODES */
Bob Halley's avatar
Bob Halley committed
35
#include <dns/rdata.h>
36
#include <dns/rdataclass.h>
37
#include <dns/rdatalist.h>
Bob Halley's avatar
Bob Halley committed
38
39
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
40
#include <dns/rdatastruct.h>
41
#include <dns/rdatatype.h>
42
#include <dns/resolver.h>
43
#include <dns/result.h>
44
#include <dns/stats.h>
Brian Wellington's avatar
Brian Wellington committed
45
#include <dns/tkey.h>
46
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
47
48
#include <dns/zone.h>
#include <dns/zt.h>
Bob Halley's avatar
add  
Bob Halley committed
49
50

#include <named/client.h>
51
52
#include <named/log.h>
#include <named/server.h>
53
#include <named/sortlist.h>
54
#include <named/xfrout.h>
55

Bob Halley's avatar
Bob Halley committed
56
57
#define PARTIALANSWER(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_PARTIALANSWER) != 0)
Bob Halley's avatar
Bob Halley committed
58
59
60
61
#define USECACHE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEOK) != 0)
#define RECURSIONOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSIONOK) != 0)
62
63
#define RECURSING(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSING) != 0)
Bob Halley's avatar
Bob Halley committed
64
65
#define CACHEGLUEOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEGLUEOK) != 0)
66
67
#define WANTRECURSION(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_WANTRECURSION) != 0)
68
69
#define WANTDNSSEC(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_WANTDNSSEC) != 0)
70
71
72
73
#define NOAUTHORITY(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOAUTHORITY) != 0)
#define NOADDITIONAL(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOADDITIONAL) != 0)
Bob Halley's avatar
Bob Halley committed
74

75
76
77
78
#if 0
#define CTRACE(m)       isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_QUERY, \
Brian Wellington's avatar
Brian Wellington committed
79
80
				      ISC_LOG_DEBUG(3), \
				      "client %p: %s", client, (m))
81
82
83
#define QTRACE(m)       isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_QUERY, \
Brian Wellington's avatar
Brian Wellington committed
84
85
				      ISC_LOG_DEBUG(3), \
				      "query %p: %s", query, (m))
86
87
88
89
90
#else
#define CTRACE(m) ((void)m)
#define QTRACE(m) ((void)m)
#endif

91
92
93
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U

94
95
static unsigned char ip6int_ndata[] = "\003ip6\003int";
static unsigned char ip6int_offsets[] = { 0, 4, 8 };
96
97
98
99
100
101
102
103
104
105

static dns_name_t ip6int_name = {
	DNS_NAME_MAGIC,
	ip6int_ndata, 9, 3,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	ip6int_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
};

Bob Halley's avatar
Bob Halley committed
106
107
static isc_result_t
query_simplefind(void *arg, dns_name_t *name, dns_rdatatype_t type,
Bob Halley's avatar
Bob Halley committed
108
		 isc_stdtime_t now,
Bob Halley's avatar
Bob Halley committed
109
110
111
112
113
114
		 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);

static inline void
query_adda6rrset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset,
		      dns_rdataset_t *sigrdataset);

115
static void
116
query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
117

118
119
120
121
122
123
124
125
126
127
128
129
130
static void
synth_fwd_start(ns_client_t *client);

static void
synth_fwd_startfind(ns_client_t *client);

static void
synth_fwd_respond(ns_client_t *client, dns_adbfind_t *find);

static void
synth_fwd_finddone(isc_task_t *task, isc_event_t *ev);

static void
131
132
133
134
135
136
137
138
139
140
141
142
143
synth_finish(ns_client_t *client, isc_result_t result);

static void
synth_rev_start(ns_client_t *client);

static void
synth_rev_byaddrdone_arpa(isc_task_t *task, isc_event_t *event);

static void
synth_rev_byaddrdone_int(isc_task_t *task, isc_event_t *event);

static void
synth_rev_respond(ns_client_t *client, dns_byaddrevent_t *bevent);
144

145
146
147
/*
 * Increment query statistics counters.
 */
148
149
150
static inline void
count_query(dns_zone_t *zone, isc_boolean_t is_zone,
	    dns_statscounter_t counter)
151
{
152
	REQUIRE(counter < DNS_STATS_NCOUNTERS);
153
154
155
156
157
158
159
160
161

	ns_g_server->querystats[counter]++;

	if (is_zone && zone != NULL) {
		isc_uint64_t *zonestats = dns_zone_getstatscounters(zone);
		if (zonestats != NULL)
			zonestats[counter]++;
	}
}
162
163
164
165
166
167
168
169
170
171

static inline void
query_maybeputqname(ns_client_t *client) {
	if (client->query.restarts > 0) {
		/*
		 * client->query.qname was dynamically allocated.
		 */
		dns_message_puttempname(client->message,
					&client->query.qname);
		client->query.qname = NULL;
172
	}
173
}
Bob Halley's avatar
Bob Halley committed
174

Bob Halley's avatar
Bob Halley committed
175
static inline void
176
query_reset(ns_client_t *client, isc_boolean_t everything) {
177
	isc_buffer_t *dbuf, *dbuf_next;
178
179
180
	ns_dbversion_t *dbversion, *dbversion_next;
	unsigned int i;

181
182
183
	/*
	 * Reset the query state of a client to its default state.
	 */
184

185
	/*
186
	 * Cancel the fetch if it's running.
187
188
	 */
	if (client->query.fetch != NULL) {
Bob Halley's avatar
Bob Halley committed
189
		dns_resolver_cancelfetch(client->query.fetch);
190

191
		client->query.fetch = NULL;
192
193
	}

194
195
196
197
198
199
200
201
202
203
	/*
	 * Cleanup any active versions.
	 */
	for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
	     dbversion != NULL;
	     dbversion = dbversion_next) {
		dbversion_next = ISC_LIST_NEXT(dbversion, link);
		dns_db_closeversion(dbversion->db, &dbversion->version,
				    ISC_FALSE);
		dns_db_detach(&dbversion->db);
204
		ISC_LIST_INITANDAPPEND(client->query.freeversions,
205
				      dbversion, link);
206
207
208
	}
	ISC_LIST_INIT(client->query.activeversions);

Michael Graff's avatar
   
Michael Graff committed
209
210
211
	if (client->query.authdb != NULL)
		dns_db_detach(&client->query.authdb);

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
	/*
	 * Clean up free versions.
	 */
	for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0;
	     dbversion != NULL;
	     dbversion = dbversion_next, i++) {
		dbversion_next = ISC_LIST_NEXT(dbversion, link);
		/*
		 * If we're not freeing everything, we keep the first three
		 * dbversions structures around.
		 */
		if (i > 3 || everything) {
			ISC_LIST_UNLINK(client->query.freeversions, dbversion,
					link);
			isc_mem_put(client->mctx, dbversion,
				    sizeof *dbversion);
		}
	}
Bob Halley's avatar
Bob Halley committed
230
231
232
233
234
235
236

	for (dbuf = ISC_LIST_HEAD(client->query.namebufs);
	     dbuf != NULL;
	     dbuf = dbuf_next) {
		dbuf_next = ISC_LIST_NEXT(dbuf, link);
		if (dbuf_next != NULL || everything) {
			ISC_LIST_UNLINK(client->query.namebufs, dbuf, link);
237
			isc_buffer_free(&dbuf);
Bob Halley's avatar
Bob Halley committed
238
239
		}
	}
Bob Halley's avatar
Bob Halley committed
240

241
242
	query_maybeputqname(client);

243
	client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
244
				    NS_QUERYATTR_CACHEOK);
245
	client->query.restarts = 0;
246
	client->query.timerset = ISC_FALSE;
247
	client->query.origqname = NULL;
248
	client->query.qname = NULL;
249
	client->query.dboptions = 0;
250
	client->query.fetchoptions = 0;
251
	client->query.gluedb = NULL;
Michael Graff's avatar
   
Michael Graff committed
252
	client->query.authdb = NULL;
Bob Halley's avatar
Bob Halley committed
253
254
255
}

static void
256
query_next(ns_client_t *client) {
257
	query_reset(client, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
258
259
}

260
261
262
void
ns_query_free(ns_client_t *client) {
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
263
264
265
266
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
267
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
268
269
	isc_result_t result;

270
	CTRACE("query_newnamebuf");
271
272
273
274
	/*
	 * Allocate a name buffer.
	 */

Bob Halley's avatar
Bob Halley committed
275
	dbuf = NULL;
276
	result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
277
278
	if (result != ISC_R_SUCCESS) {
		CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
Bob Halley's avatar
Bob Halley committed
279
		return (result);
280
	}
Bob Halley's avatar
Bob Halley committed
281
282
	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);

283
	CTRACE("query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
284
285
286
	return (ISC_R_SUCCESS);
}

287
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
288
query_getnamebuf(ns_client_t *client) {
289
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
290
291
292
	isc_result_t result;
	isc_region_t r;

293
	CTRACE("query_getnamebuf");
294
295
296
297
298
	/*
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

Bob Halley's avatar
Bob Halley committed
299
300
	if (ISC_LIST_EMPTY(client->query.namebufs)) {
		result = query_newnamebuf(client);
301
302
		if (result != ISC_R_SUCCESS) {
		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
303
			return (NULL);
304
		}
Bob Halley's avatar
Bob Halley committed
305
306
307
308
	}

	dbuf = ISC_LIST_TAIL(client->query.namebufs);
	INSIST(dbuf != NULL);
309
	isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
310
311
	if (r.length < 255) {
		result = query_newnamebuf(client);
312
313
		if (result != ISC_R_SUCCESS) {
		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
314
			return (NULL);
315
316

		}
Bob Halley's avatar
Bob Halley committed
317
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
318
		isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
319
320
		INSIST(r.length >= 255);
	}
321
	CTRACE("query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
322
323
324
	return (dbuf);
}

325
static inline void
326
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
327
328
	isc_region_t r;

329
	CTRACE("query_keepname");
330
331
332
333
334
335
336
337
	/*
	 * 'name' is using space in 'dbuf', but 'dbuf' has not yet been
	 * adjusted to take account of that.  We do the adjustment.
	 */

	REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0);

	dns_name_toregion(name, &r);
338
	isc_buffer_add(dbuf, r.length);
339
340
341
342
343
344
345
346
	dns_name_setbuffer(name, NULL);
	client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
}

static inline void
query_releasename(ns_client_t *client, dns_name_t **namep) {
	dns_name_t *name = *namep;

347
348
349
350
351
352
	/*
	 * 'name' is no longer needed.  Return it to our pool of temporary
	 * names.  If it is using a name buffer, relinquish its exclusive
	 * rights on the buffer.
	 */

353
	CTRACE("query_releasename");
354
355
356
357
358
	if (dns_name_hasbuffer(name)) {
		INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED)
		       != 0);
		client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
	}
Bob Halley's avatar
Bob Halley committed
359
	dns_message_puttempname(client->message, namep);
360
	CTRACE("query_releasename: done");
361
362
363
}

static inline dns_name_t *
364
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
365
366
367
368
369
370
371
372
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

	REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0);

373
	CTRACE("query_newname");
Bob Halley's avatar
Bob Halley committed
374
375
	name = NULL;
	result = dns_message_gettempname(client->message, &name);
376
377
	if (result != ISC_R_SUCCESS) {
		CTRACE("query_newname: dns_message_gettempname failed: done");
Bob Halley's avatar
Bob Halley committed
378
		return (NULL);
379
	}
380
381
	isc_buffer_availableregion(dbuf, &r);
	isc_buffer_init(nbuf, r.base, r.length);
382
383
384
385
	dns_name_init(name, NULL);
	dns_name_setbuffer(name, nbuf);
	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;

386
	CTRACE("query_newname: done");
387
388
389
390
391
392
393
	return (name);
}

static inline dns_rdataset_t *
query_newrdataset(ns_client_t *client) {
	dns_rdataset_t *rdataset;
	isc_result_t result;
394

395
	CTRACE("query_newrdataset");
Bob Halley's avatar
Bob Halley committed
396
397
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
398
	if (result != ISC_R_SUCCESS) {
David Lawrence's avatar
David Lawrence committed
399
400
	  CTRACE("query_newrdataset: "
		 "dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
401
		return (NULL);
402
	}
403
404
	dns_rdataset_init(rdataset);

405
	CTRACE("query_newrdataset: done");
406
407
408
	return (rdataset);
}

Bob Halley's avatar
Bob Halley committed
409
410
411
412
static inline void
query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
	dns_rdataset_t *rdataset = *rdatasetp;

413
	CTRACE("query_putrdataset");
Bob Halley's avatar
Bob Halley committed
414
	if (rdataset != NULL) {
415
		if (dns_rdataset_isassociated(rdataset))
Bob Halley's avatar
Bob Halley committed
416
417
418
			dns_rdataset_disassociate(rdataset);
		dns_message_puttemprdataset(client->message, rdatasetp);
	}
419
	CTRACE("query_putrdataset: done");
Bob Halley's avatar
Bob Halley committed
420
421
}

422

423
424
425
426
427
428
429
430
431
432
static inline isc_result_t
query_newdbversion(ns_client_t *client, unsigned int n) {
	unsigned int i;
	ns_dbversion_t *dbversion;

	for (i = 0; i < n; i++) {
		dbversion = isc_mem_get(client->mctx, sizeof *dbversion);
		if (dbversion != NULL) {
			dbversion->db = NULL;
			dbversion->version = NULL;
433
			ISC_LIST_INITANDAPPEND(client->query.freeversions,
434
					      dbversion, link);
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
		} else {
			/*
			 * We only return ISC_R_NOMEMORY if we couldn't
			 * allocate anything.
			 */
			if (i == 0)
				return (ISC_R_NOMEMORY);
			else
				return (ISC_R_SUCCESS);
		}
	}

	return (ISC_R_SUCCESS);
}

static inline ns_dbversion_t *
query_getdbversion(ns_client_t *client) {
	isc_result_t result;
	ns_dbversion_t *dbversion;

	if (ISC_LIST_EMPTY(client->query.freeversions)) {
		result = query_newdbversion(client, 1);
		if (result != ISC_R_SUCCESS)
			return (NULL);
	}
	dbversion = ISC_LIST_HEAD(client->query.freeversions);
	INSIST(dbversion != NULL);
	ISC_LIST_UNLINK(client->query.freeversions, dbversion, link);
463

464
465
466
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
467
468
isc_result_t
ns_query_init(ns_client_t *client) {
469
470
	isc_result_t result;

Bob Halley's avatar
Bob Halley committed
471
	ISC_LIST_INIT(client->query.namebufs);
472
473
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
474
	client->query.restarts = 0;
475
	client->query.timerset = ISC_FALSE;
476
477
	client->query.qname = NULL;
	client->query.fetch = NULL;
Michael Graff's avatar
   
Michael Graff committed
478
	client->query.authdb = NULL;
479
	query_reset(client, ISC_FALSE);
480
481
482
	result = query_newdbversion(client, 3);
	if (result != ISC_R_SUCCESS)
		return (result);
Bob Halley's avatar
Bob Halley committed
483
484
	dns_a6_init(&client->query.a6ctx, query_simplefind, query_adda6rrset,
		    NULL, NULL, client);
Bob Halley's avatar
Bob Halley committed
485
486
487
	return (query_newnamebuf(client));
}

488
489
490
491
static inline ns_dbversion_t *
query_findversion(ns_client_t *client, dns_db_t *db,
		  isc_boolean_t *newzonep)
{
492
493
494
495
496
497
498
499
500
501
502
503
	ns_dbversion_t *dbversion;

	/*
	 * We may already have done a query related to this
	 * database.  If so, we must be sure to make subsequent
	 * queries from the same version.
	 */
	for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
	     dbversion != NULL;
	     dbversion = ISC_LIST_NEXT(dbversion, link)) {
		if (dbversion->db == db)
			break;
Michael Graff's avatar
   
Michael Graff committed
504
505
	}

506
507
508
509
510
511
512
513
514
515
	if (dbversion == NULL) {
		/*
		 * This is a new zone for this query.  Add it to
		 * the active list.
		 */
		dbversion = query_getdbversion(client);
		if (dbversion == NULL)
			return (NULL);
		dns_db_attach(db, &dbversion->db);
		dns_db_currentversion(db, &dbversion->version);
516
		dbversion->queryok = ISC_FALSE;
517
518
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
519
520
521
		*newzonep = ISC_TRUE;
	} else
		*newzonep = ISC_FALSE;
522

523
524
525
526
	return (dbversion);
}

static inline isc_result_t
527
528
query_getzonedb(ns_client_t *client, dns_name_t *name, unsigned int options,
		dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp)
529
530
531
532
533
{
	isc_result_t result;
	isc_boolean_t check_acl, new_zone;
	dns_acl_t *queryacl;
	ns_dbversion_t *dbversion;
534
	unsigned int ztoptions;
535
536
537
538
539
	dns_zone_t *zone = NULL;
	dns_db_t *db = NULL;

	REQUIRE(zonep != NULL && *zonep == NULL);
	REQUIRE(dbp != NULL && *dbp == NULL);
540
541

	/*
542
	 * Find a zone database to answer the query.
543
	 */
544
545
	ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
		DNS_ZTFIND_NOEXACT : 0;
546

547
	result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
548
			     &zone);
549
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
550
		result = dns_zone_getdb(zone, &db);
551

552
	if (result != ISC_R_SUCCESS)		
553
		goto fail;
554

Michael Graff's avatar
   
Michael Graff committed
555
556
557
558
559
560
561
562
563
564
565
566
567
568
	/*
	 * If this is the first time we are called (that is, looking up
	 * the actual name in the query section) remember this database.
	 *
	 * If authdb is non-NULL, we have been here before, and the
	 * found database is always returned.
	 *
	 * This limits our searching to the zone where the first name
	 * (the query target) is found.  This prevents following CNAMES
	 * or DNAMES into other zones and prevents returning additional
	 * data from other zones.
	 */
	if (!client->view->additionalfromauth) {
		if (client->query.authdb != NULL) {
569
570
			if (db != client->query.authdb)
				goto refuse;
Michael Graff's avatar
   
Michael Graff committed
571
		} else {
572
			dns_db_attach(db, &client->query.authdb);
Michael Graff's avatar
   
Michael Graff committed
573
574
575
		}
	}

576
	/*
577
	 * If the zone has an ACL, we'll check it, otherwise
578
579
580
	 * we use the view's "allow-query" ACL.  Each ACL is only checked
	 * once per query.
	 *
581
	 * Also, get the database version to use.
582
583
584
585
	 */

	check_acl = ISC_TRUE;	/* Keep compiler happy. */
	queryacl = NULL;
586
587
588
589

	/*
	 * Get the current version of this database.
	 */
590
591
592
593
594
	dbversion = query_findversion(client, db, &new_zone);
	if (dbversion == NULL) {
		result = DNS_R_SERVFAIL;
		goto fail;
	}
595
596
597
	if (new_zone) {
		check_acl = ISC_TRUE;
	} else if (!dbversion->queryok) {
598
		goto refuse;
599
600
601
	} else {
		check_acl = ISC_FALSE;
	}
602

603
	queryacl = dns_zone_getqueryacl(zone);
604
605
606
607
608
609
610
611
612
613
614
615
616
	if (queryacl == NULL) {
		queryacl = client->view->queryacl;
		if ((client->query.attributes &
		     NS_QUERYATTR_QUERYOKVALID) != 0) {
			/*
			 * We've evaluated the view's queryacl already.  If
			 * NS_QUERYATTR_QUERYOK is set, then the client is
			 * allowed to make queries, otherwise the query should
			 * be refused.
			 */
			check_acl = ISC_FALSE;
			if ((client->query.attributes &
			     NS_QUERYATTR_QUERYOK) == 0)
617
				goto refuse;
618
619
620
621
622
623
624
625
626
		} else {
			/*
			 * We haven't evaluated the view's queryacl yet.
			 */
			check_acl = ISC_TRUE;
		}
	}

	if (check_acl) {
627
		isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
628
629
630
631
632
633
		char msg[DNS_NAME_FORMATSIZE + DNS_RDATACLASS_FORMATSIZE
			 + sizeof "query '/'"];
		
		ns_client_aclmsg("query", name, client->view->rdclass,
				 msg, sizeof(msg));
		result = ns_client_checkacl(client, msg, queryacl,
634
					    ISC_TRUE,
635
					    log ? ISC_LOG_INFO : ISC_LOG_DEBUG(3));
636

637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
		if (queryacl == client->view->queryacl) {
			if (result == ISC_R_SUCCESS) {
				/*
				 * We were allowed by the default
				 * "allow-query" ACL.  Remember this so we
				 * don't have to check again.
				 */
				client->query.attributes |=
					NS_QUERYATTR_QUERYOK;
			}
			/*
			 * We've now evaluated the view's query ACL, and
			 * the NS_QUERYATTR_QUERYOK attribute is now valid.
			 */
			client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
Michael Graff's avatar
   
Michael Graff committed
652
		}
653
654
655
656
657
658

		if (result != ISC_R_SUCCESS)
			goto refuse;
	}

	/* Approved. */
659

660
661
662
663
	/*
	 * Remember the result of the ACL check so we
	 * don't have to check again.
	 */
664
665
666
667
668
	dbversion->queryok = ISC_TRUE;

	/* Transfer ownership. */
	*zonep = zone;
	*dbp = db;
669
	*versionp = dbversion->version;
670
671
672
673
674
675
676
677
678
679

	return (ISC_R_SUCCESS);

 refuse:
	result = DNS_R_REFUSED;
 fail:
	if (zone != NULL)
		dns_zone_detach(&zone);
	if (db != NULL)
		dns_db_detach(&db);
Michael Graff's avatar
   
Michael Graff committed
680

681
	return (result);
682
683
}

684
685
686
687
688
static inline isc_result_t
query_getcachedb(ns_client_t *client, dns_db_t **dbp, unsigned int options)
{
	isc_result_t result;
	isc_boolean_t check_acl;
689
690
691
	dns_db_t *db = NULL;

	REQUIRE(dbp != NULL && *dbp == NULL);
692
693
694

	/*
	 * Find a cache database to answer the query.
695
	 * This may fail with DNS_R_REFUSED if the client
696
697
698
699
700
	 * is not allowed to use the cache.
	 */

	if (!USECACHE(client))
		return (DNS_R_REFUSED);
701
	dns_db_attach(client->view->cachedb, &db);
702

703
704
705
706
707
708
709
710
711
712
713
	if ((client->query.attributes &
	     NS_QUERYATTR_QUERYOKVALID) != 0) {
		/*
		 * We've evaluated the view's queryacl already.  If
		 * NS_QUERYATTR_QUERYOK is set, then the client is
		 * allowed to make queries, otherwise the query should
		 * be refused.
		 */
		check_acl = ISC_FALSE;
		if ((client->query.attributes &
		     NS_QUERYATTR_QUERYOK) == 0)
714
			goto refuse;
715
716
717
718
719
720
	} else {
		/*
		 * We haven't evaluated the view's queryacl yet.
		 */
		check_acl = ISC_TRUE;
	}
721

722
723
	if (check_acl) {
		isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
724
725
		
		result = ns_client_checkacl(client, "query (cache)",
726
727
					    client->view->queryacl,
					    ISC_TRUE,
728
729
					    log ? ISC_LOG_INFO :
						  ISC_LOG_DEBUG(3));
730
731
732
733
734
735
736
737
738
739
740
741
742
743
		if (result == ISC_R_SUCCESS) {
			/*
			 * We were allowed by the default
			 * "allow-query" ACL.  Remember this so we
			 * don't have to check again.
			 */
			client->query.attributes |=
				NS_QUERYATTR_QUERYOK;
		}
		/*
		 * We've now evaluated the view's query ACL, and
		 * the NS_QUERYATTR_QUERYOK attribute is now valid.
		 */
		client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
744

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
		if (result != ISC_R_SUCCESS)
			goto refuse;
	}

	/* Approved. */

	/* Transfer ownership. */
	*dbp = db;

	return (ISC_R_SUCCESS);

 refuse:
	result = DNS_R_REFUSED;

	if (db != NULL)
		dns_db_detach(&db);
761
762
763
764
765
766
767
768
769
770
771
772

	return (result);
}


static inline isc_result_t
query_getdb(ns_client_t *client, dns_name_t *name, unsigned int options,
	    dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp,
	    isc_boolean_t *is_zonep)
{
	isc_result_t result;

773
#ifdef DNS_OPT_NEWCODES_LIVE
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
	if (client->opt_zone != NULL) {
		result = query_getzonedb(client, &(client->opt_zone->name),
					 options, zonep, dbp, versionp);
		if (result == ISC_R_SUCCESS)
			*is_zonep = ISC_TRUE;
		else
			result = DNS_R_REFUSED;
	} else {
		result = query_getzonedb(client, name, options, zonep, dbp,
					 versionp);
		if (result == ISC_R_SUCCESS) {
			*is_zonep = ISC_TRUE;
		} else if (result == ISC_R_NOTFOUND) {
			result = query_getcachedb(client, dbp, options);
			*is_zonep = ISC_FALSE;
		}
	}
791
#else /* DNS_OPT_NEWCODES_LIVE */
792
793
794
795
796
797
798
	result = query_getzonedb(client, name, options, zonep, dbp, versionp);
	if (result == ISC_R_SUCCESS) {
		*is_zonep = ISC_TRUE;
	} else if (result == ISC_R_NOTFOUND) {
		result = query_getcachedb(client, dbp, options);
		*is_zonep = ISC_FALSE;
	}
799
#endif /* DNS_OPT_NEWCODES_LIVE */
800
801
802
	return (result);
}

Bob Halley's avatar
Bob Halley committed
803
804
static isc_result_t
query_simplefind(void *arg, dns_name_t *name, dns_rdatatype_t type,
Bob Halley's avatar
Bob Halley committed
805
		 isc_stdtime_t now,
Bob Halley's avatar
Bob Halley committed
806
807
808
809
810
811
812
		 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
{
	ns_client_t *client = arg;
	isc_result_t result;
	dns_fixedname_t foundname;
	dns_db_t *db;
	dns_dbversion_t *version;
813
	unsigned int dboptions;
Bob Halley's avatar
Bob Halley committed
814
815
	isc_boolean_t is_zone;
	dns_rdataset_t zrdataset, zsigrdataset;
Bob Halley's avatar
Bob Halley committed
816
	dns_zone_t *zone;
Bob Halley's avatar
Bob Halley committed
817
818

	REQUIRE(NS_CLIENT_VALID(client));
819
	REQUIRE(rdataset != NULL);
Bob Halley's avatar
Bob Halley committed
820

Bob Halley's avatar
Bob Halley committed
821
	dns_rdataset_init(&zrdataset);
822
823
	if (sigrdataset != NULL)
		dns_rdataset_init(&zsigrdataset);
Bob Halley's avatar
Bob Halley committed
824

Bob Halley's avatar
Bob Halley committed
825
826
827
	/*
	 * Find a database to answer the query.
	 */
Bob Halley's avatar
Bob Halley committed
828
	zone = NULL;
Bob Halley's avatar
Bob Halley committed
829
830
	db = NULL;
	version = NULL;
831
832
833
	result = query_getdb(client, name, 0, &zone, &db, &version, &is_zone);
	if (result != ISC_R_SUCCESS)
		goto cleanup;
Bob Halley's avatar
Bob Halley committed
834

Bob Halley's avatar
Bob Halley committed
835
 db_find:
Bob Halley's avatar
Bob Halley committed
836
837
838
839
	/*
	 * Now look for an answer in the database.
	 */
	dns_fixedname_init(&foundname);
840
	dboptions = client->query.dboptions;
Bob Halley's avatar
Bob Halley committed
841
	if (db == client->query.gluedb || (!is_zone && CACHEGLUEOK(client)))
842
843
		dboptions |= DNS_DBFIND_GLUEOK;
	result = dns_db_find(db, name, version, type, dboptions,
Bob Halley's avatar
Bob Halley committed
844
			     now, NULL, dns_fixedname_name(&foundname),
Bob Halley's avatar
Bob Halley committed
845
			     rdataset, sigrdataset);
Bob Halley's avatar
Bob Halley committed
846
	if (result == DNS_R_DELEGATION ||
847
	    result == ISC_R_NOTFOUND) {
848
		if (dns_rdataset_isassociated(rdataset))
Bob Halley's avatar
Bob Halley committed
849
			dns_rdataset_disassociate(rdataset);
850
851
		if (sigrdataset != NULL &&
		    dns_rdataset_isassociated(sigrdataset))
Bob Halley's avatar
Bob Halley committed
852
853
			dns_rdataset_disassociate(sigrdataset);
		if (is_zone) {
854
			count_query(zone, is_zone, dns_statscounter_referral);
Bob Halley's avatar
Bob Halley committed
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
			if (USECACHE(client)) {
				/*
				 * Either the answer is in the cache, or we
				 * don't know it.
				 */
				is_zone = ISC_FALSE;
				version = NULL;
				dns_db_detach(&db);
				dns_db_attach(client->view->cachedb, &db);
				goto db_find;
			}
		} else {
			/*
			 * We don't have the data in the cache.  If we've got
			 * glue from the zone, use it.
			 */
871
			if (dns_rdataset_isassociated(&zrdataset)) {
Bob Halley's avatar
Bob Halley committed
872
				dns_rdataset_clone(&zrdataset, rdataset);
873
874
				if (sigrdataset != NULL &&
				    dns_rdataset_isassociated(&zsigrdataset))
Bob Halley's avatar
Bob Halley committed
875
876
877
878
879
880
881
882
883
					dns_rdataset_clone(&zsigrdataset,
							   sigrdataset);
				result = ISC_R_SUCCESS;
				goto cleanup;
			}
		}
		/*
		 * We don't know the answer.
		 */
884
		result = ISC_R_NOTFOUND;
Bob Halley's avatar
Bob Halley committed
885
	} else if (result == DNS_R_GLUE) {
886
		if (USECACHE(client) && RECURSIONOK(client)) {
Bob Halley's avatar
Bob Halley committed
887
888
889
890
891
892
893
894
			/*
			 * We found an answer, but the cache may be better.
			 * Remember what we've got and go look in the cache.
			 */
			is_zone = ISC_FALSE;
			version = NULL;
			dns_rdataset_clone(rdataset, &zrdataset);
			dns_rdataset_disassociate(rdataset);
895
896
897
			if (sigrdataset != NULL &&
			    dns_rdataset_isassociated(sigrdataset))
			{
Bob Halley's avatar
Bob Halley committed
898
899
900
901
902
903
904
905
906
907
908
				dns_rdataset_clone(sigrdataset, &zsigrdataset);
				dns_rdataset_disassociate(sigrdataset);
			}
			dns_db_detach(&db);
			dns_db_attach(client->view->cachedb, &db);
			goto db_find;
		}
		/*
		 * Otherwise, the glue is the best answer.
		 */
		result = ISC_R_SUCCESS;
909
	} else if (result != ISC_R_SUCCESS) {
910
		if (dns_rdataset_isassociated(rdataset))
911
			dns_rdataset_disassociate(rdataset);
912
913
		if (sigrdataset != NULL &&
		    dns_rdataset_isassociated(sigrdataset))
914
			dns_rdataset_disassociate(sigrdataset);
915
		result = ISC_R_NOTFOUND;
916
	}
917
918
919
920
921
922
	/*
	 * If we get here, the result is ISC_R_SUCCESS, and we found the
	 * answer we were looking for in the zone.  Update the zone's
	 * query counter.
	 */
	if (result == ISC_R_SUCCESS)
923
		count_query(zone, is_zone, dns_statscounter_success);
Bob Halley's avatar
Bob Halley committed
924

Bob Halley's avatar
Bob Halley committed
925
 cleanup:
926
	if (dns_rdataset_isassociated(&zrdataset)) {
Bob Halley's avatar
Bob Halley committed
927
		dns_rdataset_disassociate(&zrdataset);
928
929
		if (sigrdataset != NULL &&
		    dns_rdataset_isassociated(&zsigrdataset))
Bob Halley's avatar
Bob Halley committed
930
931
			dns_rdataset_disassociate(&zsigrdataset);
	}
Bob Halley's avatar
Bob Halley committed
932
933
	if (db != NULL)
		dns_db_detach(&db);
Bob Halley's avatar
Bob Halley committed
934
935
	if (zone != NULL)
		dns_zone_detach(&zone);
Bob Halley's avatar
Bob Halley committed
936
937
938
939

	return (result);
}

Bob Halley's avatar
Bob Halley committed
940
941
static inline isc_boolean_t
query_isduplicate(ns_client_t *client, dns_name_t *name,
942
		  dns_rdatatype_t type, dns_name_t **mnamep)
Bob Halley's avatar
Bob Halley committed
943
944
{
	dns_section_t section;
945
	dns_name_t *mname = NULL;
946
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
947

948
949
	CTRACE("query_isduplicate");

Bob Halley's avatar
Bob Halley committed
950
951
952
	for (section = DNS_SECTION_ANSWER;
	     section <= DNS_SECTION_ADDITIONAL;
	     section++) {
953
954
955
		result = dns_message_findname(client->message, section,
					      name, type, 0, &mname, NULL);
		if (result == ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
956
957
958
			/*
			 * We've already got this RRset in the response.
			 */
959
			CTRACE("query_isduplicate: true: done");
Bob Halley's avatar
Bob Halley committed
960
			return (ISC_TRUE);
961
		} else if (result == DNS_R_NXRRSET) {
962
963
964
965
966
967
968
969
			/*
			 * The name exists, but the rdataset does not.
			 */
			if (section == DNS_SECTION_ADDITIONAL)
				break;
		} else
			RUNTIME_CHECK(result == DNS_R_NXDOMAIN);
		mname = NULL;
Bob Halley's avatar
Bob Halley committed
970
971
	}

972
	/*
Brian Wellington's avatar
typo    
Brian Wellington committed
973
	 * If the dns_name_t we're looking up is already in the message,
974
975
976
977
978
979
980
	 * we don't want to trigger the caller's name replacement logic.
	 */
	if (name == mname)
		mname = NULL;

	*mnamep = mname;

981
	CTRACE("query_isduplicate: false: done");
Bob Halley's avatar
Bob Halley committed
982
983
	return (ISC_FALSE);
}
Bob Halley's avatar
Bob Halley committed
984

985
static isc_result_t
986
query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
987
	ns_client_t *client = arg;
988
	isc_result_t result, eresult;
989
990
991
	dns_dbnode_t *node;
	dns_db_t *db;
	dns_name_t *fname, *mname;
992
	dns_rdataset_t *rdataset, *sigrdataset, *a6rdataset, *trdataset;
993
	isc_buffer_t *dbuf;
994
	isc_buffer_t b;
995
996
	dns_dbversion_t *version;
	isc_boolean_t added_something, need_addname;
Bob Halley's avatar
Bob Halley committed
997
	dns_zone_t *zone;
Bob Halley's avatar
Bob Halley committed
998
	dns_rdatatype_t type;
999
1000

	REQUIRE(NS_CLIENT_VALID(client));