query.c 263 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
5
6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
Bob Halley's avatar
add  
Bob Halley committed
10
11
 */

12
/*! \file */
David Lawrence's avatar
David Lawrence committed
13

Bob Halley's avatar
add  
Bob Halley committed
14
15
#include <config.h>

Brian Wellington's avatar
Brian Wellington committed
16
17
#include <string.h>

18
#include <isc/hex.h>
Bob Halley's avatar
add  
Bob Halley committed
19
#include <isc/mem.h>
Mark Andrews's avatar
Mark Andrews committed
20
#include <isc/print.h>
Evan Hunt's avatar
Evan Hunt committed
21
#include <isc/rwlock.h>
22
#include <isc/serial.h>
23
#include <isc/stats.h>
24
#include <isc/thread.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>
Evan Hunt's avatar
Evan Hunt committed
28
#include <dns/badcache.h>
29
#include <dns/byaddr.h>
30
#include <dns/cache.h>
Bob Halley's avatar
Bob Halley committed
31
#include <dns/db.h>
32
#include <dns/dlz.h>
33
#include <dns/dns64.h>
34
#include <dns/dnssec.h>
Bob Halley's avatar
add  
Bob Halley committed
35
#include <dns/events.h>
Bob Halley's avatar
Bob Halley committed
36
#include <dns/message.h>
37
#include <dns/ncache.h>
38
#include <dns/nsec3.h>
39
#include <dns/order.h>
Bob Halley's avatar
Bob Halley committed
40
#include <dns/rdata.h>
41
#include <dns/rdataclass.h>
42
#include <dns/rdatalist.h>
Bob Halley's avatar
Bob Halley committed
43
44
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
45
#include <dns/rdatastruct.h>
46
#include <dns/rdatatype.h>
47
#include <dns/resolver.h>
48
#include <dns/result.h>
49
#include <dns/stats.h>
Brian Wellington's avatar
Brian Wellington committed
50
#include <dns/tkey.h>
51
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
52
53
#include <dns/zone.h>
#include <dns/zt.h>
Bob Halley's avatar
add  
Bob Halley committed
54
55

#include <named/client.h>
56
#include <named/globals.h>
57
58
#include <named/log.h>
#include <named/server.h>
59
#include <named/sortlist.h>
60
#include <named/xfrout.h>
61

62
63
64
65
66
67
68
69
70
71
72
#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

73
/*% Partial answer? */
Bob Halley's avatar
Bob Halley committed
74
75
#define PARTIALANSWER(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_PARTIALANSWER) != 0)
76
/*% Use Cache? */
Bob Halley's avatar
Bob Halley committed
77
78
#define USECACHE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEOK) != 0)
79
/*% Recursion OK? */
Bob Halley's avatar
Bob Halley committed
80
81
#define RECURSIONOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSIONOK) != 0)
82
/*% Recursing? */
83
84
#define RECURSING(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSING) != 0)
85
/*% Cache glue ok? */
Bob Halley's avatar
Bob Halley committed
86
87
#define CACHEGLUEOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEGLUEOK) != 0)
88
/*% Want Recursion? */
89
90
#define WANTRECURSION(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_WANTRECURSION) != 0)
91
92
/*% Is TCP? */
#define TCP(c)			(((c)->attributes & NS_CLIENTATTR_TCP) != 0)
Mark Andrews's avatar
Mark Andrews committed
93

94
/*% Want DNSSEC? */
95
96
#define WANTDNSSEC(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTDNSSEC) != 0)
97
98
99
/*% Want WANTAD? */
#define WANTAD(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTAD) != 0)
100
101
102
103
104
105
/*% Client presented a valid COOKIE. */
#define HAVECOOKIE(c)		(((c)->attributes & \
				  NS_CLIENTATTR_HAVECOOKIE) != 0)
