PageRenderTime 197ms CodeModel.GetById 101ms app.highlight 74ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/bind9/bin/named/query.c

https://bitbucket.org/freebsd/freebsd-head/
C | 7242 lines | 5285 code | 595 blank | 1362 comment | 1873 complexity | 884d102af774a65093406c7039f80ba4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
   3 * Copyright (C) 1999-2003  Internet Software Consortium.
   4 *
   5 * Permission to use, copy, modify, and/or distribute this software for any
   6 * purpose with or without fee is hereby granted, provided that the above
   7 * copyright notice and this permission notice appear in all copies.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15 * PERFORMANCE OF THIS SOFTWARE.
  16 */
  17
  18/* $Id: query.c,v 1.353.8.24 2012/02/07 01:14:39 marka Exp $ */
  19
  20/*! \file */
  21
  22#include <config.h>
  23
  24#include <string.h>
  25
  26#include <isc/hex.h>
  27#include <isc/mem.h>
  28#include <isc/stats.h>
  29#include <isc/util.h>
  30
  31#include <dns/adb.h>
  32#include <dns/byaddr.h>
  33#include <dns/db.h>
  34#include <dns/dlz.h>
  35#include <dns/dns64.h>
  36#include <dns/dnssec.h>
  37#include <dns/events.h>
  38#include <dns/message.h>
  39#include <dns/ncache.h>
  40#include <dns/nsec3.h>
  41#include <dns/order.h>
  42#include <dns/rdata.h>
  43#include <dns/rdataclass.h>
  44#include <dns/rdatalist.h>
  45#include <dns/rdataset.h>
  46#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>
  52#include <dns/tkey.h>
  53#include <dns/view.h>
  54#include <dns/zone.h>
  55#include <dns/zt.h>
  56
  57#include <named/client.h>
  58#include <named/globals.h>
  59#include <named/log.h>
  60#include <named/server.h>
  61#include <named/sortlist.h>
  62#include <named/xfrout.h>
  63
  64#if 0
  65/*
  66 * It has been recommended that DNS64 be changed to return excluded
  67 * AAAA addresses if DNS64 synthesis does not occur.  This minimises
  68 * the impact on the lookup results.  While most DNS AAAA lookups are
  69 * done to send IP packets to a host, not all of them are and filtering
  70 * excluded addresses has a negative impact on those uses.
  71 */
  72#define dns64_bis_return_excluded_addresses 1
  73#endif
  74
  75/*% Partial answer? */
  76#define PARTIALANSWER(c)	(((c)->query.attributes & \
  77				  NS_QUERYATTR_PARTIALANSWER) != 0)
  78/*% Use Cache? */
  79#define USECACHE(c)		(((c)->query.attributes & \
  80				  NS_QUERYATTR_CACHEOK) != 0)
  81/*% Recursion OK? */
  82#define RECURSIONOK(c)		(((c)->query.attributes & \
  83				  NS_QUERYATTR_RECURSIONOK) != 0)
  84/*% Recursing? */
  85#define RECURSING(c)		(((c)->query.attributes & \
  86				  NS_QUERYATTR_RECURSING) != 0)
  87/*% Cache glue ok? */
  88#define CACHEGLUEOK(c)		(((c)->query.attributes & \
  89				  NS_QUERYATTR_CACHEGLUEOK) != 0)
  90/*% Want Recursion? */
  91#define WANTRECURSION(c)	(((c)->query.attributes & \
  92				  NS_QUERYATTR_WANTRECURSION) != 0)
  93/*% Want DNSSEC? */
  94#define WANTDNSSEC(c)		(((c)->attributes & \
  95				  NS_CLIENTATTR_WANTDNSSEC) != 0)
  96/*% No authority? */
  97#define NOAUTHORITY(c)		(((c)->query.attributes & \
  98				  NS_QUERYATTR_NOAUTHORITY) != 0)
  99/*% No additional? */
 100#define NOADDITIONAL(c)		(((c)->query.attributes & \
 101				  NS_QUERYATTR_NOADDITIONAL) != 0)
 102/*% Secure? */
 103#define SECURE(c)		(((c)->query.attributes & \
 104				  NS_QUERYATTR_SECURE) != 0)
 105/*% DNS64 A lookup? */
 106#define DNS64(c)		(((c)->query.attributes & \
 107				  NS_QUERYATTR_DNS64) != 0)
 108
 109#define DNS64EXCLUDE(c)		(((c)->query.attributes & \
 110				  NS_QUERYATTR_DNS64EXCLUDE) != 0)
 111
 112/*% No QNAME Proof? */
 113#define NOQNAME(r)		(((r)->attributes & \
 114				  DNS_RDATASETATTR_NOQNAME) != 0)
 115
 116#if 0
 117#define CTRACE(m)       isc_log_write(ns_g_lctx, \
 118				      NS_LOGCATEGORY_CLIENT, \
 119				      NS_LOGMODULE_QUERY, \
 120				      ISC_LOG_DEBUG(3), \
 121				      "client %p: %s", client, (m))
 122#define QTRACE(m)       isc_log_write(ns_g_lctx, \
 123				      NS_LOGCATEGORY_GENERAL, \
 124				      NS_LOGMODULE_QUERY, \
 125				      ISC_LOG_DEBUG(3), \
 126				      "query %p: %s", query, (m))
 127#else
 128#define CTRACE(m) ((void)m)
 129#define QTRACE(m) ((void)m)
 130#endif
 131
 132#define DNS_GETDB_NOEXACT 0x01U
 133#define DNS_GETDB_NOLOG 0x02U
 134#define DNS_GETDB_PARTIAL 0x04U
 135#define DNS_GETDB_IGNOREACL 0x08U
 136
 137#define PENDINGOK(x)	(((x) & DNS_DBFIND_PENDINGOK) != 0)
 138
 139typedef struct client_additionalctx {
 140	ns_client_t *client;
 141	dns_rdataset_t *rdataset;
 142} client_additionalctx_t;
 143
 144static isc_result_t
 145query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
 146
 147static isc_boolean_t
 148validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
 149	 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
 150
 151static void
 152query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
 153		       dns_dbversion_t *version, ns_client_t *client,
 154		       dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
 155		       dns_name_t *fname, isc_boolean_t exact,
 156		       dns_name_t *found);
 157
 158static inline void
 159log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);
 160
 161static void
 162rpz_st_clear(ns_client_t *client);
 163
 164/*%
 165 * Increment query statistics counters.
 166 */
 167static inline void
 168inc_stats(ns_client_t *client, isc_statscounter_t counter) {
 169	dns_zone_t *zone = client->query.authzone;
 170
 171	isc_stats_increment(ns_g_server->nsstats, counter);
 172
 173	if (zone != NULL) {
 174		isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
 175		if (zonestats != NULL)
 176			isc_stats_increment(zonestats, counter);
 177	}
 178}
 179
 180static void
 181query_send(ns_client_t *client) {
 182	isc_statscounter_t counter;
 183	if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0)
 184		inc_stats(client, dns_nsstatscounter_nonauthans);
 185	else
 186		inc_stats(client, dns_nsstatscounter_authans);
 187	if (client->message->rcode == dns_rcode_noerror) {
 188		if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER])) {
 189			if (client->query.isreferral) {
 190				counter = dns_nsstatscounter_referral;
 191			} else {
 192				counter = dns_nsstatscounter_nxrrset;
 193			}
 194		} else {
 195			counter = dns_nsstatscounter_success;
 196		}
 197	} else if (client->message->rcode == dns_rcode_nxdomain) {
 198		counter = dns_nsstatscounter_nxdomain;
 199	} else {
 200		/* We end up here in case of YXDOMAIN, and maybe others */
 201		counter = dns_nsstatscounter_failure;
 202	}
 203	inc_stats(client, counter);
 204	ns_client_send(client);
 205}
 206
 207static void
 208query_error(ns_client_t *client, isc_result_t result, int line) {
 209	int loglevel = ISC_LOG_DEBUG(3);
 210
 211	switch (result) {
 212	case DNS_R_SERVFAIL:
 213		loglevel = ISC_LOG_DEBUG(1);
 214		inc_stats(client, dns_nsstatscounter_servfail);
 215		break;
 216	case DNS_R_FORMERR:
 217		inc_stats(client, dns_nsstatscounter_formerr);
 218		break;
 219	default:
 220		inc_stats(client, dns_nsstatscounter_failure);
 221		break;
 222	}
 223
 224	log_queryerror(client, result, line, loglevel);
 225
 226	ns_client_error(client, result);
 227}
 228
 229static void
 230query_next(ns_client_t *client, isc_result_t result) {
 231	if (result == DNS_R_DUPLICATE)
 232		inc_stats(client, dns_nsstatscounter_duplicate);
 233	else if (result == DNS_R_DROP)
 234		inc_stats(client, dns_nsstatscounter_dropped);
 235	else
 236		inc_stats(client, dns_nsstatscounter_failure);
 237	ns_client_next(client, result);
 238}
 239
 240static inline void
 241query_freefreeversions(ns_client_t *client, isc_boolean_t everything) {
 242	ns_dbversion_t *dbversion, *dbversion_next;
 243	unsigned int i;
 244
 245	for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0;
 246	     dbversion != NULL;
 247	     dbversion = dbversion_next, i++)
 248	{
 249		dbversion_next = ISC_LIST_NEXT(dbversion, link);
 250		/*
 251		 * If we're not freeing everything, we keep the first three
 252		 * dbversions structures around.
 253		 */
 254		if (i > 3 || everything) {
 255			ISC_LIST_UNLINK(client->query.freeversions, dbversion,
 256					link);
 257			isc_mem_put(client->mctx, dbversion,
 258				    sizeof(*dbversion));
 259		}
 260	}
 261}
 262
 263void
 264ns_query_cancel(ns_client_t *client) {
 265	LOCK(&client->query.fetchlock);
 266	if (client->query.fetch != NULL) {
 267		dns_resolver_cancelfetch(client->query.fetch);
 268
 269		client->query.fetch = NULL;
 270	}
 271	UNLOCK(&client->query.fetchlock);
 272}
 273
 274static inline void
 275query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
 276	dns_rdataset_t *rdataset = *rdatasetp;
 277
 278	CTRACE("query_putrdataset");
 279	if (rdataset != NULL) {
 280		if (dns_rdataset_isassociated(rdataset))
 281			dns_rdataset_disassociate(rdataset);
 282		dns_message_puttemprdataset(client->message, rdatasetp);
 283	}
 284	CTRACE("query_putrdataset: done");
 285}
 286
 287static inline void
 288query_reset(ns_client_t *client, isc_boolean_t everything) {
 289	isc_buffer_t *dbuf, *dbuf_next;
 290	ns_dbversion_t *dbversion, *dbversion_next;
 291
 292	/*%
 293	 * Reset the query state of a client to its default state.
 294	 */
 295
 296	/*
 297	 * Cancel the fetch if it's running.
 298	 */
 299	ns_query_cancel(client);
 300
 301	/*
 302	 * Cleanup any active versions.
 303	 */
 304	for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
 305	     dbversion != NULL;
 306	     dbversion = dbversion_next) {
 307		dbversion_next = ISC_LIST_NEXT(dbversion, link);
 308		dns_db_closeversion(dbversion->db, &dbversion->version,
 309				    ISC_FALSE);
 310		dns_db_detach(&dbversion->db);
 311		ISC_LIST_INITANDAPPEND(client->query.freeversions,
 312				      dbversion, link);
 313	}
 314	ISC_LIST_INIT(client->query.activeversions);
 315
 316	if (client->query.authdb != NULL)
 317		dns_db_detach(&client->query.authdb);
 318	if (client->query.authzone != NULL)
 319		dns_zone_detach(&client->query.authzone);
 320
 321	if (client->query.dns64_aaaa != NULL)
 322		query_putrdataset(client, &client->query.dns64_aaaa);
 323	if (client->query.dns64_sigaaaa != NULL)
 324		query_putrdataset(client, &client->query.dns64_sigaaaa);
 325	if (client->query.dns64_aaaaok != NULL) {
 326		isc_mem_put(client->mctx, client->query.dns64_aaaaok,
 327			    client->query.dns64_aaaaoklen *
 328			    sizeof(isc_boolean_t));
 329		client->query.dns64_aaaaok =  NULL;
 330		client->query.dns64_aaaaoklen =  0;
 331	}
 332
 333	query_freefreeversions(client, everything);
 334
 335	for (dbuf = ISC_LIST_HEAD(client->query.namebufs);
 336	     dbuf != NULL;
 337	     dbuf = dbuf_next) {
 338		dbuf_next = ISC_LIST_NEXT(dbuf, link);
 339		if (dbuf_next != NULL || everything) {
 340			ISC_LIST_UNLINK(client->query.namebufs, dbuf, link);
 341			isc_buffer_free(&dbuf);
 342		}
 343	}
 344
 345	if (client->query.restarts > 0) {
 346		/*
 347		 * client->query.qname was dynamically allocated.
 348		 */
 349		dns_message_puttempname(client->message,
 350					&client->query.qname);
 351	}
 352	client->query.qname = NULL;
 353	client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
 354				    NS_QUERYATTR_CACHEOK |
 355				    NS_QUERYATTR_SECURE);
 356	client->query.restarts = 0;
 357	client->query.timerset = ISC_FALSE;
 358	if (client->query.rpz_st != NULL) {
 359		rpz_st_clear(client);
 360		if (everything) {
 361			isc_mem_put(client->mctx, client->query.rpz_st,
 362				    sizeof(*client->query.rpz_st));
 363			client->query.rpz_st = NULL;
 364		}
 365	}
 366	client->query.origqname = NULL;
 367	client->query.dboptions = 0;
 368	client->query.fetchoptions = 0;
 369	client->query.gluedb = NULL;
 370	client->query.authdbset = ISC_FALSE;
 371	client->query.isreferral = ISC_FALSE;
 372	client->query.dns64_options = 0;
 373	client->query.dns64_ttl = ISC_UINT32_MAX;
 374}
 375
 376static void
 377query_next_callback(ns_client_t *client) {
 378	query_reset(client, ISC_FALSE);
 379}
 380
 381void
 382ns_query_free(ns_client_t *client) {
 383	query_reset(client, ISC_TRUE);
 384}
 385
 386static inline isc_result_t
 387query_newnamebuf(ns_client_t *client) {
 388	isc_buffer_t *dbuf;
 389	isc_result_t result;
 390
 391	CTRACE("query_newnamebuf");
 392	/*%
 393	 * Allocate a name buffer.
 394	 */
 395
 396	dbuf = NULL;
 397	result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
 398	if (result != ISC_R_SUCCESS) {
 399		CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
 400		return (result);
 401	}
 402	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);
 403
 404	CTRACE("query_newnamebuf: done");
 405	return (ISC_R_SUCCESS);
 406}
 407
 408static inline isc_buffer_t *
 409query_getnamebuf(ns_client_t *client) {
 410	isc_buffer_t *dbuf;
 411	isc_result_t result;
 412	isc_region_t r;
 413
 414	CTRACE("query_getnamebuf");
 415	/*%
 416	 * Return a name buffer with space for a maximal name, allocating
 417	 * a new one if necessary.
 418	 */
 419
 420	if (ISC_LIST_EMPTY(client->query.namebufs)) {
 421		result = query_newnamebuf(client);
 422		if (result != ISC_R_SUCCESS) {
 423		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
 424			return (NULL);
 425		}
 426	}
 427
 428	dbuf = ISC_LIST_TAIL(client->query.namebufs);
 429	INSIST(dbuf != NULL);
 430	isc_buffer_availableregion(dbuf, &r);
 431	if (r.length < 255) {
 432		result = query_newnamebuf(client);
 433		if (result != ISC_R_SUCCESS) {
 434		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
 435			return (NULL);
 436
 437		}
 438		dbuf = ISC_LIST_TAIL(client->query.namebufs);
 439		isc_buffer_availableregion(dbuf, &r);
 440		INSIST(r.length >= 255);
 441	}
 442	CTRACE("query_getnamebuf: done");
 443	return (dbuf);
 444}
 445
 446static inline void
 447query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
 448	isc_region_t r;
 449
 450	CTRACE("query_keepname");
 451	/*%
 452	 * 'name' is using space in 'dbuf', but 'dbuf' has not yet been
 453	 * adjusted to take account of that.  We do the adjustment.
 454	 */
 455
 456	REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0);
 457
 458	dns_name_toregion(name, &r);
 459	isc_buffer_add(dbuf, r.length);
 460	dns_name_setbuffer(name, NULL);
 461	client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
 462}
 463
 464static inline void
 465query_releasename(ns_client_t *client, dns_name_t **namep) {
 466	dns_name_t *name = *namep;
 467
 468	/*%
 469	 * 'name' is no longer needed.  Return it to our pool of temporary
 470	 * names.  If it is using a name buffer, relinquish its exclusive
 471	 * rights on the buffer.
 472	 */
 473
 474	CTRACE("query_releasename");
 475	if (dns_name_hasbuffer(name)) {
 476		INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED)
 477		       != 0);
 478		client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
 479	}
 480	dns_message_puttempname(client->message, namep);
 481	CTRACE("query_releasename: done");
 482}
 483
 484static inline dns_name_t *
 485query_newname(ns_client_t *client, isc_buffer_t *dbuf,
 486	      isc_buffer_t *nbuf)
 487{
 488	dns_name_t *name;
 489	isc_region_t r;
 490	isc_result_t result;
 491
 492	REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0);
 493
 494	CTRACE("query_newname");
 495	name = NULL;
 496	result = dns_message_gettempname(client->message, &name);
 497	if (result != ISC_R_SUCCESS) {
 498		CTRACE("query_newname: dns_message_gettempname failed: done");
 499		return (NULL);
 500	}
 501	isc_buffer_availableregion(dbuf, &r);
 502	isc_buffer_init(nbuf, r.base, r.length);
 503	dns_name_init(name, NULL);
 504	dns_name_setbuffer(name, nbuf);
 505	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;
 506
 507	CTRACE("query_newname: done");
 508	return (name);
 509}
 510
 511static inline dns_rdataset_t *
 512query_newrdataset(ns_client_t *client) {
 513	dns_rdataset_t *rdataset;
 514	isc_result_t result;
 515
 516	CTRACE("query_newrdataset");
 517	rdataset = NULL;
 518	result = dns_message_gettemprdataset(client->message, &rdataset);
 519	if (result != ISC_R_SUCCESS) {
 520	  CTRACE("query_newrdataset: "
 521		 "dns_message_gettemprdataset failed: done");
 522		return (NULL);
 523	}
 524	dns_rdataset_init(rdataset);
 525
 526	CTRACE("query_newrdataset: done");
 527	return (rdataset);
 528}
 529
 530static inline isc_result_t
 531query_newdbversion(ns_client_t *client, unsigned int n) {
 532	unsigned int i;
 533	ns_dbversion_t *dbversion;
 534
 535	for (i = 0; i < n; i++) {
 536		dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
 537		if (dbversion != NULL) {
 538			dbversion->db = NULL;
 539			dbversion->version = NULL;
 540			ISC_LIST_INITANDAPPEND(client->query.freeversions,
 541					      dbversion, link);
 542		} else {
 543			/*
 544			 * We only return ISC_R_NOMEMORY if we couldn't
 545			 * allocate anything.
 546			 */
 547			if (i == 0)
 548				return (ISC_R_NOMEMORY);
 549			else
 550				return (ISC_R_SUCCESS);
 551		}
 552	}
 553
 554	return (ISC_R_SUCCESS);
 555}
 556
 557static inline ns_dbversion_t *
 558query_getdbversion(ns_client_t *client) {
 559	isc_result_t result;
 560	ns_dbversion_t *dbversion;
 561
 562	if (ISC_LIST_EMPTY(client->query.freeversions)) {
 563		result = query_newdbversion(client, 1);
 564		if (result != ISC_R_SUCCESS)
 565			return (NULL);
 566	}
 567	dbversion = ISC_LIST_HEAD(client->query.freeversions);
 568	INSIST(dbversion != NULL);
 569	ISC_LIST_UNLINK(client->query.freeversions, dbversion, link);
 570
 571	return (dbversion);
 572}
 573
 574isc_result_t
 575ns_query_init(ns_client_t *client) {
 576	isc_result_t result;
 577
 578	ISC_LIST_INIT(client->query.namebufs);
 579	ISC_LIST_INIT(client->query.activeversions);
 580	ISC_LIST_INIT(client->query.freeversions);
 581	client->query.restarts = 0;
 582	client->query.timerset = ISC_FALSE;
 583	client->query.rpz_st = NULL;
 584	client->query.qname = NULL;
 585	result = isc_mutex_init(&client->query.fetchlock);
 586	if (result != ISC_R_SUCCESS)
 587		return (result);
 588	client->query.fetch = NULL;
 589	client->query.authdb = NULL;
 590	client->query.authzone = NULL;
 591	client->query.authdbset = ISC_FALSE;
 592	client->query.isreferral = ISC_FALSE;
 593	client->query.dns64_aaaa = NULL;
 594	client->query.dns64_sigaaaa = NULL;
 595	client->query.dns64_aaaaok = NULL;
 596	client->query.dns64_aaaaoklen = 0;
 597	query_reset(client, ISC_FALSE);
 598	result = query_newdbversion(client, 3);
 599	if (result != ISC_R_SUCCESS) {
 600		DESTROYLOCK(&client->query.fetchlock);
 601		return (result);
 602	}
 603	result = query_newnamebuf(client);
 604	if (result != ISC_R_SUCCESS)
 605		query_freefreeversions(client, ISC_TRUE);
 606
 607	return (result);
 608}
 609
 610static inline ns_dbversion_t *
 611query_findversion(ns_client_t *client, dns_db_t *db)
 612{
 613	ns_dbversion_t *dbversion;
 614
 615	/*%
 616	 * We may already have done a query related to this
 617	 * database.  If so, we must be sure to make subsequent
 618	 * queries from the same version.
 619	 */
 620	for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
 621	     dbversion != NULL;
 622	     dbversion = ISC_LIST_NEXT(dbversion, link)) {
 623		if (dbversion->db == db)
 624			break;
 625	}
 626
 627	if (dbversion == NULL) {
 628		/*
 629		 * This is a new zone for this query.  Add it to
 630		 * the active list.
 631		 */
 632		dbversion = query_getdbversion(client);
 633		if (dbversion == NULL)
 634			return (NULL);
 635		dns_db_attach(db, &dbversion->db);
 636		dns_db_currentversion(db, &dbversion->version);
 637		dbversion->acl_checked = ISC_FALSE;
 638		dbversion->queryok = ISC_FALSE;
 639		ISC_LIST_APPEND(client->query.activeversions,
 640				dbversion, link);
 641	}
 642
 643	return (dbversion);
 644}
 645
 646static inline isc_result_t
 647query_validatezonedb(ns_client_t *client, dns_name_t *name,
 648		     dns_rdatatype_t qtype, unsigned int options,
 649		     dns_zone_t *zone, dns_db_t *db,
 650		     dns_dbversion_t **versionp)
 651{
 652	isc_result_t result;
 653	dns_acl_t *queryacl;
 654	ns_dbversion_t *dbversion;
 655
 656	REQUIRE(zone != NULL);
 657	REQUIRE(db != NULL);
 658
 659	/*
 660	 * This limits our searching to the zone where the first name
 661	 * (the query target) was looked for.  This prevents following
 662	 * CNAMES or DNAMES into other zones and prevents returning
 663	 * additional data from other zones.
 664	 */
 665	if (!client->view->additionalfromauth &&
 666	    client->query.authdbset &&
 667	    db != client->query.authdb)
 668		return (DNS_R_REFUSED);
 669
 670	/*
 671	 * Non recursive query to a static-stub zone is prohibited; its
 672	 * zone content is not public data, but a part of local configuration
 673	 * and should not be disclosed.
 674	 */
 675	if (dns_zone_gettype(zone) == dns_zone_staticstub &&
 676	    !RECURSIONOK(client)) {
 677		return (DNS_R_REFUSED);
 678	}
 679
 680	/*
 681	 * If the zone has an ACL, we'll check it, otherwise
 682	 * we use the view's "allow-query" ACL.  Each ACL is only checked
 683	 * once per query.
 684	 *
 685	 * Also, get the database version to use.
 686	 */
 687
 688	/*
 689	 * Get the current version of this database.
 690	 */
 691	dbversion = query_findversion(client, db);
 692	if (dbversion == NULL)
 693		return (DNS_R_SERVFAIL);
 694
 695	if ((options & DNS_GETDB_IGNOREACL) != 0)
 696		goto approved;
 697	if (dbversion->acl_checked) {
 698		if (!dbversion->queryok)
 699			return (DNS_R_REFUSED);
 700		goto approved;
 701	}
 702
 703	queryacl = dns_zone_getqueryacl(zone);
 704	if (queryacl == NULL) {
 705		queryacl = client->view->queryacl;
 706		if ((client->query.attributes &
 707		     NS_QUERYATTR_QUERYOKVALID) != 0) {
 708			/*
 709			 * We've evaluated the view's queryacl already.  If
 710			 * NS_QUERYATTR_QUERYOK is set, then the client is
 711			 * allowed to make queries, otherwise the query should
 712			 * be refused.
 713			 */
 714			dbversion->acl_checked = ISC_TRUE;
 715			if ((client->query.attributes &
 716			     NS_QUERYATTR_QUERYOK) == 0) {
 717				dbversion->queryok = ISC_FALSE;
 718				return (DNS_R_REFUSED);
 719			}
 720			dbversion->queryok = ISC_TRUE;
 721			goto approved;
 722		}
 723	}
 724
 725	result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
 726	if ((options & DNS_GETDB_NOLOG) == 0) {
 727		char msg[NS_CLIENT_ACLMSGSIZE("query")];
 728		if (result == ISC_R_SUCCESS) {
 729			if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) {
 730				ns_client_aclmsg("query", name, qtype,
 731						 client->view->rdclass,
 732						 msg, sizeof(msg));
 733				ns_client_log(client,
 734					      DNS_LOGCATEGORY_SECURITY,
 735					      NS_LOGMODULE_QUERY,
 736					      ISC_LOG_DEBUG(3),
 737					      "%s approved", msg);
 738			}
 739		} else {
 740			ns_client_aclmsg("query", name, qtype,
 741					 client->view->rdclass,
 742					 msg, sizeof(msg));
 743			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
 744				      NS_LOGMODULE_QUERY, ISC_LOG_INFO,
 745				      "%s denied", msg);
 746		}
 747	}
 748
 749	if (queryacl == client->view->queryacl) {
 750		if (result == ISC_R_SUCCESS) {
 751			/*
 752			 * We were allowed by the default
 753			 * "allow-query" ACL.  Remember this so we
 754			 * don't have to check again.
 755			 */
 756			client->query.attributes |= NS_QUERYATTR_QUERYOK;
 757		}
 758		/*
 759		 * We've now evaluated the view's query ACL, and
 760		 * the NS_QUERYATTR_QUERYOK attribute is now valid.
 761		 */
 762		client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
 763	}
 764
 765	dbversion->acl_checked = ISC_TRUE;
 766	if (result != ISC_R_SUCCESS) {
 767		dbversion->queryok = ISC_FALSE;
 768		return (DNS_R_REFUSED);
 769	}
 770	dbversion->queryok = ISC_TRUE;
 771
 772 approved:
 773	/* Transfer ownership, if necessary. */
 774	if (versionp != NULL)
 775		*versionp = dbversion->version;
 776	return (ISC_R_SUCCESS);
 777}
 778
 779static inline isc_result_t
 780query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
 781		unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
 782		dns_dbversion_t **versionp)
 783{
 784	isc_result_t result;
 785	unsigned int ztoptions;
 786	dns_zone_t *zone = NULL;
 787	dns_db_t *db = NULL;
 788	isc_boolean_t partial = ISC_FALSE;
 789
 790	REQUIRE(zonep != NULL && *zonep == NULL);
 791	REQUIRE(dbp != NULL && *dbp == NULL);
 792
 793	/*%
 794	 * Find a zone database to answer the query.
 795	 */
 796	ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
 797		DNS_ZTFIND_NOEXACT : 0;
 798
 799	result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
 800			     &zone);
 801	if (result == DNS_R_PARTIALMATCH)
 802		partial = ISC_TRUE;
 803	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
 804		result = dns_zone_getdb(zone, &db);
 805
 806	if (result != ISC_R_SUCCESS)
 807		goto fail;
 808
 809	result = query_validatezonedb(client, name, qtype, options, zone, db,
 810				      versionp);
 811
 812	if (result != ISC_R_SUCCESS)
 813		goto fail;
 814
 815	/* Transfer ownership. */
 816	*zonep = zone;
 817	*dbp = db;
 818
 819	if (partial && (options & DNS_GETDB_PARTIAL) != 0)
 820		return (DNS_R_PARTIALMATCH);
 821	return (ISC_R_SUCCESS);
 822
 823 fail:
 824	if (zone != NULL)
 825		dns_zone_detach(&zone);
 826	if (db != NULL)
 827		dns_db_detach(&db);
 828
 829	return (result);
 830}
 831
 832static void
 833rpz_log_rewrite(ns_client_t *client, const char *disabled,
 834		dns_rpz_policy_t policy, dns_rpz_type_t type,
 835		dns_name_t *rpz_qname) {
 836	char qname_buf[DNS_NAME_FORMATSIZE];
 837	char rpz_qname_buf[DNS_NAME_FORMATSIZE];
 838
 839	if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
 840		return;
 841
 842	dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
 843	dns_name_format(rpz_qname, rpz_qname_buf, sizeof(rpz_qname_buf));
 844
 845	ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
 846		      DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s",
 847		      disabled,
 848		      dns_rpz_type2str(type), dns_rpz_policy2str(policy),
 849		      qname_buf, rpz_qname_buf);
 850}
 851
 852static void
 853rpz_log_fail(ns_client_t *client, int level,
 854	     dns_rpz_type_t rpz_type, dns_name_t *name,
 855	     const char *str, isc_result_t result)
 856{
 857	char namebuf1[DNS_NAME_FORMATSIZE];
 858	char namebuf2[DNS_NAME_FORMATSIZE];
 859
 860	if (!isc_log_wouldlog(ns_g_lctx, level))
 861		return;
 862
 863	dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
 864	dns_name_format(name, namebuf2, sizeof(namebuf2));
 865	ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
 866		      NS_LOGMODULE_QUERY, level,
 867		      "rpz %s rewrite %s via %s %sfailed: %s",
 868		      dns_rpz_type2str(rpz_type),
 869		      namebuf1, namebuf2, str, isc_result_totext(result));
 870}
 871
 872/*
 873 * Get a policy rewrite zone database.
 874 */
 875static isc_result_t
 876rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *rpz_qname,
 877	  dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp)
 878{
 879	char namebuf1[DNS_NAME_FORMATSIZE];
 880	char namebuf2[DNS_NAME_FORMATSIZE];
 881	dns_dbversion_t *rpz_version = NULL;
 882	isc_result_t result;
 883
 884	result = query_getzonedb(client, rpz_qname, dns_rdatatype_any,
 885				 DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
 886	if (result == ISC_R_SUCCESS) {
 887		if (isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) {
 888			dns_name_format(client->query.qname, namebuf1,
 889					sizeof(namebuf1));
 890			dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2));
 891			ns_client_log(client, DNS_LOGCATEGORY_RPZ,
 892				      NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2,
 893				      "try rpz %s rewrite %s via %s",
 894				      dns_rpz_type2str(rpz_type),
 895				      namebuf1, namebuf2);
 896		}
 897		*versionp = rpz_version;
 898		return (ISC_R_SUCCESS);
 899	}
 900	rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname,
 901		     "query_getzonedb() ", result);
 902	return (result);
 903}
 904
 905static inline isc_result_t
 906query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
 907		 dns_db_t **dbp, unsigned int options)
 908{
 909	isc_result_t result;
 910	isc_boolean_t check_acl;
 911	dns_db_t *db = NULL;
 912
 913	REQUIRE(dbp != NULL && *dbp == NULL);
 914
 915	/*%
 916	 * Find a cache database to answer the query.
 917	 * This may fail with DNS_R_REFUSED if the client
 918	 * is not allowed to use the cache.
 919	 */
 920
 921	if (!USECACHE(client))
 922		return (DNS_R_REFUSED);
 923	dns_db_attach(client->view->cachedb, &db);
 924
 925	if ((client->query.attributes & NS_QUERYATTR_CACHEACLOKVALID) != 0) {
 926		/*
 927		 * We've evaluated the view's cacheacl already.  If
 928		 * NS_QUERYATTR_CACHEACLOK is set, then the client is
 929		 * allowed to make queries, otherwise the query should
 930		 * be refused.
 931		 */
 932		check_acl = ISC_FALSE;
 933		if ((client->query.attributes & NS_QUERYATTR_CACHEACLOK) == 0)
 934			goto refuse;
 935	} else {
 936		/*
 937		 * We haven't evaluated the view's queryacl yet.
 938		 */
 939		check_acl = ISC_TRUE;
 940	}
 941
 942	if (check_acl) {
 943		isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
 944		char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")];
 945
 946		result = ns_client_checkaclsilent(client, NULL,
 947						  client->view->cacheacl,
 948						  ISC_TRUE);
 949		if (result == ISC_R_SUCCESS) {
 950			/*
 951			 * We were allowed by the "allow-query-cache" ACL.
 952			 * Remember this so we don't have to check again.
 953			 */
 954			client->query.attributes |=
 955				NS_QUERYATTR_CACHEACLOK;
 956			if (log && isc_log_wouldlog(ns_g_lctx,
 957						     ISC_LOG_DEBUG(3)))
 958			{
 959				ns_client_aclmsg("query (cache)", name, qtype,
 960						 client->view->rdclass,
 961						 msg, sizeof(msg));
 962				ns_client_log(client,
 963					      DNS_LOGCATEGORY_SECURITY,
 964					      NS_LOGMODULE_QUERY,
 965					      ISC_LOG_DEBUG(3),
 966					      "%s approved", msg);
 967			}
 968		} else if (log) {
 969			ns_client_aclmsg("query (cache)", name, qtype,
 970					 client->view->rdclass, msg,
 971					 sizeof(msg));
 972			ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
 973				      NS_LOGMODULE_QUERY, ISC_LOG_INFO,
 974				      "%s denied", msg);
 975		}
 976		/*
 977		 * We've now evaluated the view's query ACL, and
 978		 * the NS_QUERYATTR_CACHEACLOKVALID attribute is now valid.
 979		 */
 980		client->query.attributes |= NS_QUERYATTR_CACHEACLOKVALID;
 981
 982		if (result != ISC_R_SUCCESS)
 983			goto refuse;
 984	}
 985
 986	/* Approved. */
 987
 988	/* Transfer ownership. */
 989	*dbp = db;
 990
 991	return (ISC_R_SUCCESS);
 992
 993 refuse:
 994	result = DNS_R_REFUSED;
 995
 996	if (db != NULL)
 997		dns_db_detach(&db);
 998
 999	return (result);
