query.c 231 KB
Newer Older
Bob Halley's avatar
add    
Bob Halley committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
add    
Bob Halley committed
6
7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9
10
11
12
13
14
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC 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
 */

Evan Hunt's avatar
Evan Hunt committed
18
/* $Id$ */
19
20

/*! \file */
David Lawrence's avatar
David Lawrence committed
21

Bob Halley's avatar
add    
Bob Halley committed
22
23
#include <config.h>

Brian Wellington's avatar
Brian Wellington committed
24
25
#include <string.h>

26
#include <isc/hex.h>
Bob Halley's avatar
add    
Bob Halley committed
27
#include <isc/mem.h>
28
#include <isc/serial.h>
29
#include <isc/stats.h>
Michael Graff's avatar
Michael Graff committed
30
#include <isc/util.h>
Bob Halley's avatar
add    
Bob Halley committed
31

32
#include <dns/adb.h>
33
#include <dns/byaddr.h>
34
#include <dns/cache.h>
Bob Halley's avatar
Bob Halley committed
35
#include <dns/db.h>
36
#include <dns/dlz.h>
37
#include <dns/dns64.h>
38
#include <dns/dnssec.h>
Bob Halley's avatar
add    
Bob Halley committed
39
#include <dns/events.h>
Bob Halley's avatar
Bob Halley committed
40
#include <dns/message.h>
41
#include <dns/ncache.h>
42
#include <dns/nsec3.h>
43
#include <dns/order.h>
Bob Halley's avatar
Bob Halley committed
44
#include <dns/rdata.h>
45
#include <dns/rdataclass.h>
46
#include <dns/rdatalist.h>
Bob Halley's avatar
Bob Halley committed
47
48
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
49
#include <dns/rdatastruct.h>
50
#include <dns/rdatatype.h>
51
#include <dns/resolver.h>
52
#include <dns/result.h>
53
#include <dns/stats.h>
Brian Wellington's avatar
Brian Wellington committed
54
#include <dns/tkey.h>
55
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
56
57
#include <dns/zone.h>
#include <dns/zt.h>
Bob Halley's avatar
add    
Bob Halley committed
58
59

#include <named/client.h>
60
#include <named/globals.h>
61
62
#include <named/log.h>
#include <named/server.h>
63
#include <named/sortlist.h>
64
#include <named/xfrout.h>
65

66
67
68
69
70
71
72
73
74
75
76
#if 0
/*
 * It has been recommended that DNS64 be changed to return excluded
 * AAAA addresses if DNS64 synthesis does not occur.  This minimises
 * the impact on the lookup results.  While most DNS AAAA lookups are
 * done to send IP packets to a host, not all of them are and filtering
 * excluded addresses has a negative impact on those uses.
 */
#define dns64_bis_return_excluded_addresses 1
#endif

77
/*% Partial answer? */
Bob Halley's avatar
Bob Halley committed
78
79
#define PARTIALANSWER(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_PARTIALANSWER) != 0)
80
/*% Use Cache? */
Bob Halley's avatar
Bob Halley committed
81
82
#define USECACHE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEOK) != 0)
83
/*% Recursion OK? */
Bob Halley's avatar
Bob Halley committed
84
85
#define RECURSIONOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSIONOK) != 0)
86
/*% Recursing? */
87
88
#define RECURSING(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSING) != 0)
89
/*% Cache glue ok? */
Bob Halley's avatar
Bob Halley committed
90
91
#define CACHEGLUEOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEGLUEOK) != 0)
92
/*% Want Recursion? */
93
94
#define WANTRECURSION(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_WANTRECURSION) != 0)
95
/*% Want DNSSEC? */
96
97
#define WANTDNSSEC(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTDNSSEC) != 0)
98
99
100
/*% Want WANTAD? */
#define WANTAD(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTAD) != 0)
101
102
103
104
105
106
107
#ifdef ISC_PLATFORM_USESIT
/*% Client presented a valid Source Identity Token. */
#define HAVESIT(c)		(((c)->attributes & \
				  NS_CLIENTATTR_HAVESIT) != 0)
