query.c 251 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
2
 * Copyright (C) 2004-2015  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
 */

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

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

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

24
#include <isc/hex.h>
Bob Halley's avatar
add  
Bob Halley committed
25
#include <isc/mem.h>
Mark Andrews's avatar
Mark Andrews committed
26
#include <isc/print.h>
Evan Hunt's avatar
Evan Hunt committed
27
#include <isc/rwlock.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>
Evan Hunt's avatar
Evan Hunt committed
33
#include <dns/badcache.h>
34
#include <dns/byaddr.h>
35
#include <dns/cache.h>
Bob Halley's avatar
Bob Halley committed
36
#include <dns/db.h>
37
#include <dns/dlz.h>
38
#include <dns/dns64.h>
39
#include <dns/dnssec.h>
Bob Halley's avatar
add  
Bob Halley committed
40
#include <dns/events.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)
96
97
/*% Is TCP? */
#define TCP(c)			(((c)->attributes & NS_CLIENTATTR_TCP) != 0)
98
/*% Want DNSSEC? */
99
100
#define WANTDNSSEC(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTDNSSEC) != 0)
101
102
103
/*% Want WANTAD? */
#define WANTAD(c)		(((c)->attributes & \
				  NS_CLIENTATTR_WANTAD) != 0)
104
105
106
107
108
109
/*% 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)
110
/*% No authority? */
111
112
#define NOAUTHORITY(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOAUTHORITY) != 0)
113
/*% No additional? */
114
115
#define NOADDITIONAL(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_NOADDITIONAL) != 0)
116
/*% Secure? */
117
118
#define SECURE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_SECURE) != 0)
119
120
121
122
123
124
/*% 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
125

126
127
128
#define REDIRECT(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_REDIRECT) != 0)

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

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#ifdef WANT_QUERYTRACE
#define CTRACE(l,m)	  do {						\
	if (client != NULL && client->query.qname != NULL) {		\
		if (isc_log_wouldlog(ns_g_lctx, l)) {			\
			char qbuf[DNS_NAME_FORMATSIZE];			\
			dns_name_format(client->query.qname,		\
					qbuf, sizeof(qbuf));		\
			isc_log_write(ns_g_lctx,			\
				      NS_LOGCATEGORY_CLIENT,		\
				      NS_LOGMODULE_QUERY,		\
				      l, "client %p (%s): %s",		\
				      client, qbuf, (m));		\
		}							\
	 } else {							\
		isc_log_write(ns_g_lctx,				\
			      NS_LOGCATEGORY_CLIENT,			\
			      NS_LOGMODULE_QUERY,			\
			      l, "client %p (<unknown-name>): %s",	\
			      client, (m));				\
	}								\
153
} while(0)
154
#else
155
#define CTRACE(l,m) ((void)m)
156
#endif /* WANT_QUERYTRACE */
157

158

159
160
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
161
#define DNS_GETDB_PARTIAL 0x04U
162
#define DNS_GETDB_IGNOREACL 0x08U
163

164
165
#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)

Evan Hunt's avatar
Evan Hunt committed
166
167
#define SFCACHE_CDFLAG 0x1

168
169
170
171
172
typedef struct client_additionalctx {
	ns_client_t *client;
	dns_rdataset_t *rdataset;
} client_additionalctx_t;

173
static isc_result_t
174
query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
175

176
177
178
179
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);

180
181
182
183
184
185
186
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);

187
188
189
static inline void
log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);

190
191
192
static void
rpz_st_clear(ns_client_t *client);

193
194
195
196
static isc_boolean_t
rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
	      dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);

197
/*%
198
199
 * Increment query statistics counters.
 */
200
static inline void
201
inc_stats(ns_client_t *client, isc_statscounter_t counter) {
202
	dns_zone_t *zone = client->query.authzone;
203
204
205
206
	dns_rdatatype_t qtype;
	dns_rdataset_t *rdataset;
	isc_stats_t *zonestats;
	dns_stats_t *querystats = NULL;
207

208
	isc_stats_increment(ns_g_server->nsstats, counter);
209

210
211
212
213
214
215
216
217
218
219
220
	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
221
	 * We only increment per-type if we're using the authoritative
222
223
224
225
226
227
228
229
230
231
232
	 * 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);
			}
		}
233
234
	}
}
235

236
237
static void
query_send(ns_client_t *client) {
238
	isc_statscounter_t counter;
239

240
241
242
243
	if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0)
		inc_stats(client, dns_nsstatscounter_nonauthans);
	else
		inc_stats(client, dns_nsstatscounter_authans);
