query.c 252 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
Automatic Updater's avatar
Automatic Updater committed
4
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
add  
Bob Halley committed
5
6
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
7
 *
Mark Andrews's avatar
Mark Andrews committed
8
9
10
11
12
13
14
 * 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
15
16
 */

17
/*! \file */
David Lawrence's avatar
David Lawrence committed
18

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

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

23
#include <isc/hex.h>
Bob Halley's avatar
add  
Bob Halley committed
24
#include <isc/mem.h>
25
#include <isc/print.h>
Evan Hunt's avatar
Evan Hunt committed
26
#include <isc/rwlock.h>
27
#include <isc/serial.h>
28
#include <isc/stats.h>
29
#include <isc/thread.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>
40
#include <dns/keytable.h>
Bob Halley's avatar
Bob Halley committed
41
#include <dns/message.h>
42
#include <dns/ncache.h>
43
#include <dns/nsec3.h>
44
#include <dns/order.h>
Bob Halley's avatar
Bob Halley committed
45
#include <dns/rdata.h>
46
#include <dns/rdataclass.h>
47
#include <dns/rdatalist.h>
Bob Halley's avatar
Bob Halley committed
48
49
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
50
#include <dns/rdatastruct.h>
51
#include <dns/rdatatype.h>
52
#include <dns/resolver.h>
53
#include <dns/result.h>
54
#include <dns/stats.h>
Brian Wellington's avatar
Brian Wellington committed
55
#include <dns/tkey.h>
56
#include <dns/view.h>
Bob Halley's avatar
Bob Halley committed
57
58
#include <dns/zone.h>
#include <dns/zt.h>
Bob Halley's avatar
add  
Bob Halley committed
59
60

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

67
68
69
70
71
72
73
74
75
76
77
#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

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

99
/*% Want DNSSEC? */
100
101
#define WANTDNSSEC(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTDNSSEC) != 0)
102
103
104
/*% Want WANTAD? */
#define WANTAD(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTAD) != 0)
105
106
107
108
109
110
111
#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
112

113
/*% No authority? */
114
115
#define NOAUTHORITY(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOAUTHORITY) != 0)
116
/*% No additional? */
117
118
#define NOADDITIONAL(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOADDITIONAL) != 0)
119
/*% Secure? */
120
121
#define SECURE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_SECURE) != 0)
122
123
124
125
126
127
/*% 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
128

129
130
131
132
/*% No QNAME Proof? */
#define NOQNAME(r)		(((r)->attributes & \
				  DNS_RDATASETATTR_NOQNAME) != 0)

133
#ifdef WANT_QUERYTRACE
134
135
136
137
138
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];
139
			char tbuf[DNS_RDATATYPE_FORMATSIZE];
140
141
			dns_name_format(client->query.qname,
					qbuf, sizeof(qbuf));
142
143
			dns_rdatatype_format(client->query.qtype,
					     tbuf, sizeof(tbuf));
144
145
			isc_log_write(ns_g_lctx,
				      NS_LOGCATEGORY_CLIENT,
146
147
148
149
150
151
				      NS_LOGMODULE_QUERY, level,
				      "query client=%p thread=0x%lx "
				      "(%s/%s): %s",
				      client,
				      (unsigned long) isc_thread_self(),
				      qbuf, tbuf, message);
152
153
154
155
		}
	 } else {
		isc_log_write(ns_g_lctx,
			      NS_LOGCATEGORY_CLIENT,
156
157
158
159
160
161
			      NS_LOGMODULE_QUERY, level,
			      "query client=%p thread=0x%lx "
			      "(<unknown-query>): %s",
			      client,
			      (unsigned long) isc_thread_self(),
			      message);
162
163
164
	}
}
#define CTRACE(l,m)	  client_trace(client, l, m)
165
#else
166
#define CTRACE(l,m) ((void)m)
167
#endif /* WANT_QUERYTRACE */
168

169

170
171
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
172
#define DNS_GETDB_PARTIAL 0x04U
173
#define DNS_GETDB_IGNOREACL 0x08U
174

175
176
#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)

177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * 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)