1000}
1001
1002
1003static inline isc_result_t
1004query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
1005	    unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
1006	    dns_dbversion_t **versionp, isc_boolean_t *is_zonep)
1007{
1008	isc_result_t result;
1009
1010	isc_result_t tresult;
1011	unsigned int namelabels;
1012	unsigned int zonelabels;
1013	dns_zone_t *zone = NULL;
1014	dns_db_t *tdbp;
1015
1016	REQUIRE(zonep != NULL && *zonep == NULL);
1017
1018	tdbp = NULL;
1019
1020	/* Calculate how many labels are in name. */
1021	namelabels = dns_name_countlabels(name);
1022	zonelabels = 0;
1023
1024	/* Try to find name in bind's standard database. */
1025	result = query_getzonedb(client, name, qtype, options, &zone,
1026				 dbp, versionp);
1027
1028	/* See how many labels are in the zone's name.	  */
1029	if (result == ISC_R_SUCCESS && zone != NULL)
1030		zonelabels = dns_name_countlabels(dns_zone_getorigin(zone));
1031	/*
1032	 * If # zone labels < # name labels, try to find an even better match
1033	 * Only try if a DLZ driver is loaded for this view
1034	 */
1035	if (zonelabels < namelabels && client->view->dlzdatabase != NULL) {
1036		tresult = dns_dlzfindzone(client->view, name,
1037					  zonelabels, &tdbp);
1038		 /* If we successful, we found a better match. */
1039		if (tresult == ISC_R_SUCCESS) {
1040			/*
1041			 * If the previous search returned a zone, detach it.
1042			 */
1043			if (zone != NULL)
1044				dns_zone_detach(&zone);
1045
1046			/*
1047			 * If the previous search returned a database,
1048			 * detach it.
1049			 */
1050			if (*dbp != NULL)
1051				dns_db_detach(dbp);
1052
1053			/*
1054			 * If the previous search returned a version, clear it.
1055			 */
1056			*versionp = NULL;
1057
1058			/*
1059			 * Get our database version.
1060			 */
1061			dns_db_currentversion(tdbp, versionp);
1062
1063			/*
1064			 * Be sure to return our database.
1065			 */
1066			*dbp = tdbp;
1067
1068			/*
1069			 * We return a null zone, No stats for DLZ zones.
1070			 */
1071			zone = NULL;
1072			result = tresult;
1073		}
1074	}
1075
1076	/* If successful, Transfer ownership of zone. */
1077	if (result == ISC_R_SUCCESS) {
1078		*zonep = zone;
1079		/*
1080		 * If neither attempt above succeeded, return the cache instead
1081		 */
1082		*is_zonep = ISC_TRUE;
1083	} else if (result == ISC_R_NOTFOUND) {
1084		result = query_getcachedb(client, name, qtype, dbp, options);
1085		*is_zonep = ISC_FALSE;
1086	}
1087	return (result);
1088}
1089
1090static inline isc_boolean_t
1091query_isduplicate(ns_client_t *client, dns_name_t *name,
1092		  dns_rdatatype_t type, dns_name_t **mnamep)
1093{
1094	dns_section_t section;
1095	dns_name_t *mname = NULL;
1096	isc_result_t result;
1097
1098	CTRACE("query_isduplicate");
1099
1100	for (section = DNS_SECTION_ANSWER;
1101	     section <= DNS_SECTION_ADDITIONAL;
1102	     section++) {
1103		result = dns_message_findname(client->message, section,
1104					      name, type, 0, &mname, NULL);
1105		if (result == ISC_R_SUCCESS) {
1106			/*
1107			 * We've already got this RRset in the response.
1108			 */
1109			CTRACE("query_isduplicate: true: done");
1110			return (ISC_TRUE);
1111		} else if (result == DNS_R_NXRRSET) {
1112			/*
1113			 * The name exists, but the rdataset does not.
1114			 */
1115			if (section == DNS_SECTION_ADDITIONAL)
1116				break;
1117		} else
1118			RUNTIME_CHECK(result == DNS_R_NXDOMAIN);
1119		mname = NULL;
1120	}
1121
1122	if (mnamep != NULL)
1123		*mnamep = mname;
1124
1125	CTRACE("query_isduplicate: false: done");
1126	return (ISC_FALSE);
1127}
1128
1129static isc_result_t
1130query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
1131	ns_client_t *client = arg;
1132	isc_result_t result, eresult;
1133	dns_dbnode_t *node;
1134	dns_db_t *db;
1135	dns_name_t *fname, *mname;
1136	dns_rdataset_t *rdataset, *sigrdataset, *trdataset;
1137	isc_buffer_t *dbuf;
1138	isc_buffer_t b;
1139	dns_dbversion_t *version;
1140	isc_boolean_t added_something, need_addname;
1141	dns_zone_t *zone;
1142	dns_rdatatype_t type;
1143
1144	REQUIRE(NS_CLIENT_VALID(client));
1145	REQUIRE(qtype != dns_rdatatype_any);
1146
1147	if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype))
1148		return (ISC_R_SUCCESS);
1149
1150	CTRACE("query_addadditional");
1151
1152	/*
1153	 * Initialization.
1154	 */
1155	eresult = ISC_R_SUCCESS;
1156	fname = NULL;
1157	rdataset = NULL;
1158	sigrdataset = NULL;
1159	trdataset = NULL;
1160	db = NULL;
1161	version = NULL;
1162	node = NULL;
1163	added_something = ISC_FALSE;
1164	need_addname = ISC_FALSE;
1165	zone = NULL;
1166
1167	/*
1168	 * We treat type A additional section processing as if it
1169	 * were "any address type" additional section processing.
1170	 * To avoid multiple lookups, we do an 'any' database
1171	 * lookup and iterate over the node.
1172	 */
1173	if (qtype == dns_rdatatype_a)
1174		type = dns_rdatatype_any;
1175	else
1176		type = qtype;
1177
1178	/*
1179	 * Get some resources.
1180	 */
1181	dbuf = query_getnamebuf(client);
1182	if (dbuf == NULL)
1183		goto cleanup;
1184	fname = query_newname(client, dbuf, &b);
1185	rdataset = query_newrdataset(client);
1186	if (fname == NULL || rdataset == NULL)
1187		goto cleanup;
1188	if (WANTDNSSEC(client)) {
1189		sigrdataset = query_newrdataset(client);
1190		if (sigrdataset == NULL)
1191			goto cleanup;
1192	}
1193
1194	/*
1195	 * Look for a zone database that might contain authoritative
1196	 * additional data.
1197	 */
1198	result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
1199				 &zone, &db, &version);
1200	if (result != ISC_R_SUCCESS)
1201		goto try_cache;
1202
1203	CTRACE("query_addadditional: db_find");
1204
1205	/*
1206	 * Since we are looking for authoritative data, we do not set
1207	 * the GLUEOK flag.  Glue will be looked for later, but not
1208	 * necessarily in the same database.
1209	 */
1210	node = NULL;
1211	result = dns_db_find(db, name, version, type, client->query.dboptions,
1212			     client->now, &node, fname, rdataset,
1213			     sigrdataset);
1214	if (result == ISC_R_SUCCESS) {
1215		if (sigrdataset != NULL && !dns_db_issecure(db) &&
1216		    dns_rdataset_isassociated(sigrdataset))
1217			dns_rdataset_disassociate(sigrdataset);
1218		goto found;
1219	}
1220
1221	if (dns_rdataset_isassociated(rdataset))
1222		dns_rdataset_disassociate(rdataset);
1223	if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
1224		dns_rdataset_disassociate(sigrdataset);
1225	if (node != NULL)
1226		dns_db_detachnode(db, &node);
1227	version = NULL;
1228	dns_db_detach(&db);
1229
1230	/*
1231	 * No authoritative data was found.  The cache is our next best bet.
1232	 */
1233
1234 try_cache:
1235	result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
1236	if (result != ISC_R_SUCCESS)
1237		/*
1238		 * Most likely the client isn't allowed to query the cache.
1239		 */
1240		goto try_glue;
1241	/*
1242	 * Attempt to validate glue.
1243	 */
1244	if (sigrdataset == NULL) {
1245		sigrdataset = query_newrdataset(client);
1246		if (sigrdataset == NULL)
1247			goto cleanup;
1248	}
1249	result = dns_db_find(db, name, version, type,
1250			     client->query.dboptions |
1251			     DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK,
1252			     client->now, &node, fname, rdataset,
1253			     sigrdataset);
1254	if (result == DNS_R_GLUE &&
1255	    validate(client, db, fname, rdataset, sigrdataset))
1256		result = ISC_R_SUCCESS;
1257	if (!WANTDNSSEC(client))
1258		query_putrdataset(client, &sigrdataset);
1259	if (result == ISC_R_SUCCESS)
1260		goto found;
1261
1262	if (dns_rdataset_isassociated(rdataset))
1263		dns_rdataset_disassociate(rdataset);
1264	if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
1265		dns_rdataset_disassociate(sigrdataset);
1266	if (node != NULL)
1267		dns_db_detachnode(db, &node);
1268	dns_db_detach(&db);
1269
1270 try_glue:
1271	/*
1272	 * No cached data was found.  Glue is our last chance.
1273	 * RFC1035 sayeth:
1274	 *
1275	 *	NS records cause both the usual additional section
1276	 *	processing to locate a type A record, and, when used
1277	 *	in a referral, a special search of the zone in which
1278	 *	they reside for glue information.
1279	 *
1280	 * This is the "special search".  Note that we must search
1281	 * the zone where the NS record resides, not the zone it
1282	 * points to, and that we only do the search in the delegation
1283	 * case (identified by client->query.gluedb being set).
1284	 */
1285
1286	if (client->query.gluedb == NULL)
1287		goto cleanup;
1288
1289	/*
1290	 * Don't poison caches using the bailiwick protection model.
1291	 */
1292	if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
1293		goto cleanup;
1294
1295	dns_db_attach(client->query.gluedb, &db);
1296	result = dns_db_find(db, name, version, type,
1297			     client->query.dboptions | DNS_DBFIND_GLUEOK,
1298			     client->now, &node, fname, rdataset,
1299			     sigrdataset);
1300	if (!(result == ISC_R_SUCCESS ||
1301	      result == DNS_R_ZONECUT ||
1302	      result == DNS_R_GLUE))
1303		goto cleanup;
1304
1305 found:
1306	/*
1307	 * We have found a potential additional data rdataset, or
1308	 * at least a node to iterate over.
1309	 */
1310	query_keepname(client, fname, dbuf);
1311
1312	/*
1313	 * If we have an rdataset, add it to the additional data
1314	 * section.
1315	 */
1316	mname = NULL;
1317	if (dns_rdataset_isassociated(rdataset) &&
1318	    !query_isduplicate(client, fname, type, &mname)) {
1319		if (mname != NULL) {
1320			INSIST(mname != fname);
1321			query_releasename(client, &fname);
1322			fname = mname;
1323		} else
1324			need_addname = ISC_TRUE;
1325		ISC_LIST_APPEND(fname->list, rdataset, link);
1326		trdataset = rdataset;
1327		rdataset = NULL;
1328		added_something = ISC_TRUE;
1329		/*
1330		 * Note: we only add SIGs if we've added the type they cover,
1331		 * so we do not need to check if the SIG rdataset is already
1332		 * in the response.
1333		 */
1334		if (sigrdataset != NULL &&
1335		    dns_rdataset_isassociated(sigrdataset))
1336		{
1337			ISC_LIST_APPEND(fname->list, sigrdataset, link);
1338			sigrdataset = NULL;
1339		}
1340	}
1341
1342	if (qtype == dns_rdatatype_a) {
1343#ifdef ALLOW_FILTER_AAAA_ON_V4
1344		isc_boolean_t have_a = ISC_FALSE;
1345#endif
1346
1347		/*
1348		 * We now go looking for A and AAAA records, along with
1349		 * their signatures.
1350		 *
1351		 * XXXRTH  This code could be more efficient.
1352		 */
1353		if (rdataset != NULL) {
1354			if (dns_rdataset_isassociated(rdataset))
1355				dns_rdataset_disassociate(rdataset);
1356		} else {
1357			rdataset = query_newrdataset(client);
1358			if (rdataset == NULL)
1359				goto addname;
1360		}
1361		if (sigrdataset != NULL) {
1362			if (dns_rdataset_isassociated(sigrdataset))
1363				dns_rdataset_disassociate(sigrdataset);
1364		} else if (WANTDNSSEC(client)) {
1365			sigrdataset = query_newrdataset(client);
1366			if (sigrdataset == NULL)
1367				goto addname;
1368		}
1369		if (query_isduplicate(client, fname, dns_rdatatype_a, NULL))
1370			goto aaaa_lookup;
1371		result = dns_db_findrdataset(db, node, version,
1372					     dns_rdatatype_a, 0,
1373					     client->now, rdataset,
1374					     sigrdataset);
1375		if (result == DNS_R_NCACHENXDOMAIN)
1376			goto addname;
1377		if (result == DNS_R_NCACHENXRRSET) {
1378			dns_rdataset_disassociate(rdataset);
1379			if (sigrdataset != NULL &&
1380			    dns_rdataset_isassociated(sigrdataset))
1381				dns_rdataset_disassociate(sigrdataset);
1382		}
1383		if (result == ISC_R_SUCCESS) {
1384			mname = NULL;
1385#ifdef ALLOW_FILTER_AAAA_ON_V4
1386			have_a = ISC_TRUE;
1387#endif
1388			if (!query_isduplicate(client, fname,
1389					       dns_rdatatype_a, &mname)) {
1390				if (mname != fname) {
1391					if (mname != NULL) {
1392						query_releasename(client, &fname);
1393						fname = mname;
1394					} else
1395						need_addname = ISC_TRUE;
1396				}
1397				ISC_LIST_APPEND(fname->list, rdataset, link);
1398				added_something = ISC_TRUE;
1399				if (sigrdataset != NULL &&
1400				    dns_rdataset_isassociated(sigrdataset))
1401				{
1402					ISC_LIST_APPEND(fname->list,
1403							sigrdataset, link);
1404					sigrdataset =
1405						query_newrdataset(client);
1406				}
1407				rdataset = query_newrdataset(client);
1408				if (rdataset == NULL)
1409					goto addname;
1410				if (WANTDNSSEC(client) && sigrdataset == NULL)
1411					goto addname;
1412			} else {
1413				dns_rdataset_disassociate(rdataset);
1414				if (sigrdataset != NULL &&
1415				    dns_rdataset_isassociated(sigrdataset))
1416					dns_rdataset_disassociate(sigrdataset);
1417			}
1418		}
1419  aaaa_lookup:
1420		if (query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL))
1421			goto addname;
1422		result = dns_db_findrdataset(db, node, version,
1423					     dns_rdatatype_aaaa, 0,
1424					     client->now, rdataset,
1425					     sigrdataset);
1426		if (result == DNS_R_NCACHENXDOMAIN)
1427			goto addname;
1428		if (result == DNS_R_NCACHENXRRSET) {
1429			dns_rdataset_disassociate(rdataset);
1430			if (sigrdataset != NULL &&
1431			    dns_rdataset_isassociated(sigrdataset))
1432				dns_rdataset_disassociate(sigrdataset);
1433		}
1434		if (result == ISC_R_SUCCESS) {
1435			mname = NULL;
1436			/*
1437			 * There's an A; check whether we're filtering AAAA
1438			 */
1439#ifdef ALLOW_FILTER_AAAA_ON_V4
1440			if (have_a &&
1441			    (client->filter_aaaa == dns_v4_aaaa_break_dnssec ||
1442			    (client->filter_aaaa == dns_v4_aaaa_filter &&
1443			     (!WANTDNSSEC(client) || sigrdataset == NULL ||
1444			      !dns_rdataset_isassociated(sigrdataset)))))
1445				goto addname;
1446#endif
1447			if (!query_isduplicate(client, fname,
1448					       dns_rdatatype_aaaa, &mname)) {
1449				if (mname != fname) {
1450					if (mname != NULL) {
1451						query_releasename(client, &fname);
1452						fname = mname;
1453					} else
1454						need_addname = ISC_TRUE;
1455				}
1456				ISC_LIST_APPEND(fname->list, rdataset, link);
1457				added_something = ISC_TRUE;
1458				if (sigrdataset != NULL &&
1459				    dns_rdataset_isassociated(sigrdataset))
1460				{
1461					ISC_LIST_APPEND(fname->list,
1462							sigrdataset, link);
1463					sigrdataset = NULL;
1464				}
1465				rdataset = NULL;
1466			}
1467		}
1468	}
1469
1470 addname:
1471	CTRACE("query_addadditional: addname");
1472	/*
1473	 * If we haven't added anything, then we're done.
1474	 */
1475	if (!added_something)
1476		goto cleanup;
1477
1478	/*
1479	 * We may have added our rdatasets to an existing name, if so, then
1480	 * need_addname will be ISC_FALSE.  Whether we used an existing name
1481	 * or a new one, we must set fname to NULL to prevent cleanup.
1482	 */
1483	if (need_addname)
1484		dns_message_addname(client->message, fname,
1485				    DNS_SECTION_ADDITIONAL);
1486	fname = NULL;
1487
1488	/*
1489	 * In a few cases, we want to add additional data for additional
1490	 * data.  It's simpler to just deal with special cases here than
1491	 * to try to create a general purpose mechanism and allow the
1492	 * rdata implementations to do it themselves.
1493	 *
1494	 * This involves recursion, but the depth is limited.  The
1495	 * most complex case is adding a SRV rdataset, which involves
1496	 * recursing to add address records, which in turn can cause
1497	 * recursion to add KEYs.
1498	 */
1499	if (type == dns_rdatatype_srv && trdataset != NULL) {
1500		/*
1501		 * If we're adding SRV records to the additional data
1502		 * section, it's helpful if we add the SRV additional data
1503		 * as well.
1504		 */
1505		eresult = dns_rdataset_additionaldata(trdataset,
1506						      query_addadditional,
1507						      client);
1508	}
1509
1510 cleanup:
1511	CTRACE("query_addadditional: cleanup");
1512	query_putrdataset(client, &rdataset);
1513	if (sigrdataset != NULL)
1514		query_putrdataset(client, &sigrdataset);
1515	if (fname != NULL)
1516		query_releasename(client, &fname);
1517	if (node != NULL)
1518		dns_db_detachnode(db, &node);
1519	if (db != NULL)
1520		dns_db_detach(&db);
1521	if (zone != NULL)
1522		dns_zone_detach(&zone);
1523
1524	CTRACE("query_addadditional: done");
1525	return (eresult);
1526}
1527
1528static inline void
1529query_discardcache(ns_client_t *client, dns_rdataset_t *rdataset_base,
1530		   dns_rdatasetadditional_t additionaltype,
1531		   dns_rdatatype_t type, dns_zone_t **zonep, dns_db_t **dbp,
1532		   dns_dbversion_t **versionp, dns_dbnode_t **nodep,
1533		   dns_name_t *fname)
1534{
1535	dns_rdataset_t *rdataset;
1536
1537	while  ((rdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
1538		ISC_LIST_UNLINK(fname->list, rdataset, link);
1539		query_putrdataset(client, &rdataset);
1540	}
1541	if (*versionp != NULL)
1542		dns_db_closeversion(*dbp, versionp, ISC_FALSE);
1543	if (*nodep != NULL)
1544		dns_db_detachnode(*dbp, nodep);
1545	if (*dbp != NULL)
1546		dns_db_detach(dbp);
1547	if (*zonep != NULL)
1548		dns_zone_detach(zonep);
1549	(void)dns_rdataset_putadditional(client->view->acache, rdataset_base,
1550					 additionaltype, type);
1551}
1552
1553static inline isc_result_t
1554query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0,
1555		   dns_dbversion_t *version)
1556{
1557	isc_result_t result = ISC_R_SUCCESS;
1558	dns_dbversion_t *version_current = NULL;
1559	dns_db_t *db_current = db0;
1560
1561	if (db_current == NULL) {
1562		result = dns_zone_getdb(zone, &db_current);
1563		if (result != ISC_R_SUCCESS)
1564			return (result);
1565	}
1566	dns_db_currentversion(db_current, &version_current);
1567	if (db_current != db || version_current != version) {
1568		result = ISC_R_FAILURE;
1569		goto cleanup;
1570	}
1571
1572 cleanup:
1573	dns_db_closeversion(db_current, &version_current, ISC_FALSE);
1574	if (db0 == NULL && db_current != NULL)
1575		dns_db_detach(&db_current);
1576
1577	return (result);
1578}
1579
1580static isc_result_t
1581query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
1582	client_additionalctx_t *additionalctx = arg;
1583	dns_rdataset_t *rdataset_base;
1584	ns_client_t *client;
1585	isc_result_t result, eresult;
1586	dns_dbnode_t *node, *cnode;
1587	dns_db_t *db, *cdb;
1588	dns_name_t *fname, *mname0, cfname;
1589	dns_rdataset_t *rdataset, *sigrdataset;
1590	dns_rdataset_t *crdataset, *crdataset_next;
1591	isc_buffer_t *dbuf;
1592	isc_buffer_t b;
1593	dns_dbversion_t *version, *cversion;
1594	isc_boolean_t added_something, need_addname, needadditionalcache;
1595	isc_boolean_t need_sigrrset;
1596	dns_zone_t *zone;
1597	dns_rdatatype_t type;
1598	dns_rdatasetadditional_t additionaltype;
1599
1600	/*
1601	 * If we don't have an additional cache call query_addadditional.
1602	 */
1603	client = additionalctx->client;
1604	REQUIRE(NS_CLIENT_VALID(client));
1605
1606	if (qtype != dns_rdatatype_a || client->view->acache == NULL) {
1607		/*
1608		 * This function is optimized for "address" types.  For other
1609		 * types, use a generic routine.
1610		 * XXX: ideally, this function should be generic enough.
1611		 */
1612		return (query_addadditional(additionalctx->client,
1613					    name, qtype));
1614	}
1615
1616	/*
1617	 * Initialization.
1618	 */
1619	rdataset_base = additionalctx->rdataset;
1620	eresult = ISC_R_SUCCESS;
1621	fname = NULL;
1622	rdataset = NULL;
1623	sigrdataset = NULL;
1624	db = NULL;
1625	cdb = NULL;
1626	version = NULL;
1627	cversion = NULL;
1628	node = NULL;
1629	cnode = NULL;
1630	added_something = ISC_FALSE;
1631	need_addname = ISC_FALSE;
1632	zone = NULL;
1633	needadditionalcache = ISC_FALSE;
1634	POST(needadditionalcache);
1635	additionaltype = dns_rdatasetadditional_fromauth;
1636	dns_name_init(&cfname, NULL);
1637
1638	CTRACE("query_addadditional2");
1639
1640	/*
1641	 * We treat type A additional section processing as if it
1642	 * were "any address type" additional section processing.
1643	 * To avoid multiple lookups, we do an 'any' database
1644	 * lookup and iterate over the node.
1645	 * XXXJT: this approach can cause a suboptimal result when the cache
1646	 * DB only has partial address types and the glue DB has remaining
1647	 * ones.
1648	 */
1649	type = dns_rdatatype_any;
1650
1651	/*
1652	 * Get some resources.
1653	 */
1654	dbuf = query_getnamebuf(client);
1655	if (dbuf == NULL)
1656		goto cleanup;
1657	fname = query_newname(client, dbuf, &b);
1658	if (fname == NULL)
1659		goto cleanup;
1660	dns_name_setbuffer(&cfname, &b); /* share the buffer */
1661
1662	/* Check additional cache */
1663	result = dns_rdataset_getadditional(rdataset_base, additionaltype,
1664					    type, client->view->acache, &zone,
1665					    &cdb, &cversion, &cnode, &cfname,
1666					    client->message, client->now);
1667	if (result != ISC_R_SUCCESS)
1668		goto findauthdb;
1669	if (zone == NULL) {
1670		CTRACE("query_addadditional2: auth zone not found");
1671		goto try_cache;
1672	}
1673
1674	/* Is the cached DB up-to-date? */
1675	result = query_iscachevalid(zone, cdb, NULL, cversion);
1676	if (result != ISC_R_SUCCESS) {
1677		CTRACE("query_addadditional2: old auth additional cache");
1678		query_discardcache(client, rdataset_base, additionaltype,
1679				   type, &zone, &cdb, &cversion, &cnode,
1680				   &cfname);
1681		goto findauthdb;
1682	}
1683
1684	if (cnode == NULL) {
1685		/*
1686		 * We have a negative cache.  We don't have to check the zone
1687		 * ACL, since the result (not using this zone) would be same
1688		 * regardless of the result.
1689		 */
1690		CTRACE("query_addadditional2: negative auth additional cache");
1691		dns_db_closeversion(cdb, &cversion, ISC_FALSE);
1692		dns_db_detach(&cdb);
1693		dns_zone_detach(&zone);
1694		goto try_cache;
1695	}
1696
1697	result = query_validatezonedb(client, name, qtype, DNS_GETDB_NOLOG,
1698				      zone, cdb, NULL);
1699	if (result != ISC_R_SUCCESS) {
1700		query_discardcache(client, rdataset_base, additionaltype,
1701				   type, &zone, &cdb, &cversion, &cnode,
1702				   &cfname);
1703		goto try_cache;
1704	}
1705
1706	/* We've got an active cache. */
1707	CTRACE("query_addadditional2: auth additional cache");
1708	dns_db_closeversion(cdb, &cversion, ISC_FALSE);
1709	db = cdb;
1710	node = cnode;
1711	dns_name_clone(&cfname, fname);
1712	query_keepname(client, fname, dbuf);
1713	goto foundcache;
1714
1715	/*
1716	 * Look for a zone database that might contain authoritative
1717	 * additional data.
1718	 */
1719 findauthdb:
1720	result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
1721				 &zone, &db, &version);
1722	if (result != ISC_R_SUCCESS) {
1723		/* Cache the negative result */
1724		(void)dns_rdataset_setadditional(rdataset_base, additionaltype,
1725						 type, client->view->acache,
1726						 NULL, NULL, NULL, NULL,
1727						 NULL);
1728		goto try_cache;
1729	}
1730
1731	CTRACE("query_addadditional2: db_find");
1732
1733	/*
1734	 * Since we are looking for authoritative data, we do not set
1735	 * the GLUEOK flag.  Glue will be looked for later, but not
1736	 * necessarily in the same database.
1737	 */
1738	node = NULL;
1739	result = dns_db_find(db, name, version, type, client->query.dboptions,
1740			     client->now, &node, fname, NULL, NULL);
1741	if (result == ISC_R_SUCCESS)
1742		goto found;
1743
1744	/* Cache the negative result */
1745	(void)dns_rdataset_setadditional(rdataset_base, additionaltype,
1746					 type, client->view->acache, zone, db,
1747					 version, NULL, fname);
1748
1749	if (node != NULL)
1750		dns_db_detachnode(db, &node);
1751	version = NULL;
1752	dns_db_detach(&db);
1753
1754	/*
1755	 * No authoritative data was found.  The cache is our next best bet.
1756	 */
1757
1758 try_cache:
1759	additionaltype = dns_rdatasetadditional_fromcache;
1760	result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
1761	if (result != ISC_R_SUCCESS)
1762		/*
1763		 * Most likely the client isn't allowed to query the cache.
1764		 */
1765		goto try_glue;
1766
1767	result = dns_db_find(db, name, version, type,
1768			     client->query.dboptions |
1769			     DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK,
1770			     client->now, &node, fname, NULL, NULL);
1771	if (result == ISC_R_SUCCESS)
1772		goto found;
1773
1774	if (node != NULL)
1775		dns_db_detachnode(db, &node);
1776	dns_db_detach(&db);
1777
1778 try_glue:
1779	/*
1780	 * No cached data was found.  Glue is our last chance.
1781	 * RFC1035 sayeth:
1782	 *
1783	 *	NS records cause both the usual additional section
1784	 *	processing to locate a type A record, and, when used
1785	 *	in a referral, a special search of the zone in which
1786	 *	they reside for glue information.
1787	 *
1788	 * This is the "special search".  Note that we must search
1789	 * the zone where the NS record resides, not the zone it
1790	 * points to, and that we only do the search in the delegation
1791	 * case (identified by client->query.gluedb being set).
1792	 */
1793	if (client->query.gluedb == NULL)
1794		goto cleanup;
1795
1796	/*
1797	 * Don't poison caches using the bailiwick protection model.
1798	 */
1799	if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
1800		goto cleanup;
1801
1802	/* Check additional cache */
1803	additionaltype = dns_rdatasetadditional_fromglue;
1804	result = dns_rdataset_getadditional(rdataset_base, additionaltype,
1805					    type, client->view->acache, NULL,
1806					    &cdb, &cversion, &cnode, &cfname,
1807					    client->message, client->now);
1808	if (result != ISC_R_SUCCESS)
1809		goto findglue;
1810
1811	result = query_iscachevalid(zone, cdb, client->query.gluedb, cversion);
1812	if (result != ISC_R_SUCCESS) {
1813		CTRACE("query_addadditional2: old glue additional cache");
1814		query_discardcache(client, rdataset_base, additionaltype,
1815				   type, &zone, &cdb, &cversion, &cnode,
1816				   &cfname);
1817		goto findglue;
1818	}
1819
1820	if (cnode == NULL) {
1821		/* We have a negative cache. */
1822		CTRACE("query_addadditional2: negative glue additional cache");
1823		dns_db_closeversi

Large files files are truncated, but you can click here to view the full file