244

245
	if (client->message->rcode == dns_rcode_noerror) {
246
247
248
		dns_section_t answer = DNS_SECTION_ANSWER;
		if (ISC_LIST_EMPTY(client->message->sections[answer])) {
			if (client->query.isreferral)
249
				counter = dns_nsstatscounter_referral;
250
			else
251
				counter = dns_nsstatscounter_nxrrset;
252
		} else
253
			counter = dns_nsstatscounter_success;
254
	} else if (client->message->rcode == dns_rcode_nxdomain)
255
		counter = dns_nsstatscounter_nxdomain;
256
257
	else if (client->message->rcode == dns_rcode_badcookie)
		counter = dns_nsstatscounter_badcookie;
258
	else /* We end up here in case of YXDOMAIN, and maybe others */
259
		counter = dns_nsstatscounter_failure;
260

261
262
263
264
265
	inc_stats(client, counter);
	ns_client_send(client);
}

static void
266
267
268
query_error(ns_client_t *client, isc_result_t result, int line) {
	int loglevel = ISC_LOG_DEBUG(3);

269
270
	switch (result) {
	case DNS_R_SERVFAIL:
271
		loglevel = ISC_LOG_DEBUG(1);
272
273
274
275
276
277
278
279
280
		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;
	}
281

282
283
284
	if (ns_g_server->log_queries)
		loglevel = ISC_LOG_INFO;

285
286
	log_queryerror(client, result, line, loglevel);

287
288
289
290
291
	ns_client_error(client, result);
}

static void
query_next(ns_client_t *client, isc_result_t result) {
292
	if (result == DNS_R_DUPLICATE)
293
		inc_stats(client, dns_nsstatscounter_duplicate);
294
	else if (result == DNS_R_DROP)
295
		inc_stats(client, dns_nsstatscounter_dropped);
296
	else
297
		inc_stats(client, dns_nsstatscounter_failure);
298
299
300
	ns_client_next(client, result);
}

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
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));
		}
	}
}

324
325
326
327
328
329
330
331
332
333
334
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);
}

335
336
337
338
static inline void
query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
	dns_rdataset_t *rdataset = *rdatasetp;

339
	CTRACE(ISC_LOG_DEBUG(3), "query_putrdataset");
340
341
342
343
344
	if (rdataset != NULL) {
		if (dns_rdataset_isassociated(rdataset))
			dns_rdataset_disassociate(rdataset);
		dns_message_puttemprdataset(client->message, rdatasetp);
	}
345
	CTRACE(ISC_LOG_DEBUG(3), "query_putrdataset: done");
346
347
}

Bob Halley's avatar
Bob Halley committed
348
static inline void
349
query_reset(ns_client_t *client, isc_boolean_t everything) {
350
	isc_buffer_t *dbuf, *dbuf_next;
351
352
	ns_dbversion_t *dbversion, *dbversion_next;

353
	/*%
354
355
	 * Reset the query state of a client to its default state.
	 */
356

357
	/*
358
	 * Cancel the fetch if it's running.
359
	 */
360
	ns_query_cancel(client);
361

362
363
364
365
366
367
368
369
370
371
	/*
	 * 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);
372
		ISC_LIST_INITANDAPPEND(client->query.freeversions,
373
				      dbversion, link);
374
375
376
	}
	ISC_LIST_INIT(client->query.activeversions);

Michael Graff's avatar
   
Michael Graff committed
377
378
	if (client->query.authdb != NULL)
		dns_db_detach(&client->query.authdb);
379
380
	if (client->query.authzone != NULL)
		dns_zone_detach(&client->query.authzone);
Michael Graff's avatar
   
Michael Graff committed
381

382
383
384
385
386
387
388
389
390
391
392
393
	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;
	}

394
395
396
397
398
399
400
401
402
403
404
	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);

405
	query_freefreeversions(client, everything);
Bob Halley's avatar
Bob Halley committed
406
407
408
409
410
411
412

	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);
413
			isc_buffer_free(&dbuf);
Bob Halley's avatar
Bob Halley committed
414
415
		}
	}
Bob Halley's avatar
Bob Halley committed
416

417
418
419
420
421
422
423
424
	if (client->query.restarts > 0) {
		/*
		 * client->query.qname was dynamically allocated.
		 */
		dns_message_puttempname(client->message,
					&client->query.qname);
	}
	client->query.qname = NULL;