#else
#define HAVESIT(c)		(0)
#endif
108

109
/*% No authority? */
110
111
#define NOAUTHORITY(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOAUTHORITY) != 0)
112
/*% No additional? */
113
114
#define NOADDITIONAL(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOADDITIONAL) != 0)
115
/*% Secure? */
116
117
#define SECURE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_SECURE) != 0)
118
119
120
121
122
123
/*% DNS64 A lookup? */
#define DNS64(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_DNS64) != 0)

#define DNS64EXCLUDE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_DNS64EXCLUDE) != 0)
Bob Halley's avatar
Bob Halley committed
124

125
126
127
128
/*% No QNAME Proof? */
#define NOQNAME(r)		(((r)->attributes & \
				  DNS_RDATASETATTR_NOQNAME) != 0)

129
130
131
132
#if 0
#define CTRACE(m)       isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_QUERY, \
Brian Wellington's avatar
Brian Wellington committed
133
134
				      ISC_LOG_DEBUG(3), \
				      "client %p: %s", client, (m))
135
136
137
#define QTRACE(m)       isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_QUERY, \
Brian Wellington's avatar
Brian Wellington committed
138
139
				      ISC_LOG_DEBUG(3), \
				      "query %p: %s", query, (m))
140
141
142
143
144
#else
#define CTRACE(m) ((void)m)
#define QTRACE(m) ((void)m)
#endif

145
146
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
147
#define DNS_GETDB_PARTIAL 0x04U
148
#define DNS_GETDB_IGNOREACL 0x08U
149

150
151
#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)

152
153
154
155
156
typedef struct client_additionalctx {
	ns_client_t *client;
	dns_rdataset_t *rdataset;
} client_additionalctx_t;

157
static isc_result_t
158
query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
159

160
161
162
163
static isc_boolean_t
validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
	 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);

164
165
166
167
168
169
170
static void
query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
		       dns_dbversion_t *version, ns_client_t *client,
		       dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
		       dns_name_t *fname, isc_boolean_t exact,
		       dns_name_t *found);

171
172
173
static inline void
log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);

174
175
176
static void
rpz_st_clear(ns_client_t *client);

177
/*%
178
179
 * Increment query statistics counters.
 */
180
static inline void
181
inc_stats(ns_client_t *client, isc_statscounter_t counter) {
182
	dns_zone_t *zone = client->query.authzone;
183
184
185
186
	dns_rdatatype_t qtype;
	dns_rdataset_t *rdataset;
	isc_stats_t *zonestats;
	dns_stats_t *querystats = NULL;
187

188
	isc_stats_increment(ns_g_server->nsstats, counter);
189

190
191
192
193
194
195
196
197
198
199
200
	if (zone == NULL)
		return;

	/* Do regular response type stats */
	zonestats = dns_zone_getrequeststats(zone);

	if (zonestats != NULL)
		isc_stats_increment(zonestats, counter);

	/* Do query type statistics
	 *
Evan Hunt's avatar
Evan Hunt committed
201
	 * We only increment per-type if we're using the authoritative
202
203
204
205
206
207
208
209
210
211
212
	 * answer counter, preventing double-counting.
	 */
	if (counter == dns_nsstatscounter_authans) {
		querystats = dns_zone_getrcvquerystats(zone);
		if (querystats != NULL) {
			rdataset = ISC_LIST_HEAD(client->query.qname->list);
			if (rdataset != NULL) {
				qtype = rdataset->type;
				dns_rdatatypestats_increment(querystats, qtype);
			}
		}
213
214
	}
}
215

216
217
static void
query_send(ns_client_t *client) {
218
	isc_statscounter_t counter;
219

220
221
222
223
	if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0)
		inc_stats(client, dns_nsstatscounter_nonauthans);
	else
		inc_stats(client, dns_nsstatscounter_authans);
224

