query.c 267 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>

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

19
#include <isc/hex.h>
Bob Halley's avatar
add  
Bob Halley committed
20
#include <isc/mem.h>
Mark Andrews's avatar
Mark Andrews committed
21
#include <isc/print.h>
Evan Hunt's avatar
Evan Hunt committed
22
#include <isc/rwlock.h>
23
#include <isc/serial.h>
24
#include <isc/stats.h>
25
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
26
#include <isc/util.h>
Bob Halley's avatar
add  
Bob Halley committed
27

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

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

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

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

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

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

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

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

167

168 169
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
170
#define DNS_GETDB_PARTIAL 0x04U
171
#define DNS_GETDB_IGNOREACL 0x08U
172

173 174
#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)

Evan Hunt's avatar
Evan Hunt committed
175 176
#define SFCACHE_CDFLAG 0x1

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
static bool
199 200 201
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
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,
206
		       dns_name_t *fname, bool exact,
207 208
		       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
static bool
216 217 218
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
	 *
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 279
	else if (client->message->rcode == dns_rcode_badcookie)
		counter = dns_nsstatscounter_badcookie;
280
	else /* We end up here in case of YXDOMAIN, and maybe others */
281
		counter = dns_nsstatscounter_failure;
282

283 284 285 286 287
	inc_stats(client, counter);
	ns_client_send(client);
}

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

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

304 305 306
	if (ns_g_server->log_queries)
		loglevel = ISC_LOG_INFO;

307 308
	log_queryerror(client, result, line, loglevel);

309 310 311 312 313
	ns_client_error(client, result);
}

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

323
static inline void
324
query_freefreeversions(ns_client_t *client, bool everything) {
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
	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));
		}
	}
}

346 347
void
ns_query_cancel(ns_client_t *client) {
348 349
	REQUIRE(NS_CLIENT_VALID(client));

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

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

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

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

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

377 378
	CTRACE(ISC_LOG_DEBUG(3), "query_reset");

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

383
	/*
384
	 * Cancel the fetch if it's running.
385
	 */
386
	ns_query_cancel(client);
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,
396
				    false);
397
		dns_db_detach(&dbversion->db);
398
		ISC_LIST_INITANDAPPEND(client->query.freeversions,
399
				      dbversion, link);
400 401 402
	}
	ISC_LIST_INIT(client->query.activeversions);

403 404
	if (client->query.authdb != NULL)
		dns_db_detach(&client->query.authdb);
405 406
	if (client->query.authzone != NULL)
		dns_zone_detach(&client->query.authzone);
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 *
415
			    sizeof(bool));
416 417 418 419
		client->query.dns64_aaaaok =  NULL;
		client->query.dns64_aaaaoklen =  0;
	}

420 421 422 423 424 425 426 427 428 429 430
	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);

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

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

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

static void
478
query_next_callback(ns_client_t *client) {
479
	query_reset(client, false);
Bob Halley's avatar
Bob Halley committed
480 481
}

482 483
void
ns_query_free(ns_client_t *client) {
484 485
	REQUIRE(NS_CLIENT_VALID(client));

486
	query_reset(client, true);
Bob Halley's avatar
Bob Halley committed
487 488 489 490
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
491
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
492 493
	isc_result_t result;

494
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf");
495
	/*%
496 497 498
	 * Allocate a name buffer.
	 */

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

508
	CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
509 510 511
	return (ISC_R_SUCCESS);
}

512
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
513
query_getnamebuf(ns_client_t *client) {
514
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
515 516 517
	isc_result_t result;
	isc_region_t r;

518
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf");
519
	/*%
520 521 522 523
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

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

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

		}
Bob Halley's avatar
Bob Halley committed
544
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
545
		isc_buffer_availableregion(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
546 547
		INSIST(r.length >= 255);
	}
548
	CTRACE(ISC_LOG_DEBUG(3), "query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
549 550 551
	return (dbuf);
}

552
static inline void
553
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
554 555
	isc_region_t r;

556
	CTRACE(ISC_LOG_DEBUG(3), "query_keepname");
557
	/*%
558 559 560 561 562 563 564
	 * '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);
565
	isc_buffer_add(dbuf, r.length);
566 567 568 569 570 571 572 573
	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;

574
	/*%
575 576 577 578 579
	 * '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.
	 */

580
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename");
581 582 583 584 585
	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
586
	dns_message_puttempname(client->message, namep);
587
	CTRACE(ISC_LOG_DEBUG(3), "query_releasename: done");
588 589 590
}

static inline dns_name_t *
591
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
592 593 594 595 596 597 598 599
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

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

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

614
	CTRACE(ISC_LOG_DEBUG(3), "query_newname: done");