425
	client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
426
427
				    NS_QUERYATTR_CACHEOK |
				    NS_QUERYATTR_SECURE);
428
	client->query.restarts = 0;
429
	client->query.timerset = ISC_FALSE;
430
431
432
433
434
435
436
437
	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;
		}
	}
438
	client->query.origqname = NULL;
439
	client->query.dboptions = 0;
440
	client->query.fetchoptions = 0;
441
	client->query.gluedb = NULL;
442
443
	client->query.authdbset = ISC_FALSE;
	client->query.isreferral = ISC_FALSE;
444
445
	client->query.dns64_options = 0;
	client->query.dns64_ttl = ISC_UINT32_MAX;
Bob Halley's avatar
Bob Halley committed
446
447
448
}

static void
449
query_next_callback(ns_client_t *client) {
450
	query_reset(client, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
451
452
}

453
454
455
void
ns_query_free(ns_client_t *client) {
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
456
457
458
459
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
460
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
461
462
	isc_result_t result;

463
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf");
464
	/*%
465
466
467
	 * Allocate a name buffer.
	 */

Bob Halley's avatar
Bob Halley committed
468
	dbuf = NULL;
469
	result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
470
	if (result != ISC_R_SUCCESS) {
471
472
		CTRACE(ISC_LOG_DEBUG(3),
		       "query_newnamebuf: isc_buffer_allocate failed: done");
Bob Halley's avatar
Bob Halley committed
473
		return (result);
474
	}
Bob Halley's avatar
Bob Halley committed
475
476
	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);

477
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
478
479
480
	return (ISC_R_SUCCESS);
}

481
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
482
query_getnamebuf(ns_client_t *client) {
483
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
484
485
486
	isc_result_t result;
	isc_region_t r;

487
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf");
488
	/*%
489
490
491
492
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

Bob Halley's avatar
Bob Halley committed
493
494
	if (ISC_LIST_EMPTY(client->query.namebufs)) {
		result = query_newnamebuf(client);
495
		if (result != ISC_R_SUCCESS) {
496
497
		    CTRACE(ISC_LOG_DEBUG(3),
			   "query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
498
			return (NULL);
499
		}
Bob Halley's avatar
Bob Halley committed
500
501
502
503
	}

	dbuf = ISC_LIST_TAIL(client->query.namebufs);
	INSIST(dbuf != NULL);
504
	isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
505
506
	if (r.length < 255) {
		result = query_newnamebuf(client);
507
		if (result != ISC_R_SUCCESS) {
508
509
		    CTRACE(ISC_LOG_DEBUG(3),
			   "query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
510
			return (NULL);
511
512

		}
Bob Halley's avatar
Bob Halley committed
513
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
514
		isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
515
516
		INSIST(r.length >= 255);
	}
517
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
518
519
520
	return (dbuf);
}

521
static inline void
522
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
523
524
	isc_region_t r;

525
	CTRACE(ISC_LOG_DEBUG(3), "query_keepname");
526
	/*%
527
528
529
530
531
532
533
	 * '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);
534
	isc_buffer_add(dbuf, r.length);
535
536
537
538
539
540
541
542
	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;

543
	/*%
544
545
546
547
548
	 * '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.
	 */

549
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename");
550
551
552
553
554
	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
555
	dns_message_puttempname(client->message, namep);
556
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename: done");
557
558
559
}

static inline dns_name_t *
560
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
561
562
563
564
565
566
567
568
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

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

569
	CTRACE(ISC_LOG_DEBUG(3), "query_newname");
Bob Halley's avatar
Bob Halley committed
570
571
	name = NULL;
	result = dns_message_gettempname(client->message, &name);
572
	if (result != ISC_R_SUCCESS) {
573
574
		CTRACE(ISC_LOG_DEBUG(3),
		       "query_newname: dns_message_gettempname failed: done");
Bob Halley's avatar
Bob Halley committed
575
		return (NULL);
576
	}
577
578
	isc_buffer_availableregion(dbuf, &r);
	isc_buffer_init(nbuf, r.base, r.length);
579
580
581
582
	dns_name_init(name, NULL);
	dns_name_setbuffer(name, nbuf);
	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;

583
	CTRACE(ISC_LOG_DEBUG(3), "query_newname: done");
584
585
586
587
588
589
590
	return (name);
}

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