/*% Client presented a COOKIE. */
#define WANTCOOKIE(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTCOOKIE) != 0)
106
/*% No authority? */
107
108
#define NOAUTHORITY(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOAUTHORITY) != 0)
109
/*% No additional? */
110
111
#define NOADDITIONAL(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOADDITIONAL) != 0)
112
/*% Secure? */
113
114
#define SECURE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_SECURE) != 0)
115
116
117
118
119
120
/*% 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
121

122
123
124
#define REDIRECT(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_REDIRECT) != 0)

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

129
#ifdef WANT_QUERYTRACE
130
131
132
133
134
static inline void
client_trace(ns_client_t *client, int level, const char *message) {
	if (client != NULL && client->query.qname != NULL) {
		if (isc_log_wouldlog(ns_g_lctx, level)) {
			char qbuf[DNS_NAME_FORMATSIZE];
135
			char tbuf[DNS_RDATATYPE_FORMATSIZE];
136
137
			dns_name_format(client->query.qname,
					qbuf, sizeof(qbuf));
138
139
			dns_rdatatype_format(client->query.qtype,
					     tbuf, sizeof(tbuf));
140
141
			isc_log_write(ns_g_lctx,
				      NS_LOGCATEGORY_CLIENT,
142
143
144
145
146
147
				      NS_LOGMODULE_QUERY, level,
				      "query client=%p thread=0x%lx "
				      "(%s/%s): %s",
				      client,
				      (unsigned long) isc_thread_self(),
				      qbuf, tbuf, message);
148
149
150
151
		}
	 } else {
		isc_log_write(ns_g_lctx,
			      NS_LOGCATEGORY_CLIENT,
152
153
154
155
156
157
			      NS_LOGMODULE_QUERY, level,
			      "query client=%p thread=0x%lx "
			      "(<unknown-query>): %s",
			      client,
			      (unsigned long) isc_thread_self(),
			      message);
158
159
160
	}
}
#define CTRACE(l,m)	  client_trace(client, l, m)
161
#else
162
#define CTRACE(l,m) ((void)m)
163
#endif /* WANT_QUERYTRACE */
164

165

166
167
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
168
#define DNS_GETDB_PARTIAL 0x04U
169
#define DNS_GETDB_IGNOREACL 0x08U
170

171
172
#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)

Evan Hunt's avatar
Evan Hunt committed
173
174
#define SFCACHE_CDFLAG 0x1

175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * These have the same semantics as:
 *
 * 	foo_attach(b, a);
 *	foo_detach(&a);
 *
 * without the locking and magic testing.
 *
 * We use SAVE and RESTORE as that shows the operation being performed.
 */
#define SAVE(a, b) do { INSIST(a == NULL); a = b; b = NULL; } while (0)
#define RESTORE(a, b) SAVE(a, b)

188
189
190
191
192
typedef struct client_additionalctx {
	ns_client_t *client;
	dns_rdataset_t *rdataset;
} client_additionalctx_t;

193
static isc_result_t
194
query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
195

196
197
198
199
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);

200
201
202
203
204
205
206
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);

207
208
209
static inline void
log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);

210
211
212
static void
rpz_st_clear(ns_client_t *client);

213
214
215
216
static isc_boolean_t
rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
	      dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);

217
/*%
218
219
 * Increment query statistics counters.
 */
220
static inline void
221
inc_stats(ns_client_t *client, isc_statscounter_t counter) {
222
	dns_zone_t *zone = client->query.authzone;
223
224
225
226
	dns_rdatatype_t qtype;
	dns_rdataset_t *rdataset;
	isc_stats_t *zonestats;
	dns_stats_t *querystats = NULL;
227

228
	isc_stats_increment(ns_g_server->nsstats, counter);
229

230
231
232
233
234
235
236
237
238
239
240
	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
241
	 * We only increment per-type if we're using the authoritative
242
243
244
245
246
247
248
249
250
251
252
	 * 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);
			}
		}
253
254
	}
}
255

256
257
static void
query_send(ns_client_t *client) {
258
	isc_statscounter_t counter;
259

260
261
262
263
	if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0)
		inc_stats(client, dns_nsstatscounter_nonauthans);
	else
		inc_stats(client, dns_nsstatscounter_authans);
264

265
	if (client->message->rcode == dns_rcode_noerror) {
266
267
268
		dns_section_t answer = DNS_SECTION_ANSWER;
		if (ISC_LIST_EMPTY(client->message->sections[answer])) {
			if (client->query.isreferral)
269
				counter = dns_nsstatscounter_referral;
270
			else
271
				counter = dns_nsstatscounter_nxrrset;
272
		} else
273
			counter = dns_nsstatscounter_success;
274
	} else if (client->message->rcode == dns_rcode_nxdomain)
275
		counter = dns_nsstatscounter_nxdomain;
276
277
	else if (client->message->rcode == dns_rcode_badcookie)
		counter = dns_nsstatscounter_badcookie;
278
	else /* We end up here in case of YXDOMAIN, and maybe others */
279
		counter = dns_nsstatscounter_failure;
280

281
282
283
284
285
	inc_stats(client, counter);
	ns_client_send(client);
}