615 616 617 618 619 620 621
	return (name);
}

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

623
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset");
Bob Halley's avatar
Bob Halley committed
624 625
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
626
	if (result != ISC_R_SUCCESS) {
627 628
	  CTRACE(ISC_LOG_DEBUG(3),
		 "query_newrdataset: "
629
		 "dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
630
		return (NULL);
631
	}
632

633
	CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset: done");
634 635 636
	return (rdataset);
}

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

678 679 680
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
681 682
isc_result_t
ns_query_init(ns_client_t *client) {
683 684
	isc_result_t result;

685 686
	REQUIRE(NS_CLIENT_VALID(client));

Bob Halley's avatar
Bob Halley committed
687
	ISC_LIST_INIT(client->query.namebufs);
688 689
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
690
	client->query.restarts = 0;
691
	client->query.timerset = false;
692
	client->query.rpz_st = NULL;
693
	client->query.qname = NULL;
694 695 696 697
	/*
	 * This mutex is destroyed when the client is destroyed in
	 * exit_check().
	 */
698 699 700
	result = isc_mutex_init(&client->query.fetchlock);
	if (result != ISC_R_SUCCESS)
		return (result);
701
	client->query.fetch = NULL;
702
	client->query.prefetch = NULL;
703
	client->query.authdb = NULL;
704
	client->query.authzone = NULL;
705 706
	client->query.authdbset = false;
	client->query.isreferral = false;
707 708 709 710
	client->query.dns64_aaaa = NULL;
	client->query.dns64_sigaaaa = NULL;
	client->query.dns64_aaaaok = NULL;
	client->query.dns64_aaaaoklen = 0;
711 712 713 714 715 716 717
	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;
718 719
	client->query.redirect.authoritative = false;
	client->query.redirect.is_zone  = false;
720
	client->query.redirect.fname =
721
		dns_fixedname_initname(&client->query.redirect.fixed);
722
	query_reset(client, false);
723
	result = query_newdbversion(client, 3);
724 725
	if (result != ISC_R_SUCCESS) {
		DESTROYLOCK(&client->query.fetchlock);
726
		return (result);
727
	}
Mark Andrews's avatar
Mark Andrews committed
728
	result = query_newnamebuf(client);
729
	if (result != ISC_R_SUCCESS) {
730
		query_freefreeversions(client, true);
731 732
		DESTROYLOCK(&client->query.fetchlock);
	}
733

Mark Andrews's avatar
Mark Andrews committed
734
	return (result);
Bob Halley's avatar
Bob Halley committed
735 736
}

737
static ns_dbversion_t *
738
query_findversion(ns_client_t *client, dns_db_t *db) {
739 740
	ns_dbversion_t *dbversion;

741
	/*%
742 743 744 745 746 747 748 749 750
	 * 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;
751 752
	}

753 754 755 756 757 758 759 760 761 762
	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);
763 764
		dbversion->acl_checked = false;
		dbversion->queryok = false;
765 766
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
767
	}
768

769 770 771 772
	return (dbversion);
}

static inline isc_result_t
773 774 775 776
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)
777 778
{
	isc_result_t result;
779
	dns_acl_t *queryacl, *queryonacl;
780 781
	ns_dbversion_t *dbversion;

782 783
	REQUIRE(zone != NULL);
	REQUIRE(db != NULL);
784

785 786
	/*
	 * This limits our searching to the zone where the first name
787
	 * (the query target) was looked for.  This prevents following
788
	 * CNAMES or DNAMES into other zones and prevents returning
789
	 * additional data from other zones.
790
	 */
791
	if (!client->view->additionalfromauth &&
792
	    client->query.authdbset &&
793
	    db != client->query.authdb)
794
		return (DNS_R_REFUSED);
795

796 797 798 799 800 801 802
	/*
	 * 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)) {
803
		return (DNS_R_REFUSED);
804 805
	}

806
	/*
807
	 * If the zone has an ACL, we'll check it, otherwise
808 809 810
	 * we use the view's "allow-query" ACL.  Each ACL is only checked
	 * once per query.
	 *
811
	 * Also, get the database version to use.
812 813
	 */

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

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

831
	queryacl = dns_zone_getqueryacl(zone);
832 833 834 835 836 837 838 839 840 841
	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.
			 */
842
			dbversion->acl_checked = true;
843
			if ((client->query.attributes &
844
			     NS_QUERYATTR_QUERYOK) == 0) {
845
				dbversion->queryok = false;
846 847
				return (DNS_R_REFUSED);
			}
848
			dbversion->queryok = true;
849
			goto approved;
850 851 852
		}
	}