225
	if (client->message->rcode == dns_rcode_noerror) {
226
227
228
		dns_section_t answer = DNS_SECTION_ANSWER;
		if (ISC_LIST_EMPTY(client->message->sections[answer])) {
			if (client->query.isreferral)
229
				counter = dns_nsstatscounter_referral;
230
			else
231
				counter = dns_nsstatscounter_nxrrset;
232
		} else
233
			counter = dns_nsstatscounter_success;
234
	} else if (client->message->rcode == dns_rcode_nxdomain)
235
		counter = dns_nsstatscounter_nxdomain;
236
	else /* We end up here in case of YXDOMAIN, and maybe others */
237
		counter = dns_nsstatscounter_failure;
238

239
240
241
242
243
	inc_stats(client, counter);
	ns_client_send(client);
}

static void
244
245
246
query_error(ns_client_t *client, isc_result_t result, int line) {
	int loglevel = ISC_LOG_DEBUG(3);

247
248
	switch (result) {
	case DNS_R_SERVFAIL:
249
		loglevel = ISC_LOG_DEBUG(1);
250
251
252
253
254
255
256
257
258
		inc_stats(client, dns_nsstatscounter_servfail);
		break;
	case DNS_R_FORMERR:
		inc_stats(client, dns_nsstatscounter_formerr);
		break;
	default:
		inc_stats(client, dns_nsstatscounter_failure);
		break;
	}
259
260
261

	log_queryerror(client, result, line, loglevel);

262
263
264
265
266
	ns_client_error(client, result);
}

static void
query_next(ns_client_t *client, isc_result_t result) {
267
	if (result == DNS_R_DUPLICATE)
268
		inc_stats(client, dns_nsstatscounter_duplicate);
269
	else if (result == DNS_R_DROP)
270
		inc_stats(client, dns_nsstatscounter_dropped);
271
	else
272
		inc_stats(client, dns_nsstatscounter_failure);
273
274
275
	ns_client_next(client, result);
}

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
static inline void
query_freefreeversions(ns_client_t *client, isc_boolean_t everything) {
	ns_dbversion_t *dbversion, *dbversion_next;
	unsigned int i;

	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));
		}
	}
}

299
300
301
302
303
304
305
306
307
308
309
void
ns_query_cancel(ns_client_t *client) {
	LOCK(&client->query.fetchlock);
	if (client->query.fetch != NULL) {
		dns_resolver_cancelfetch(client->query.fetch);

		client->query.fetch = NULL;
	}
	UNLOCK(&client->query.fetchlock);
}

310
311
312
313
314
315
316
317
318
319
320
321
322
static inline void
query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
	dns_rdataset_t *rdataset = *rdatasetp;

	CTRACE("query_putrdataset");
	if (rdataset != NULL) {
		if (dns_rdataset_isassociated(rdataset))
			dns_rdataset_disassociate(rdataset);
		dns_message_puttemprdataset(client->message, rdatasetp);
	}
	CTRACE("query_putrdataset: done");
}

Bob Halley's avatar
Bob Halley committed
323
static inline void
324
query_reset(ns_client_t *client, isc_boolean_t everything) {
325
	isc_buffer_t *dbuf, *dbuf_next;
326
327
	ns_dbversion_t *dbversion, *dbversion_next;

328
	/*%
329
330
	 * Reset the query state of a client to its default state.
	 */
331

332
	/*
333
	 * Cancel the fetch if it's running.
334
	 */
335
	ns_query_cancel(client);
336

337
338
339
340
341
342
343
344
345
346
	/*
	 * 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);
347
		ISC_LIST_INITANDAPPEND(client->query.freeversions,
348
				      dbversion, link);
349
350
351
	}
	ISC_LIST_INIT(client->query.activeversions);

Michael Graff's avatar
   
Michael Graff committed
352
353
	if (client->query.authdb != NULL)
		dns_db_detach(&client->query.authdb);
354
355
	if (client->query.authzone != NULL)
		dns_zone_detach(&client->query.authzone);
Michael Graff's avatar
   
Michael Graff committed
356

357
358
359
360
361
362
363
364
365
366
367
368
	if (client->query.dns64_aaaa != NULL)
		query_putrdataset(client, &client->query.dns64_aaaa);
	if (client->query.dns64_sigaaaa != NULL)
		query_putrdataset(client, &client->query.dns64_sigaaaa);
	if (client->query.dns64_aaaaok != NULL) {
		isc_mem_put(client->mctx, client->query.dns64_aaaaok,
			    client->query.dns64_aaaaoklen *
			    sizeof(isc_boolean_t));
		client->query.dns64_aaaaok =  NULL;
		client->query.dns64_aaaaoklen =  0;
	}

369
	query_freefreeversions(client, everything);
Bob Halley's avatar
Bob Halley committed
370
371
372
373
374
375
376

	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);
377
			isc_buffer_free(&dbuf);
Bob Halley's avatar
Bob Halley committed
378
379
		}
	}
Bob Halley's avatar
Bob Halley committed
380

381
382
383
384
385
386
387
388
	if (client->query.restarts > 0) {
		/*
		 * client->query.qname was dynamically allocated.
		 */
		dns_message_puttempname(client->message,
					&client->query.qname);
	}
	client->query.qname = NULL;