static void
286
287
288
query_error(ns_client_t *client, isc_result_t result, int line) {
	int loglevel = ISC_LOG_DEBUG(3);

289
290
	switch (result) {
	case DNS_R_SERVFAIL:
291
		loglevel = ISC_LOG_DEBUG(1);
292
293
294
295
296
297
298
299
300
		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;
	}
301

302
303
304
	if (ns_g_server->log_queries)
		loglevel = ISC_LOG_INFO;

305
306
	log_queryerror(client, result, line, loglevel);

307
308
309
310
311
	ns_client_error(client, result);
}

static void
query_next(ns_client_t *client, isc_result_t result) {
312
	if (result == DNS_R_DUPLICATE)
313
		inc_stats(client, dns_nsstatscounter_duplicate);
314
	else if (result == DNS_R_DROP)
315
		inc_stats(client, dns_nsstatscounter_dropped);
316
	else
317
		inc_stats(client, dns_nsstatscounter_failure);
318
319
320
	ns_client_next(client, result);
}

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
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));
		}
	}
}

344
345
void
ns_query_cancel(ns_client_t *client) {
346
347
	REQUIRE(NS_CLIENT_VALID(client));

348
349
350
351
352
353
354
355
356
	LOCK(&client->query.fetchlock);
	if (client->query.fetch != NULL) {
		dns_resolver_cancelfetch(client->query.fetch);

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

357
358
359
360
static inline void
query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
	dns_rdataset_t *rdataset = *rdatasetp;

361
	CTRACE(ISC_LOG_DEBUG(3), "query_putrdataset");
362
363
364
365
366
	if (rdataset != NULL) {
		if (dns_rdataset_isassociated(rdataset))
			dns_rdataset_disassociate(rdataset);
		dns_message_puttemprdataset(client->message, rdatasetp);
	}
367
	CTRACE(ISC_LOG_DEBUG(3), "query_putrdataset: done");
368
369
}

Bob Halley's avatar
Bob Halley committed
370
static inline void
371
query_reset(ns_client_t *client, isc_boolean_t everything) {
372
	isc_buffer_t *dbuf, *dbuf_next;
373
374
	ns_dbversion_t *dbversion, *dbversion_next;

375
376
	CTRACE(ISC_LOG_DEBUG(3), "query_reset");

377
	/*%
378
379
	 * Reset the query state of a client to its default state.
	 */
380

381
	/*
382
	 * Cancel the fetch if it's running.
383
	 */
384
	ns_query_cancel(client);
385

386
387
388
389
390
391
392
393
394
395
	/*
	 * 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);
396
		ISC_LIST_INITANDAPPEND(client->query.freeversions,
397
				      dbversion, link);
398
399
400
	}
	ISC_LIST_INIT(client->query.activeversions);

Michael Graff's avatar
   
Michael Graff committed
401
402
	if (client->query.authdb != NULL)
		dns_db_detach(&client->query.authdb);
403
404
	if (client->query.authzone != NULL)
		dns_zone_detach(&client->query.authzone);
Michael Graff's avatar
   
Michael Graff committed
405

406
407
408
409
410
411
412
413
414
415
416
417
	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;
	}

418
419
420
421
422
423
424
425
426
427
428
	query_putrdataset(client, &client->query.redirect.rdataset);
	query_putrdataset(client, &client->query.redirect.sigrdataset);
	if (client->query.redirect.db != NULL) {
		if (client->query.redirect.node != NULL)
			dns_db_detachnode(client->query.redirect.db,
					  &client->query.redirect.node);
		dns_db_detach(&client->query.redirect.db);
	}
	if (client->query.redirect.zone != NULL)
		dns_zone_detach(&client->query.redirect.zone);

429
	query_freefreeversions(client, everything);
Bob Halley's avatar
Bob Halley committed
430
431
432
433
434
435
436

	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);
437
			isc_buffer_free(&dbuf);
Bob Halley's avatar
Bob Halley committed
438
439
		}
	}
Bob Halley's avatar
Bob Halley committed
440

441
442
443
444
445
446
447
448
	if (client->query.restarts > 0) {
		/*
		 * client->query.qname was dynamically allocated.
		 */
		dns_message_puttempname(client->message,
					&client->query.qname);
	}
	client->query.qname = NULL;
449
	client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
450
451
				    NS_QUERYATTR_CACHEOK |
				    NS_QUERYATTR_SECURE);
452
	client->query.restarts = 0;
453
	client->query.timerset = ISC_FALSE;
454
455
456
457
458
459
460
461
	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;
		}
	}