190
191
192
193
194
typedef struct client_additionalctx {
	ns_client_t *client;
	dns_rdataset_t *rdataset;
} client_additionalctx_t;

195
static isc_result_t
196
query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
197

198
199
200
201
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);

202
203
204
205
206
207
208
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);

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

212
213
214
static void
rpz_st_clear(ns_client_t *client);

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

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

230
	isc_stats_increment(ns_g_server->nsstats, counter);
231

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

258
259
static void
query_send(ns_client_t *client) {
260
	isc_statscounter_t counter;
261

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

267
	if (client->message->rcode == dns_rcode_noerror) {
268
269
270
		dns_section_t answer = DNS_SECTION_ANSWER;
		if (ISC_LIST_EMPTY(client->message->sections[answer])) {
			if (client->query.isreferral)
271
				counter = dns_nsstatscounter_referral;
272
			else
273
				counter = dns_nsstatscounter_nxrrset;
274
		} else
275
			counter = dns_nsstatscounter_success;
276
	} else if (client->message->rcode == dns_rcode_nxdomain)
277
		counter = dns_nsstatscounter_nxdomain;
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

	log_queryerror(client, result, line, loglevel);

304
305
306
307
308
	ns_client_error(client, result);
}

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

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

341
342
void
ns_query_cancel(ns_client_t *client) {
343
344
	REQUIRE(NS_CLIENT_VALID(client));

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

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

354
355
356
357
static inline void
query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
	dns_rdataset_t *rdataset = *rdatasetp;

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

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

372
373
	CTRACE(ISC_LOG_DEBUG(3), "query_reset");

374
	/*%
375
376
	 * Reset the query state of a client to its default state.
	 */
377

378
	/*
379
	 * Cancel the fetch if it's running.
380
	 */
381
	ns_query_cancel(client);
382

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

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

403
404
405
406
407
408
409
410
411
412
413
414
	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;
	}

415
	query_freefreeversions(client, everything);
Bob Halley's avatar
Bob Halley committed
416
417
418
419
420
421
422

	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);
423
			isc_buffer_free(&dbuf);
Bob Halley's avatar
Bob Halley committed
424
425
		}
	}
Bob Halley's avatar
Bob Halley committed
426

427
428
429
430
431
432
433
434
	if (client->query.restarts > 0) {
		/*
		 * client->query.qname was dynamically allocated.
		 */
		dns_message_puttempname(client->message,
					&client->query.qname);
	}
	client->query.qname = NULL;
435
	client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
436
437
				    NS_QUERYATTR_CACHEOK |
				    NS_QUERYATTR_SECURE);
438
	client->query.restarts = 0;
439
	client->query.timerset = ISC_FALSE;
440
441
442
443
444
445
446
447
	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;
		}
	}
448
	client->query.origqname = NULL;
449
	client->query.dboptions = 0;
450
	client->query.fetchoptions = 0;
451
	client->query.gluedb = NULL;
452
453
	client->query.authdbset = ISC_FALSE;
	client->query.isreferral = ISC_FALSE;
454
455
	client->query.dns64_options = 0;
	client->query.dns64_ttl = ISC_UINT32_MAX;
456
457
458
	client->query.root_key_sentinel_keyid = 0;
	client->query.root_key_sentinel_is_ta = ISC_FALSE;
	client->query.root_key_sentinel_not_ta = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
459
460
461
}

static void
462
query_next_callback(ns_client_t *client) {
463
	query_reset(client, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
464
465
}

466
467
void
ns_query_free(ns_client_t *client) {
468
469
	REQUIRE(NS_CLIENT_VALID(client));

470
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
471
472
473
474
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
475
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
476
477
	isc_result_t result;

478
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf");
479
	/*%
480
481
482
	 * Allocate a name buffer.
	 */

Bob Halley's avatar
Bob Halley committed
483
	dbuf = NULL;
484
	result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
485
	if (result != ISC_R_SUCCESS) {
486
487
		CTRACE(ISC_LOG_DEBUG(3),
		       "query_newnamebuf: isc_buffer_allocate failed: done");
Bob Halley's avatar
Bob Halley committed
488
		return (result);
489
	}
Bob Halley's avatar
Bob Halley committed
490
491
	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);

492
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
493
494
495
	return (ISC_R_SUCCESS);
}