592
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset");
Bob Halley's avatar
Bob Halley committed
593
594
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
595
	if (result != ISC_R_SUCCESS) {
596
597
	  CTRACE(ISC_LOG_DEBUG(3),
		 "query_newrdataset: "
David Lawrence's avatar
David Lawrence committed
598
		 "dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
599
		return (NULL);
600
	}
601

602
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset: done");
603
604
605
	return (rdataset);
}

606
607
608
609
610
611
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
612
		dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
613
614
615
		if (dbversion != NULL) {
			dbversion->db = NULL;
			dbversion->version = NULL;
616
			ISC_LIST_INITANDAPPEND(client->query.freeversions,
617
					      dbversion, link);
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
		} 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);
646

647
648
649
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
650
651
isc_result_t
ns_query_init(ns_client_t *client) {
652
653
	isc_result_t result;

Bob Halley's avatar
Bob Halley committed
654
	ISC_LIST_INIT(client->query.namebufs);
655
656
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
657
	client->query.restarts = 0;
658
	client->query.timerset = ISC_FALSE;
659
	client->query.rpz_st = NULL;
660
	client->query.qname = NULL;
661
662
663
664
	/*
	 * This mutex is destroyed when the client is destroyed in
	 * exit_check().
	 */
665
666
667
	result = isc_mutex_init(&client->query.fetchlock);
	if (result != ISC_R_SUCCESS)
		return (result);
668
	client->query.fetch = NULL;
669
	client->query.prefetch = NULL;
Michael Graff's avatar
   
Michael Graff committed
670
	client->query.authdb = NULL;
671
672
	client->query.authzone = NULL;
	client->query.authdbset = ISC_FALSE;
673
	client->query.isreferral = ISC_FALSE;
674
675
676
677
	client->query.dns64_aaaa = NULL;
	client->query.dns64_sigaaaa = NULL;
	client->query.dns64_aaaaok = NULL;
	client->query.dns64_aaaaoklen = 0;
678
679
680
681
682
683
684
685
686
687
	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;
	dns_fixedname_init(&client->query.redirect.fixed);
	client->query.redirect.fname =
		dns_fixedname_name(&client->query.redirect.fixed);
688
	query_reset(client, ISC_FALSE);
689
	result = query_newdbversion(client, 3);
690
691
	if (result != ISC_R_SUCCESS) {
		DESTROYLOCK(&client->query.fetchlock);
692
		return (result);
693
	}
Mark Andrews's avatar
Mark Andrews committed
694
	result = query_newnamebuf(client);
695
	if (result != ISC_R_SUCCESS) {
696
		query_freefreeversions(client, ISC_TRUE);
697
698
		DESTROYLOCK(&client->query.fetchlock);
	}
699

Mark Andrews's avatar
Mark Andrews committed
700
	return (result);
Bob Halley's avatar
Bob Halley committed
701
702
}

703
static inline ns_dbversion_t *
704
query_findversion(ns_client_t *client, dns_db_t *db) {
705
706
	ns_dbversion_t *dbversion;

707
	/*%
708
709
710
711
712
713
714
715
716
	 * 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
717
718
	}

719
720
721
722
723
724
725
726
727
728
	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);
729
		dbversion->acl_checked = ISC_FALSE;
730
		dbversion->queryok = ISC_FALSE;
731
732
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
733
	}
734

735
736
737
738
	return (dbversion);
}

static inline isc_result_t
739
740
741
742
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)
743
744
{
	isc_result_t result;
Evan Hunt's avatar
Evan Hunt committed
745
	dns_acl_t *queryacl, *queryonacl;
746
747
	ns_dbversion_t *dbversion;

748
749
	REQUIRE(zone != NULL);
	REQUIRE(db != NULL);
750

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

762
763
764
765
766
767
768
	/*
	 * 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)) {
769
		return (DNS_R_REFUSED);
770
771
	}

772
	/*
773
	 * If the zone has an ACL, we'll check it, otherwise
774
775
776
	 * we use the view's "allow-query" ACL.  Each ACL is only checked
	 * once per query.
	 *
777
	 * Also, get the database version to use.
778
779
	 */

780
781
782
	/*
	 * Get the current version of this database.
	 */
783
	dbversion = query_findversion(client, db);
784
785
	if (dbversion == NULL) {
		CTRACE(ISC_LOG_ERROR, "unable to get db version");
786
		return (DNS_R_SERVFAIL);
787
	}
788
789
790
791
792
793
794

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

797
	queryacl = dns_zone_getqueryacl(zone);
798
799
800
801
802
803
804
805
806
807
	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.
			 */
808
			dbversion->acl_checked = ISC_TRUE;
809
			if ((client->query.attributes &
810
811
812
813
814
815
			     NS_QUERYATTR_QUERYOK) == 0) {
				dbversion->queryok = ISC_FALSE;
				return (DNS_R_REFUSED);
			}
			dbversion->queryok = ISC_TRUE;
			goto approved;
816
817
818
		}
	}