389
	client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
390
391
				    NS_QUERYATTR_CACHEOK |
				    NS_QUERYATTR_SECURE);
392
	client->query.restarts = 0;
393
	client->query.timerset = ISC_FALSE;
394
395
396
397
398
399
400
401
	if (client->query.rpz_st != NULL) {
		rpz_st_clear(client);
		if (everything) {
			isc_mem_put(client->mctx, client->query.rpz_st,
				    sizeof(*client->query.rpz_st));
			client->query.rpz_st = NULL;
		}
	}
402
	client->query.origqname = NULL;
403
	client->query.dboptions = 0;
404
	client->query.fetchoptions = 0;
405
	client->query.gluedb = NULL;
406
407
	client->query.authdbset = ISC_FALSE;
	client->query.isreferral = ISC_FALSE;
408
409
	client->query.dns64_options = 0;
	client->query.dns64_ttl = ISC_UINT32_MAX;
Bob Halley's avatar
Bob Halley committed
410
411
412
}

static void
413
query_next_callback(ns_client_t *client) {
414
	query_reset(client, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
415
416
}

417
418
419
void
ns_query_free(ns_client_t *client) {
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
420
421
422
423
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
424
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
425
426
	isc_result_t result;

427
	CTRACE("query_newnamebuf");
428
	/*%
429
430
431
	 * Allocate a name buffer.
	 */

Bob Halley's avatar
Bob Halley committed
432
	dbuf = NULL;
433
	result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
434
435
	if (result != ISC_R_SUCCESS) {
		CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
Bob Halley's avatar
Bob Halley committed
436
		return (result);
437
	}
Bob Halley's avatar
Bob Halley committed
438
439
	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);

440
	CTRACE("query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
441
442
443
	return (ISC_R_SUCCESS);
}

444
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
445
query_getnamebuf(ns_client_t *client) {
446
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
447
448
449
	isc_result_t result;
	isc_region_t r;

450
	CTRACE("query_getnamebuf");
451
	/*%
452
453
454
455
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

Bob Halley's avatar
Bob Halley committed
456
457
	if (ISC_LIST_EMPTY(client->query.namebufs)) {
		result = query_newnamebuf(client);
458
459
		if (result != ISC_R_SUCCESS) {
		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
460
			return (NULL);
461
		}
Bob Halley's avatar
Bob Halley committed
462
463
464
465
	}

	dbuf = ISC_LIST_TAIL(client->query.namebufs);
	INSIST(dbuf != NULL);
466
	isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
467
468
	if (r.length < 255) {
		result = query_newnamebuf(client);
469
470
		if (result != ISC_R_SUCCESS) {
		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
471
			return (NULL);
472
473

		}
Bob Halley's avatar
Bob Halley committed
474
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
475
		isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
476
477
		INSIST(r.length >= 255);
	}
478
	CTRACE("query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
479
480
481
	return (dbuf);
}

482
static inline void
483
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
484
485
	isc_region_t r;

486
	CTRACE("query_keepname");
487
	/*%
488
489
490
491
492
493
494
	 * '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);
495
	isc_buffer_add(dbuf, r.length);
496
497
498
499
500
501
502
503
	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;

504
	/*%
505
506
507
508
509
	 * '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.
	 */

510
	CTRACE("query_releasename");
511
512
513
514
515
	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
516
	dns_message_puttempname(client->message, namep);
517
	CTRACE("query_releasename: done");
518
519
520
}

static inline dns_name_t *
521
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
522
523
524
525
526
527
528
529
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

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

530
	CTRACE("query_newname");
Bob Halley's avatar
Bob Halley committed
531
532
	name = NULL;
	result = dns_message_gettempname(client->message, &name);
533
534
	if (result != ISC_R_SUCCESS) {
		CTRACE("query_newname: dns_message_gettempname failed: done");
Bob Halley's avatar
Bob Halley committed
535
		return (NULL);
536
	}
537
538
	isc_buffer_availableregion(dbuf, &r);
	isc_buffer_init(nbuf, r.base, r.length);
539
540
541
542
	dns_name_init(name, NULL);
	dns_name_setbuffer(name, nbuf);
	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;

543
	CTRACE("query_newname: done");
544
545
546
547
548
549
550
	return (name);
}

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