496
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
497
query_getnamebuf(ns_client_t *client) {
498
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
499
500
501
	isc_result_t result;
	isc_region_t r;

502
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf");
503
	/*%
504
505
506
507
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

Bob Halley's avatar
Bob Halley committed
508
509
	if (ISC_LIST_EMPTY(client->query.namebufs)) {
		result = query_newnamebuf(client);
510
		if (result != ISC_R_SUCCESS) {
511
512
		    CTRACE(ISC_LOG_DEBUG(3),
			   "query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
513
			return (NULL);
514
		}
Bob Halley's avatar
Bob Halley committed
515
516
517
518
	}

	dbuf = ISC_LIST_TAIL(client->query.namebufs);
	INSIST(dbuf != NULL);
519
	isc_buffer_availableregion(dbuf, &r);
Mukund Sivaraman's avatar
Mukund Sivaraman committed
520
	if (r.length < DNS_NAME_MAXWIRE) {
Bob Halley's avatar
Bob Halley committed
521
		result = query_newnamebuf(client);
522
		if (result != ISC_R_SUCCESS) {
523
524
		    CTRACE(ISC_LOG_DEBUG(3),
			   "query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
525
			return (NULL);
526
527

		}
Bob Halley's avatar
Bob Halley committed
528
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
529
		isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
530
531
		INSIST(r.length >= 255);
	}
532
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
533
534
535
	return (dbuf);
}

536
static inline void
537
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
538
539
	isc_region_t r;

540
	CTRACE(ISC_LOG_DEBUG(3), "query_keepname");
541
	/*%
542
543
544
545
546
547
548
	 * '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);
549
	isc_buffer_add(dbuf, r.length);
550
551
552
553
554
555
556
557
	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;

558
	/*%
559
560
561
562
563
	 * '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.
	 */

564
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename");
565
566
567
568
569
	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
570
	dns_message_puttempname(client->message, namep);
571
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename: done");
572
573
574
}

static inline dns_name_t *
575
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
576
577
578
579
580
581
582
583
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

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

584
	CTRACE(ISC_LOG_DEBUG(3), "query_newname");
Bob Halley's avatar
Bob Halley committed
585
586
	name = NULL;
	result = dns_message_gettempname(client->message, &name);
587
	if (result != ISC_R_SUCCESS) {
588
589
		CTRACE(ISC_LOG_DEBUG(3),
		       "query_newname: dns_message_gettempname failed: done");
Bob Halley's avatar
Bob Halley committed
590
		return (NULL);
591
	}
592
593
	isc_buffer_availableregion(dbuf, &r);
	isc_buffer_init(nbuf, r.base, r.length);
594
595
596
597
	dns_name_init(name, NULL);
	dns_name_setbuffer(name, nbuf);
	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;

598
	CTRACE(ISC_LOG_DEBUG(3), "query_newname: done");
599
600
601
602
603
604
605
	return (name);
}

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

607
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset");
Bob Halley's avatar
Bob Halley committed
608
609
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
610
	if (result != ISC_R_SUCCESS) {
611
612
	  CTRACE(ISC_LOG_DEBUG(3),
		 "query_newrdataset: "
David Lawrence's avatar
David Lawrence committed
613
		 "dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
614
		return (NULL);
615
	}
616

617
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset: done");
618
619
620
	return (rdataset);
}

621
622
623
624
625
626
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
627
		dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
628
629
630
		if (dbversion != NULL) {
			dbversion->db = NULL;
			dbversion->version = NULL;
631
			ISC_LIST_INITANDAPPEND(client->query.freeversions,
632
					      dbversion, link);
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
		} 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);
661