462
	client->query.origqname = NULL;
463
	client->query.dboptions = 0;
464
	client->query.fetchoptions = 0;
465
	client->query.gluedb = NULL;
466
467
	client->query.authdbset = ISC_FALSE;
	client->query.isreferral = ISC_FALSE;
468
469
	client->query.dns64_options = 0;
	client->query.dns64_ttl = ISC_UINT32_MAX;
Bob Halley's avatar
Bob Halley committed
470
471
472
}

static void
473
query_next_callback(ns_client_t *client) {
474
	query_reset(client, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
475
476
}

477
478
void
ns_query_free(ns_client_t *client) {
479
480
	REQUIRE(NS_CLIENT_VALID(client));

481
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
482
483
484
485
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
486
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
487
488
	isc_result_t result;

489
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf");
490
	/*%
491
492
493
	 * Allocate a name buffer.
	 */

Bob Halley's avatar
Bob Halley committed
494
	dbuf = NULL;
495
	result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
496
	if (result != ISC_R_SUCCESS) {
497
498
		CTRACE(ISC_LOG_DEBUG(3),
		       "query_newnamebuf: isc_buffer_allocate failed: done");
Bob Halley's avatar
Bob Halley committed
499
		return (result);
500
	}
Bob Halley's avatar
Bob Halley committed
501
502
	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);

503
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
504
505
506
	return (ISC_R_SUCCESS);
}

507
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
508
query_getnamebuf(ns_client_t *client) {
509
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
510
511
512
	isc_result_t result;
	isc_region_t r;

513
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf");
514
	/*%
515
516
517
518
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

Bob Halley's avatar
Bob Halley committed
519
520
	if (ISC_LIST_EMPTY(client->query.namebufs)) {
		result = query_newnamebuf(client);
521
		if (result != ISC_R_SUCCESS) {
522
523
		    CTRACE(ISC_LOG_DEBUG(3),
			   "query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
524
			return (NULL);
525
		}
Bob Halley's avatar
Bob Halley committed
526
527
528
529
	}

	dbuf = ISC_LIST_TAIL(client->query.namebufs);
	INSIST(dbuf != NULL);
530
	isc_buffer_availableregion(dbuf, &r);
Mukund Sivaraman's avatar
Mukund Sivaraman committed
531
	if (r.length < DNS_NAME_MAXWIRE) {
Bob Halley's avatar
Bob Halley committed
532
		result = query_newnamebuf(client);
533
		if (result != ISC_R_SUCCESS) {
534
535
		    CTRACE(ISC_LOG_DEBUG(3),
			   "query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
536
			return (NULL);
537
538

		}
Bob Halley's avatar
Bob Halley committed
539
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
540
		isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
541
542
		INSIST(r.length >= 255);
	}
543
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
544
545
546
	return (dbuf);
}

547
static inline void
548
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
549
550
	isc_region_t r;

551
	CTRACE(ISC_LOG_DEBUG(3), "query_keepname");
552
	/*%
553
554
555
556
557
558
559
	 * '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);
560
	isc_buffer_add(dbuf, r.length);
561
562
563
564
565
566
567
568
	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;

569
	/*%
570
571
572
573
574
	 * '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.
	 */

575
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename");
576
577
578
579
580
	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
581
	dns_message_puttempname(client->message, namep);
582
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename: done");
583
584
585
}

static inline dns_name_t *
586
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
587
588
589
590
591
592
593
594
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

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

595
	CTRACE(ISC_LOG_DEBUG(3), "query_newname");
Bob Halley's avatar
Bob Halley committed
596
597
	name = NULL;
	result = dns_message_gettempname(client->message, &name);
598
	if (result != ISC_R_SUCCESS) {
599
600
		CTRACE(ISC_LOG_DEBUG(3),
		       "query_newname: dns_message_gettempname failed: done");
Bob Halley's avatar
Bob Halley committed
601
		return (NULL);
602
	}
603
604
	isc_buffer_availableregion(dbuf, &r);
	isc_buffer_init(nbuf, r.base, r.length);
605
606
607
608
	dns_name_init(name, NULL);
	dns_name_setbuffer(name, nbuf);
	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;

609
	CTRACE(ISC_LOG_DEBUG(3), "query_newname: done");
610
611
612
613
614
615
616
	return (name);
}

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

618
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset");
Bob Halley's avatar
Bob Halley committed
619
620
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
621
	if (result != ISC_R_SUCCESS) {
622
623
	  CTRACE(ISC_LOG_DEBUG(3),
		 "query_newrdataset: "
David Lawrence's avatar
David Lawrence committed
624
		 "dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
625
		return (NULL);
626
	}
627

628
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset: done");
629
630
631
	return (rdataset);
}