552
	CTRACE("query_newrdataset");
Bob Halley's avatar
Bob Halley committed
553
554
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
555
	if (result != ISC_R_SUCCESS) {
David Lawrence's avatar
David Lawrence committed
556
557
	  CTRACE("query_newrdataset: "
		 "dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
558
		return (NULL);
559
	}
560
561
	dns_rdataset_init(rdataset);

562
	CTRACE("query_newrdataset: done");
563
564
565
	return (rdataset);
}

566
567
568
569
570
571
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++) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
572
		dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
573
574
575
		if (dbversion != NULL) {
			dbversion->db = NULL;
			dbversion->version = NULL;
576
			ISC_LIST_INITANDAPPEND(client->query.freeversions,
577
					      dbversion, link);
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
		} 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);
606

607
608
609
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
610
611
isc_result_t
ns_query_init(ns_client_t *client) {
612
613
	isc_result_t result;

Bob Halley's avatar
Bob Halley committed
614
	ISC_LIST_INIT(client->query.namebufs);
615
616
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
617
	client->query.restarts = 0;
618
	client->query.timerset = ISC_FALSE;
619
	client->query.rpz_st = NULL;
620
	client->query.qname = NULL;
621
622
623
	result = isc_mutex_init(&client->query.fetchlock);
	if (result != ISC_R_SUCCESS)
		return (result);
624
	client->query.fetch = NULL;
625
	client->query.prefetch = NULL;
Michael Graff's avatar
   
Michael Graff committed
626
	client->query.authdb = NULL;
627
628
	client->query.authzone = NULL;
	client->query.authdbset = ISC_FALSE;
629
	client->query.isreferral = ISC_FALSE;
630
631
632
633
	client->query.dns64_aaaa = NULL;
	client->query.dns64_sigaaaa = NULL;
	client->query.dns64_aaaaok = NULL;
	client->query.dns64_aaaaoklen = 0;
634
	query_reset(client, ISC_FALSE);
635
	result = query_newdbversion(client, 3);
636
637
	if (result != ISC_R_SUCCESS) {
		DESTROYLOCK(&client->query.fetchlock);
638
		return (result);
639
	}
Mark Andrews's avatar
Mark Andrews committed
640
	result = query_newnamebuf(client);
641
642
643
	if (result != ISC_R_SUCCESS)
		query_freefreeversions(client, ISC_TRUE);

Mark Andrews's avatar
Mark Andrews committed
644
	return (result);
Bob Halley's avatar
Bob Halley committed
645
646
}