662
663
664
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
665
666
isc_result_t
ns_query_init(ns_client_t *client) {
667
668
	isc_result_t result;

669
670
	REQUIRE(NS_CLIENT_VALID(client));

Bob Halley's avatar
Bob Halley committed
671
	ISC_LIST_INIT(client->query.namebufs);
672
673
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
674
	client->query.restarts = 0;
675
	client->query.timerset = ISC_FALSE;
676
	client->query.rpz_st = NULL;
677
	client->query.qname = NULL;
678
679
680
681
	/*
	 * This mutex is destroyed when the client is destroyed in
	 * exit_check().
	 */
682
683
684
	result = isc_mutex_init(&client->query.fetchlock);
	if (result != ISC_R_SUCCESS)
		return (result);
685
	client->query.fetch = NULL;
686
	client->query.prefetch = NULL;
Michael Graff's avatar
   
Michael Graff committed
687
	client->query.authdb = NULL;
688
689
	client->query.authzone = NULL;
	client->query.authdbset = ISC_FALSE;
690
	client->query.isreferral = ISC_FALSE;
691
692
693
694
	client->query.dns64_aaaa = NULL;
	client->query.dns64_sigaaaa = NULL;
	client->query.dns64_aaaaok = NULL;
	client->query.dns64_aaaaoklen = 0;
695
	query_reset(client, ISC_FALSE);
696
	result = query_newdbversion(client, 3);
697
698
	if (result != ISC_R_SUCCESS) {
		DESTROYLOCK(&client->query.fetchlock);
699
		return (result);
700
	}
Mark Andrews's avatar
Mark Andrews committed
701
	result = query_newnamebuf(client);
702
	if (result != ISC_R_SUCCESS) {
703
		query_freefreeversions(client, ISC_TRUE);
704
705
		DESTROYLOCK(&client->query.fetchlock);
	}
706

Mark Andrews's avatar
Mark Andrews committed
707
	return (result);
Bob Halley's avatar
Bob Halley committed
708
709
}

710
static ns_dbversion_t *
711
query_findversion(ns_client_t *client, dns_db_t *db) {
712
713
	ns_dbversion_t *dbversion;

714
	/*%
715
716
717
718
719
720
721
722
723
	 * 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
724
725
	}

726
727
728
729
730
731
732
733
734
735
	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);
736
		dbversion->acl_checked = ISC_FALSE;
737
		dbversion->queryok = ISC_FALSE;
738
739
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
740
	}
741

742
743
744
745
	return (dbversion);
}

static inline isc_result_t
746
747
748
749
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)
750
751
{
	isc_result_t result;
Evan Hunt's avatar
Evan Hunt committed
752
	dns_acl_t *queryacl, *queryonacl;
753
754
	ns_dbversion_t *dbversion;

755
756
	REQUIRE(zone != NULL);
	REQUIRE(db != NULL);
757

Michael Graff's avatar
   
Michael Graff committed
758
759
	/*
	 * This limits our searching to the zone where the first name
760
	 * (the query target) was looked for.  This prevents following
761
	 * CNAMES or DNAMES into other zones and prevents returning
762
	 * additional data from other zones.
Michael Graff's avatar
   
Michael Graff committed
763
	 */
764
	if (!client->view->additionalfromauth &&
765
	    client->query.authdbset &&
766
	    db != client->query.authdb)
767
		return (DNS_R_REFUSED);
Michael Graff's avatar
   
Michael Graff committed
768

769
770
771
772
773
774
775
	/*
	 * 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)) {
776
		return (DNS_R_REFUSED);
777
778
	}

779
	/*
780
	 * If the zone has an ACL, we'll check it, otherwise
781
782
783
	 * we use the view's "allow-query" ACL.  Each ACL is only checked
	 * once per query.
	 *
784
	 * Also, get the database version to use.
785
786
	 */

787
788
789
	/*
	 * Get the current version of this database.
	 */
790
	dbversion = query_findversion(client, db);
791
792
	if (dbversion == NULL) {
		CTRACE(ISC_LOG_ERROR, "unable to get db version");
793
		return (DNS_R_SERVFAIL);
794
	}
795
796
797
798
799
800
801

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

804
	queryacl = dns_zone_getqueryacl(zone);
805
806
807
808
809
810
811
812
813
814
	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.
			 */
815
			dbversion->acl_checked = ISC_TRUE;
816
			if ((client->query.attributes &
817
818
819
820
821
822
			     NS_QUERYATTR_QUERYOK) == 0) {
				dbversion->queryok = ISC_FALSE;
				return (DNS_R_REFUSED);
			}
			dbversion->queryok = ISC_TRUE;
			goto approved;
823
824
825
		}
	}