853
	result = ns_client_checkaclsilent(client, NULL, queryacl, true);
854 855 856 857
	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))) {
858
				ns_client_aclmsg("query", name, qtype,
859 860
						 client->view->rdclass,
						 msg, sizeof(msg));
861 862 863 864 865
				ns_client_log(client,
					      DNS_LOGCATEGORY_SECURITY,
					      NS_LOGMODULE_QUERY,
					      ISC_LOG_DEBUG(3),
					      "%s approved", msg);
866
			}
867 868 869 870 871 872 873
		} 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);
874
		}
875
	}
876

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

893 894 895 896 897 898
	/* 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;

899
		result = ns_client_checkaclsilent(client, &client->destaddr,
900
						  queryonacl, true);
901 902 903 904 905 906 907
		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");
	}

908
	dbversion->acl_checked = true;
909
	if (result != ISC_R_SUCCESS) {
910
		dbversion->queryok = false;
911 912
		return (DNS_R_REFUSED);
	}
913
	dbversion->queryok = true;
914

915
 approved:
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;
931
	bool partial = false;
932 933 934 935

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

936
	/*%
937 938 939 940 941 942 943
	 * 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);
944

945
	if (result == DNS_R_PARTIALMATCH)
946
		partial = true;
947 948 949
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
		result = dns_zone_getdb(zone, &db);

950
	if (result != ISC_R_SUCCESS)
951 952 953 954 955 956 957 958
		goto fail;

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

	if (result != ISC_R_SUCCESS)
		goto fail;

959 960 961 962
	/* Transfer ownership. */
	*zonep = zone;
	*dbp = db;

963 964
	if (partial && (options & DNS_GETDB_PARTIAL) != 0)
		return (DNS_R_PARTIALMATCH);
965 966 967 968 969 970 971
	return (ISC_R_SUCCESS);

 fail:
	if (zone != NULL)
		dns_zone_detach(&zone);
	if (db != NULL)
		dns_db_detach(&db);
972

973
	return (result);
974 975
}

976
static void
977
rpz_log_rewrite(ns_client_t *client, bool disabled,
978
		dns_rpz_policy_t policy, dns_rpz_type_t type,
979
		dns_zone_t *p_zone, dns_name_t *p_name,
980
		dns_name_t *cname, dns_rpz_num_t rpz_num)
981
{
982
	char cname_buf[DNS_NAME_FORMATSIZE] = { 0 };
Mark Andrews's avatar
Mark Andrews committed
983 984
	char p_name_buf[DNS_NAME_FORMATSIZE];
	char qname_buf[DNS_NAME_FORMATSIZE];
Mark Andrews's avatar
Mark Andrews committed
985 986
	char classbuf[DNS_RDATACLASS_FORMATSIZE];
	char typebuf[DNS_RDATATYPE_FORMATSIZE];
987
	const char *s1 = cname_buf, *s2 = cname_buf;
Mark Andrews's avatar
Mark Andrews committed
988
	dns_rdataset_t *rdataset;
989
	dns_rpz_st_t *st;
Mark Andrews's avatar
Mark Andrews committed
990
	isc_stats_t *zonestats;
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005

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

1007
	if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
1008 1009
		return;

1010 1011 1012 1013
	st = client->query.rpz_st;
	if ((st->popt.no_log & DNS_RPZ_ZBIT(rpz_num)) != 0)
		return;

1014
	dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
1015
	dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
1016 1017 1018 1019 1020
	if (cname != NULL) {
		s1 = " (CNAME to: ";
		dns_name_format(cname, cname_buf, sizeof(cname_buf));
		s2 = ")";
	}
1021

Mark Andrews's avatar
Mark Andrews committed
1022 1023 1024 1025 1026 1027
	/*
	 *  Log Qclass and Qtype in addition to existing
	 *  fields.
	 */
	rdataset = ISC_LIST_HEAD(client->query.origqname->list);
	INSIST(rdataset != NULL);
Mark Andrews's avatar
Mark Andrews committed
1028 1029
	dns_rdataclass_format(rdataset->rdclass, classbuf, sizeof(classbuf));
	dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf));
Mark Andrews's avatar
Mark Andrews committed
1030

1031
	ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
Mark Andrews's avatar
Mark Andrews committed
1032 1033 1034 1035 1036 1037
		      DNS_RPZ_INFO_LEVEL,
#ifdef RPZ_LOG_QTYPE_QCLASS
		      "%srpz %s %s rewrite %s/%s/%s via %s%s%s%s",
#else /* RPZ_LOG_QTYPE_QCLASS */
		      "%srpz %s %s rewrite %s via %s%s%s%s",
#endif /* RPZ_LOG_QTYPE_QCLASS */