647
static inline ns_dbversion_t *
648
query_findversion(ns_client_t *client, dns_db_t *db)
649
{
650
651
	ns_dbversion_t *dbversion;

652
	/*%
653
654
655
656
657
658
659
660
661
	 * 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
662
663
	}

664
665
666
667
668
669
670
671
672
673
	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);
674
		dbversion->acl_checked = ISC_FALSE;
675
		dbversion->queryok = ISC_FALSE;
676
677
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
678
	}
679

680
681
682
683
	return (dbversion);
}

static inline isc_result_t
684
685
686
687
query_validatezonedb(ns_client_t *client, dns_name_t *name,
		     dns_rdatatype_t qtype, unsigned int options,
		     dns_zone_t *zone, dns_db_t *db,
		     dns_dbversion_t **versionp)
688
689
{
	isc_result_t result;
Evan Hunt's avatar
Evan Hunt committed
690
	dns_acl_t *queryacl, *queryonacl;
691
692
	ns_dbversion_t *dbversion;

693
694
	REQUIRE(zone != NULL);
	REQUIRE(db != NULL);
695

Michael Graff's avatar
   
Michael Graff committed
696
697
	/*
	 * This limits our searching to the zone where the first name
698
	 * (the query target) was looked for.  This prevents following
699
	 * CNAMES or DNAMES into other zones and prevents returning
700
	 * additional data from other zones.
Michael Graff's avatar
   
Michael Graff committed
701
	 */
702
	if (!client->view->additionalfromauth &&
703
	    client->query.authdbset &&
704
	    db != client->query.authdb)
705
		return (DNS_R_REFUSED);
Michael Graff's avatar
   
Michael Graff committed
706

707
708
709
710
711
712
713
	/*
	 * Non recursive query to a static-stub zone is prohibited; its
	 * zone content is not public data, but a part of local configuration
	 * and should not be disclosed.
	 */
	if (dns_zone_gettype(zone) == dns_zone_staticstub &&
	    !RECURSIONOK(client)) {
714
		return (DNS_R_REFUSED);
715
716
	}

717
	/*
718
	 * If the zone has an ACL, we'll check it, otherwise
719
720
721
	 * we use the view's "allow-query" ACL.  Each ACL is only checked
	 * once per query.
	 *
722
	 * Also, get the database version to use.
723
724
	 */

725
726
727
	/*
	 * Get the current version of this database.
	 */
728
729
730
731
732
733
734
735
736
737
	dbversion = query_findversion(client, db);
	if (dbversion == NULL)
		return (DNS_R_SERVFAIL);

	if ((options & DNS_GETDB_IGNOREACL) != 0)
		goto approved;
	if (dbversion->acl_checked) {
		if (!dbversion->queryok)
			return (DNS_R_REFUSED);
		goto approved;
738
	}
739

740
	queryacl = dns_zone_getqueryacl(zone);
741
742
743
744
745
746
747
748
749
750
	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.
			 */
751
			dbversion->acl_checked = ISC_TRUE;
752
			if ((client->query.attributes &
753
754
755
756
757
758
			     NS_QUERYATTR_QUERYOK) == 0) {
				dbversion->queryok = ISC_FALSE;
				return (DNS_R_REFUSED);
			}
			dbversion->queryok = ISC_TRUE;
			goto approved;
759
760
761
		}
	}

762
763
764
765
766
	result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
	if ((options & DNS_GETDB_NOLOG) == 0) {
		char msg[NS_CLIENT_ACLMSGSIZE("query")];
		if (result == ISC_R_SUCCESS) {
			if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) {
767
				ns_client_aclmsg("query", name, qtype,
768
769
						 client->view->rdclass,
						 msg, sizeof(msg));
770
771
772
773
774
				ns_client_log(client,
					      DNS_LOGCATEGORY_SECURITY,
					      NS_LOGMODULE_QUERY,
					      ISC_LOG_DEBUG(3),
					      "%s approved", msg);
775
			}
776
777
778
779
780
781
782
		} else {
			ns_client_aclmsg("query", name, qtype,
					 client->view->rdclass,
					 msg, sizeof(msg));
			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
				      NS_LOGMODULE_QUERY, ISC_LOG_INFO,
				      "%s denied", msg);
783
		}
