query.c 258 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
2
 * Copyright (C) 2004-2016  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>
30
#include <isc/thread.h>
Michael Graff's avatar
Michael Graff committed
31
#include <isc/util.h>
Bob Halley's avatar
add  
Bob Halley committed
32

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

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

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

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

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

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

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

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

171

172 173
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
174
#define DNS_GETDB_PARTIAL 0x04U
175
#define DNS_GETDB_IGNOREACL 0x08U
176

177 178
#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)

Evan Hunt's avatar
Evan Hunt committed
179 180
#define SFCACHE_CDFLAG 0x1

181 182 183 184 185 186 187 188 189 190 191 192 193
/*
 * 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)

194 195 196 197 198
typedef struct client_additionalctx {
	ns_client_t *client;
	dns_rdataset_t *rdataset;
} client_additionalctx_t;

199
static isc_result_t
200
query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
201

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

206 207 208 209 210 211 212
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);

213 214 215
static inline void
log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);

216 217 218
static void
rpz_st_clear(ns_client_t *client);

219 220 221 222
static isc_boolean_t
rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
	      dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);

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

234
	isc_stats_increment(ns_g_server->nsstats, counter);
235

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

262 263
static void
query_send(ns_client_t *client) {
264
	isc_statscounter_t counter;
265

266 267 268 269
	if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0)
		inc_stats(client, dns_nsstatscounter_nonauthans);
	else
		inc_stats(client, dns_nsstatscounter_authans);
270

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

287 288 289 290 291
	inc_stats(client, counter);
	ns_client_send(client);
}

static void
292 293 294
query_error(ns_client_t *client, isc_result_t result, int line) {
	int loglevel = ISC_LOG_DEBUG(3);

295 296
	switch (result) {
	case DNS_R_SERVFAIL:
297
		loglevel = ISC_LOG_DEBUG(1);
298 299 300 301 302 303 304 305 306
		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;
	}
307

308 309 310
	if (ns_g_server->log_queries)
		loglevel = ISC_LOG_INFO;

311 312
	log_queryerror(client, result, line, loglevel);

313 314 315 316 317
	ns_client_error(client, result);
}

static void
query_next(ns_client_t *client, isc_result_t result) {
318
	if (result == DNS_R_DUPLICATE)
319
		inc_stats(client, dns_nsstatscounter_duplicate);
320
	else if (result == DNS_R_DROP)
321
		inc_stats(client, dns_nsstatscounter_dropped);
322
	else
323
		inc_stats(client, dns_nsstatscounter_failure);
324 325 326
	ns_client_next(client, result);
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
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));
		}
	}
}

350 351 352 353 354 355 356 357 358 359 360
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);
}

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

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

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

379 380
	CTRACE(ISC_LOG_DEBUG(3), "query_reset");

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

385
	/*
386
	 * Cancel the fetch if it's running.
387
	 */
388
	ns_query_cancel(client);
389

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

Michael Graff's avatar
 
Michael Graff committed
405 406
	if (client->query.authdb != NULL)
		dns_db_detach(&client->query.authdb);
407 408
	if (client->query.authzone != NULL)
		dns_zone_detach(&client->query.authzone);
Michael Graff's avatar
 
Michael Graff committed
409

410 411 412 413 414 415 416 417 418 419 420 421
	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;
	}

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

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

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

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

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

481 482 483
void
ns_query_free(ns_client_t *client) {
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
484 485 486 487
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

675 676 677
	return (dbversion);
}

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

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

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

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

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

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

763 764 765 766
	return (dbversion);
}

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

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

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

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

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

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

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

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

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

871 872
	if (queryacl == client->view->queryacl) {
		if (result == ISC_R_SUCCESS) {
873
			/*
874 875 876
			 * We were allowed by the default
			 * "allow-query" ACL.  Remember this so we
			 * don't have to check again.
877
			 */
878
			client->query.attributes |= NS_QUERYATTR_QUERYOK;
Michael Graff's avatar
 
Michael Graff committed
879