632
633
634
635
636
637
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
638
		dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
639
640
641
		if (dbversion != NULL) {
			dbversion->db = NULL;
			dbversion->version = NULL;
642
			ISC_LIST_INITANDAPPEND(client->query.freeversions,
643
					      dbversion, link);
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
		} 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);
672

673
674
675
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
676
677
isc_result_t
ns_query_init(ns_client_t *client) {
678
679
	isc_result_t result;

680
681
	REQUIRE(NS_CLIENT_VALID(client));

Bob Halley's avatar
Bob Halley committed
682
	ISC_LIST_INIT(client->query.namebufs);
683
684
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
685
	client->query.restarts = 0;
686
	client->query.timerset = ISC_FALSE;
687
	client->query.rpz_st = NULL;
688
	client->query.qname = NULL;
689
690
691
692
	/*
	 * This mutex is destroyed when the client is destroyed in
	 * exit_check().
	 */
693
694
695
	result = isc_mutex_init(&client->query.fetchlock);
	if (result != ISC_R_SUCCESS)
		return (result);
696
	client->query.fetch = NULL;
697
	client->query.prefetch = NULL;
Michael Graff's avatar
   
Michael Graff committed
698
	client->query.authdb = NULL;
699
700
	client->query.authzone = NULL;
	client->query.authdbset = ISC_FALSE;
701
	client->query.isreferral = ISC_FALSE;
702
703
704
705
	client->query.dns64_aaaa = NULL;
	client->query.dns64_sigaaaa = NULL;
	client->query.dns64_aaaaok = NULL;
	client->query.dns64_aaaaoklen = 0;
706
707
708
709
710
711
712
	client->query.redirect.db = NULL;
	client->query.redirect.node = NULL;
	client->query.redirect.zone = NULL;
	client->query.redirect.qtype = dns_rdatatype_none;
	client->query.redirect.result = ISC_R_SUCCESS;
	client->query.redirect.rdataset = NULL;
	client->query.redirect.sigrdataset = NULL;
713
714
	client->query.redirect.authoritative = ISC_FALSE;
	client->query.redirect.is_zone  = ISC_FALSE;
715
	client->query.redirect.fname =
716
		dns_fixedname_initname(&client->query.redirect.fixed);
717
	query_reset(client, ISC_FALSE);
718
	result = query_newdbversion(client, 3);
719
720
	if (result != ISC_R_SUCCESS) {
		DESTROYLOCK(&client->query.fetchlock);
721
		return (result);
722
	}
Mark Andrews's avatar
Mark Andrews committed
723
	result = query_newnamebuf(client);
724
	if (result != ISC_R_SUCCESS) {
725
		query_freefreeversions(client, ISC_TRUE);
726
727
		DESTROYLOCK(&client->query.fetchlock);
	}
728

Mark Andrews's avatar
Mark Andrews committed
729
	return (result);
Bob Halley's avatar
Bob Halley committed
730
731
}

732
static ns_dbversion_t *
733
query_findversion(ns_client_t *client, dns_db_t *db) {
734
735
	ns_dbversion_t *dbversion;

736
	/*%
737
738
739
740
741
742
743
744
745
	 * 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
746
747
	}

748
749
750
751
752
753
754
755
756
757
	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);
758
		dbversion->acl_checked = ISC_FALSE;
759
		dbversion->queryok = ISC_FALSE;
760
761
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
762
	}
763

764
765
766
767
	return (dbversion);
}

static inline isc_result_t
768
769
770
771
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)
772
773
{
	isc_result_t result;
Evan Hunt's avatar
Evan Hunt committed
774
	dns_acl_t *queryacl, *queryonacl;
775
776
	ns_dbversion_t *dbversion;

777
778
	REQUIRE(zone != NULL);
	REQUIRE(db != NULL);
779

Michael Graff's avatar
   
Michael Graff committed
780
781
	/*
	 * This limits our searching to the zone where the first name
782
	 * (the query target) was looked for.  This prevents following
783
	 * CNAMES or DNAMES into other zones and prevents returning
784
	 * additional data from other zones.
Michael Graff's avatar
   
Michael Graff committed
785
	 */
786
	if (!client->view->additionalfromauth &&
787
	    client->query.authdbset &&
788
	    db != client->query.authdb)
789
		return (DNS_R_REFUSED);
Michael Graff's avatar
   
Michael Graff committed
790

791
792
793
794
795
796
797
	/*
	 * 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)) {
798
		return (DNS_R_REFUSED);
799
800
	}

801
	/*
802
	 * If the zone has an ACL, we'll check it, otherwise
803
804
805
	 * we use the view's "allow-query" ACL.  Each ACL is only checked
	 * once per query.
	 *
806
	 * Also, get the database version to use.
807
808
	 */

809
810
811
	/*
	 * Get the current version of this database.
	 */
812
	dbversion = query_findversion(client, db);
813
814
	if (dbversion == NULL) {
		CTRACE(ISC_LOG_ERROR, "unable to get db version");
815
		return (DNS_R_SERVFAIL);
816
	}
817
818
819
820
821
822
823

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

826
	queryacl = dns_zone_getqueryacl(zone);
827
828
829
830
831
832
833
834
835
836
	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.
			 */
837
			dbversion->acl_checked = ISC_TRUE;
838
			if ((client->query.attributes &
839
840
841
842
843
844
			     NS_QUERYATTR_QUERYOK) == 0) {
				dbversion->queryok = ISC_FALSE;
				return (DNS_R_REFUSED);
			}
			dbversion->queryok = ISC_TRUE;
			goto approved;
845
846
847
		}
	}