784
	}
785

786
787
	if (queryacl == client->view->queryacl) {
		if (result == ISC_R_SUCCESS) {
788
			/*
789
790
791
			 * We were allowed by the default
			 * "allow-query" ACL.  Remember this so we
			 * don't have to check again.
792
			 */
793
			client->query.attributes |= NS_QUERYATTR_QUERYOK;
Michael Graff's avatar
   
Michael Graff committed
794
		}
795
796
797
798
799
		/*
		 * We've now evaluated the view's query ACL, and
		 * the NS_QUERYATTR_QUERYOK attribute is now valid.
		 */
		client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
800
801
	}

Evan Hunt's avatar
Evan Hunt committed
802
803
804
805
806
807
	/* If and only if we've gotten this far, check allow-query-on too */
	if (result == ISC_R_SUCCESS) {
		queryonacl = dns_zone_getqueryonacl(zone);
		if (queryonacl == NULL)
			queryonacl = client->view->queryonacl;

808
		result = ns_client_checkaclsilent(client, &client->destaddr,
Evan Hunt's avatar
Evan Hunt committed
809
810
811
812
813
814
815
816
						  queryonacl, ISC_TRUE);
		if ((options & DNS_GETDB_NOLOG) == 0 &&
		    result != ISC_R_SUCCESS)
			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
				      NS_LOGMODULE_QUERY, ISC_LOG_INFO,
				      "query-on denied");
	}

817
818
819
820
821
	dbversion->acl_checked = ISC_TRUE;
	if (result != ISC_R_SUCCESS) {
		dbversion->queryok = ISC_FALSE;
		return (DNS_R_REFUSED);
	}
822
	dbversion->queryok = ISC_TRUE;
823

824
 approved:
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
	/* Transfer ownership, if necessary. */
	if (versionp != NULL)
		*versionp = dbversion->version;
	return (ISC_R_SUCCESS);
}

static inline isc_result_t
query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
		unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
		dns_dbversion_t **versionp)
{
	isc_result_t result;
	unsigned int ztoptions;
	dns_zone_t *zone = NULL;
	dns_db_t *db = NULL;
	isc_boolean_t partial = ISC_FALSE;

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

845
	/*%
846
847
848
849
850
851
852
	 * Find a zone database to answer the query.
	 */
	ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
		DNS_ZTFIND_NOEXACT : 0;

	result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
			     &zone);
Evan Hunt's avatar
Evan Hunt committed
853

854
855
856
857
858
	if (result == DNS_R_PARTIALMATCH)
		partial = ISC_TRUE;
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
		result = dns_zone_getdb(zone, &db);

859
	if (result != ISC_R_SUCCESS)
860
861
862
863
864
865
866
867
		goto fail;

	result = query_validatezonedb(client, name, qtype, options, zone, db,
				      versionp);

	if (result != ISC_R_SUCCESS)
		goto fail;

868
869
870
871
	/* Transfer ownership. */
	*zonep = zone;
	*dbp = db;

872
873
	if (partial && (options & DNS_GETDB_PARTIAL) != 0)
		return (DNS_R_PARTIALMATCH);
874
875
876
877
878
879
880
	return (ISC_R_SUCCESS);

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

882
	return (result);
883
884
}

885
static void
886
rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
887
		dns_rpz_policy_t policy, dns_rpz_type_t type,
888
889
890
		dns_zone_t *p_zone, dns_name_t *p_name)
{
	isc_stats_t *zonestats;
891
	char qname_buf[DNS_NAME_FORMATSIZE];
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
	char p_name_buf[DNS_NAME_FORMATSIZE];

	/*
	 * Count enabled rewrites in the global counter.
	 * Count both enabled and disabled rewrites for each zone.
	 */
	if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) {
		isc_stats_increment(ns_g_server->nsstats,
				    dns_nsstatscounter_rpz_rewrites);
	}
	if (p_zone != NULL) {
		zonestats = dns_zone_getrequeststats(p_zone);
		if (zonestats != NULL)
			isc_stats_increment(zonestats,
					    dns_nsstatscounter_rpz_rewrites);
	}