819
820
821
822
823
	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))) {
824
				ns_client_aclmsg("query", name, qtype,
825
826
						 client->view->rdclass,
						 msg, sizeof(msg));
827
828
829
830
831
				ns_client_log(client,
					      DNS_LOGCATEGORY_SECURITY,
					      NS_LOGMODULE_QUERY,
					      ISC_LOG_DEBUG(3),
					      "%s approved", msg);
832
			}
833
834
835
836
837
838
839
		} 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);
840
		}
841
	}
842

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

Evan Hunt's avatar
Evan Hunt committed
859
860
861
862
863
864
	/* 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;

865
		result = ns_client_checkaclsilent(client, &client->destaddr,
Evan Hunt's avatar
Evan Hunt committed
866
867
868
869
870
871
872
873
						  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");
	}

874
875
876
877
878
	dbversion->acl_checked = ISC_TRUE;
	if (result != ISC_R_SUCCESS) {
		dbversion->queryok = ISC_FALSE;
		return (DNS_R_REFUSED);
	}
879
	dbversion->queryok = ISC_TRUE;
880

881
 approved:
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
	/* 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);

902
	/*%
903
904
905
906
907
908
909
	 * 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
910

911
912
913
914
915
	if (result == DNS_R_PARTIALMATCH)
		partial = ISC_TRUE;
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
		result = dns_zone_getdb(zone, &db);

916
	if (result != ISC_R_SUCCESS)
917
918
919
920
921
922
923
924
		goto fail;

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

	if (result != ISC_R_SUCCESS)
		goto fail;

925
926
927
928
	/* Transfer ownership. */
	*zonep = zone;
	*dbp = db;

929
930
	if (partial && (options & DNS_GETDB_PARTIAL) != 0)
		return (DNS_R_PARTIALMATCH);
931
932
933
934
935
936
937
	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
938

939
	return (result);
940
941
}

942
static void
943
rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
944
		dns_rpz_policy_t policy, dns_rpz_type_t type,
945
		dns_zone_t *p_zone, dns_name_t *p_name,
946
		dns_name_t *cname, dns_rpz_num_t rpz_num)
947
948
{
	isc_stats_t *zonestats;
949
	char qname_buf[DNS_NAME_FORMATSIZE];
950
	char p_name_buf[DNS_NAME_FORMATSIZE];
951
952
	char cname_buf[DNS_NAME_FORMATSIZE] = { 0 };
	const char *s1 = cname_buf, *s2 = cname_buf;
953
	dns_rpz_st_t *st;
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968

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

970
	if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
971
972
		return;

973
974
975
976
	st = client->query.rpz_st;
	if ((st->popt.no_log & DNS_RPZ_ZBIT(rpz_num)) != 0)
		return;

977
	dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
978
	dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
979
980
981
982
983
	if (cname != NULL) {
		s1 = " (CNAME to: ";
		dns_name_format(cname, cname_buf, sizeof(cname_buf));
		s2 = ")";
	}
984

985
	ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
986
		      DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s%s%s%s",
987
		      disabled ? "disabled " : "",
988
		      dns_rpz_type2str(type), dns_rpz_policy2str(policy),
989
		      qname_buf, p_name_buf, s1, cname_buf, s2);
990
991
992
}

static void
993
994
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)
995
{
996
997
	char qnamebuf[DNS_NAME_FORMATSIZE];
	char p_namebuf[DNS_NAME_FORMATSIZE];
Evan Hunt's avatar
Evan Hunt committed
998
	const char *failed;
999

1000
	if (!isc_log_wouldlog(ns_g_lctx, level))
1001
1002
		return;

1003
	/*
Evan Hunt's avatar
Evan Hunt committed
1004
	 * bin/tests/system/rpz/tests.sh looks for "rpz.*failed" for problems.
1005
	 */
Evan Hunt's avatar
Evan Hunt committed
1006
1007
1008
1009
	if (level <= DNS_RPZ_DEBUG_LEVEL1)
		failed = "failed: ";
	else
		failed = ": ";
1010
1011