826
827
828
829
830
	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))) {
831
				ns_client_aclmsg("query", name, qtype,
832
833
						 client->view->rdclass,
						 msg, sizeof(msg));
834
835
836
837
838
				ns_client_log(client,
					      DNS_LOGCATEGORY_SECURITY,
					      NS_LOGMODULE_QUERY,
					      ISC_LOG_DEBUG(3),
					      "%s approved", msg);
839
			}
840
841
842
843
844
845
846
		} 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);
847
		}
848
	}
849

850
851
	if (queryacl == client->view->queryacl) {
		if (result == ISC_R_SUCCESS) {
852
			/*
853
854
855
			 * We were allowed by the default
			 * "allow-query" ACL.  Remember this so we
			 * don't have to check again.
856
			 */
857
			client->query.attributes |= NS_QUERYATTR_QUERYOK;
Michael Graff's avatar
   
Michael Graff committed
858
		}
859
860
861
862
863
		/*
		 * We've now evaluated the view's query ACL, and
		 * the NS_QUERYATTR_QUERYOK attribute is now valid.
		 */
		client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
864
865
	}

Evan Hunt's avatar
Evan Hunt committed
866
867
868
869
870
871
	/* 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;

872
		result = ns_client_checkaclsilent(client, &client->destaddr,
Evan Hunt's avatar
Evan Hunt committed
873
874
875
876
877
878
879
880
						  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");
	}

881
882
883
884
885
	dbversion->acl_checked = ISC_TRUE;
	if (result != ISC_R_SUCCESS) {
		dbversion->queryok = ISC_FALSE;
		return (DNS_R_REFUSED);
	}
886
	dbversion->queryok = ISC_TRUE;
887

888
 approved:
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
	/* 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);

909
	/*%
910
911
912
913
914
915
916
	 * 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
917

918
919
920
921
922
	if (result == DNS_R_PARTIALMATCH)
		partial = ISC_TRUE;
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
		result = dns_zone_getdb(zone, &db);

923
	if (result != ISC_R_SUCCESS)
924
925
926
927
928
929
930
931
		goto fail;

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

	if (result != ISC_R_SUCCESS)
		goto fail;

932
933
934
935
	/* Transfer ownership. */
	*zonep = zone;
	*dbp = db;

936
937
	if (partial && (options & DNS_GETDB_PARTIAL) != 0)
		return (DNS_R_PARTIALMATCH);
938
939
940
941
942
943
944
	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
945

946
	return (result);
947
948
}

949
static void
950
rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
951
		dns_rpz_policy_t policy, dns_rpz_type_t type,
952
953
954
		dns_zone_t *p_zone, dns_name_t *p_name)
{
	isc_stats_t *zonestats;
955
	char qname_buf[DNS_NAME_FORMATSIZE];
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
	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);
	}
972

973
	if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
974
975
		return;

976
	dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
977
	dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
978

979
980
	ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
		      DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s",
981
		      disabled ? "disabled " : "",
982
		      dns_rpz_type2str(type), dns_rpz_policy2str(policy),
983
		      qname_buf, p_name_buf);
984
985
986
}

static void
987
988
989
rpz_log_fail_helper(ns_client_t *client, int level, dns_name_t *p_name,
		    dns_rpz_type_t rpz_type1, dns_rpz_type_t rpz_type2,
		    const char *str, isc_result_t result)
990
{
991
992
	char qnamebuf[DNS_NAME_FORMATSIZE];
	char p_namebuf[DNS_NAME_FORMATSIZE];
Evan Hunt's avatar
Evan Hunt committed
993
	const char *failed;
994
	const char *slash;
Evan Hunt's avatar
Evan Hunt committed
995
996
	const char *via;
	const char *str_blank;
997
998
	const char *rpztypestr1;
	const char *rpztypestr2;
999