848
849
850
851
852
	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))) {
853
				ns_client_aclmsg("query", name, qtype,
854
855
						 client->view->rdclass,
						 msg, sizeof(msg));
856
857
858
859
860
				ns_client_log(client,
					      DNS_LOGCATEGORY_SECURITY,
					      NS_LOGMODULE_QUERY,
					      ISC_LOG_DEBUG(3),
					      "%s approved", msg);
861
			}
862
863
864
865
866
867
868
		} 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);
869
		}
870
	}
871

872
873
	if (queryacl == client->view->queryacl) {
		if (result == ISC_R_SUCCESS) {
874
			/*
875
876
877
			 * We were allowed by the default
			 * "allow-query" ACL.  Remember this so we
			 * don't have to check again.
878
			 */
879
			client->query.attributes |= NS_QUERYATTR_QUERYOK;
Michael Graff's avatar
   
Michael Graff committed
880
		}
881
882
883
884
885
		/*
		 * We've now evaluated the view's query ACL, and
		 * the NS_QUERYATTR_QUERYOK attribute is now valid.
		 */
		client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
886
887
	}

Evan Hunt's avatar
Evan Hunt committed
888
889
890
891
892
893
	/* 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;

894
		result = ns_client_checkaclsilent(client, &client->destaddr,
Evan Hunt's avatar
Evan Hunt committed
895
896
897
898
899
900
901
902
						  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");
	}

903
904
905
906
907
	dbversion->acl_checked = ISC_TRUE;
	if (result != ISC_R_SUCCESS) {
		dbversion->queryok = ISC_FALSE;
		return (DNS_R_REFUSED);
	}
908
	dbversion->queryok = ISC_TRUE;
909

910
 approved:
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
	/* 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);

931
	/*%
932
933
934
935
936
937
938
	 * 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
939

940
941
942
943
944
	if (result == DNS_R_PARTIALMATCH)
		partial = ISC_TRUE;
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
		result = dns_zone_getdb(zone, &db);

945
	if (result != ISC_R_SUCCESS)
946
947
948
949
950
951
952
953
		goto fail;

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

	if (result != ISC_R_SUCCESS)
		goto fail;

954
955
956
957
	/* Transfer ownership. */
	*zonep = zone;
	*dbp = db;

958
959
	if (partial && (options & DNS_GETDB_PARTIAL) != 0)
		return (DNS_R_PARTIALMATCH);
960
961
962
963
964
965
966
	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
967

968
	return (result);
969
970
}

971
static void
972
rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
973
		dns_rpz_policy_t policy, dns_rpz_type_t type,
974
		dns_zone_t *p_zone, dns_name_t *p_name,
975
		dns_name_t *cname, dns_rpz_num_t rpz_num)
976
977
{
	isc_stats_t *zonestats;
978
	char qname_buf[DNS_NAME_FORMATSIZE];
979
	char p_name_buf[DNS_NAME_FORMATSIZE];
980
981
	char cname_buf[DNS_NAME_FORMATSIZE] = { 0 };
	const char *s1 = cname_buf, *s2 = cname_buf;
982
	dns_rpz_st_t *st;
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997

	/*
	 * 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);
	}
998

999
	if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
1000
		return;