908

909
	if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
910
911
		return;

912
	dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
913
	dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
914

915
916
	ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
		      DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s",
917
		      disabled ? "disabled " : "",
918
		      dns_rpz_type2str(type), dns_rpz_policy2str(policy),
919
		      qname_buf, p_name_buf);
920
921
922
}

static void
923
924
rpz_log_fail(ns_client_t *client, int level, dns_name_t *p_name,
	     dns_rpz_type_t rpz_type, const char *str, isc_result_t result)
925
{
926
927
	char qnamebuf[DNS_NAME_FORMATSIZE];
	char p_namebuf[DNS_NAME_FORMATSIZE];
Evan Hunt's avatar
Evan Hunt committed
928
	const char *failed;
929

930
	if (!isc_log_wouldlog(ns_g_lctx, level))
931
932
		return;

933
	/*
Evan Hunt's avatar
Evan Hunt committed
934
	 * bin/tests/system/rpz/tests.sh looks for "rpz.*failed" for problems.
935
	 */
Evan Hunt's avatar
Evan Hunt committed
936
937
938
939
	if (level <= DNS_RPZ_DEBUG_LEVEL1)
		failed = "failed: ";
	else
		failed = ": ";
940
941
	dns_name_format(client->query.qname, qnamebuf, sizeof(qnamebuf));
	dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
942
943
	ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
		      NS_LOGMODULE_QUERY, level,
Evan Hunt's avatar
Evan Hunt committed
944
		      "rpz %s rewrite %s via %s%s%s%s",
945
		      dns_rpz_type2str(rpz_type),
Evan Hunt's avatar
Evan Hunt committed
946
947
		      qnamebuf, p_namebuf,
		      str, failed, isc_result_totext(result));
948
949
950
951
952
953
}

/*
 * Get a policy rewrite zone database.
 */
static isc_result_t
954
rpz_getdb(ns_client_t *client, dns_name_t *p_name, dns_rpz_type_t rpz_type,
955
	  dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp)
956
{
957
958
	char qnamebuf[DNS_NAME_FORMATSIZE];
	char p_namebuf[DNS_NAME_FORMATSIZE];
959
960
961
	dns_dbversion_t *rpz_version = NULL;
	isc_result_t result;

962
	result = query_getzonedb(client, p_name, dns_rdatatype_any,
963
964
				 DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
	if (result == ISC_R_SUCCESS) {
965
		if (isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) {
966
967
968
			dns_name_format(client->query.qname, qnamebuf,
					sizeof(qnamebuf));
			dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
969
			ns_client_log(client, DNS_LOGCATEGORY_RPZ,
970
971
972
				      NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2,
				      "try rpz %s rewrite %s via %s",
				      dns_rpz_type2str(rpz_type),
973
				      qnamebuf, p_namebuf);
974
975
976
977
		}
		*versionp = rpz_version;
		return (ISC_R_SUCCESS);
	}
978
	rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
Evan Hunt's avatar
Evan Hunt committed
979
		     " query_getzonedb()", result);
980
981
982
	return (result);
}

983
static inline isc_result_t
984
985
query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
		 dns_db_t **dbp, unsigned int options)
986
987
988
{
	isc_result_t result;
	isc_boolean_t check_acl;
989
990
991
	dns_db_t *db = NULL;

	REQUIRE(dbp != NULL && *dbp == NULL);
992

993
	/*%
994
	 * Find a cache database to answer the query.
995
	 * This may fail with DNS_R_REFUSED if the client
996
997
998
999
1000
	 * is not allowed to use the cache.
	 */

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

1003
	if ((client->query.attributes & NS_QUERYATTR_CACHEACLOKVALID) != 0) {
1004
		/*
1005
1006
		 * We've evaluated the view's cacheacl already.  If
		 * NS_QUERYATTR_CACHEACLOK is set, then the client is
1007
1008
1009
1010
		 * allowed to make queries, otherwise the query should
		 * be refused.
		 */
		check_acl = ISC_FALSE;