query.c 72.1 KB
Newer Older
Bob Halley's avatar
add  
Bob Halley committed
1
/*
Bob Halley's avatar
Bob Halley committed
2
 * Copyright (C) 1999, 2000  Internet Software Consortium.
Bob Halley's avatar
add  
Bob Halley committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM 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.
 */

#include <config.h>

#include <isc/assertions.h>
Bob Halley's avatar
Bob Halley committed
21
#include <isc/buffer.h>
Bob Halley's avatar
add  
Bob Halley committed
22 23 24 25 26
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/result.h>
#include <isc/task.h>
#include <isc/timer.h>
Bob Halley's avatar
Bob Halley committed
27
#include <isc/event.h>
Bob Halley's avatar
Bob Halley committed
28
#include <isc/log.h>
Michael Graff's avatar
Michael Graff committed
29
#include <isc/util.h>
Bob Halley's avatar
add  
Bob Halley committed
30

Bob Halley's avatar
Bob Halley committed
31
#include <dns/a6.h>
32
#include <dns/acl.h>
Bob Halley's avatar
Bob Halley committed
33 34
#include <dns/db.h>
#include <dns/dbtable.h>
Bob Halley's avatar
add  
Bob Halley committed
35 36
#include <dns/dispatch.h>
#include <dns/events.h>
37
#include <dns/fixedname.h>
Bob Halley's avatar
Bob Halley committed
38 39 40
#include <dns/message.h>
#include <dns/name.h>
#include <dns/rdata.h>
41
#include <dns/rdatatype.h>
42
#include <dns/rdatalist.h>
Bob Halley's avatar
Bob Halley committed
43 44
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
45
#include <dns/resolver.h>
46
#include <dns/view.h>
Brian Wellington's avatar
Brian Wellington committed
47
#include <dns/tkey.h>
Bob Halley's avatar
Bob Halley committed
48 49
#include <dns/zone.h>
#include <dns/zt.h>
Bob Halley's avatar
add  
Bob Halley committed
50 51

#include <named/client.h>
Bob Halley's avatar
Bob Halley committed
52
#include <named/globals.h>
53
#include <named/log.h>
Bob Halley's avatar
Bob Halley committed
54
#include <named/query.h>
55
#include <named/server.h>
56
#include <named/xfrout.h>
57

Bob Halley's avatar
Bob Halley committed
58 59
#define PARTIALANSWER(c)	(((c)->query.attributes & \
				  NS_QUERYATTR_PARTIALANSWER) != 0)
Bob Halley's avatar
Bob Halley committed
60 61 62 63
#define USECACHE(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEOK) != 0)
#define RECURSIONOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSIONOK) != 0)
64 65
#define RECURSING(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_RECURSING) != 0)
Bob Halley's avatar
Bob Halley committed
66 67
#define CACHEGLUEOK(c)		(((c)->query.attributes & \
				  NS_QUERYATTR_CACHEGLUEOK) != 0)
Bob Halley's avatar
Bob Halley committed
68

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
#if 0
#define CTRACE(m)       isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_CLIENT, \
				      NS_LOGMODULE_QUERY, \
                                      ISC_LOG_DEBUG(3), \
                                      "client %p: %s", client, (m))
#define QTRACE(m)       isc_log_write(ns_g_lctx, \
				      NS_LOGCATEGORY_GENERAL, \
				      NS_LOGMODULE_QUERY, \
                                      ISC_LOG_DEBUG(3), \
                                      "query %p: %s", query, (m))
#else
#define CTRACE(m) ((void)m)
#define QTRACE(m) ((void)m)
#endif


Bob Halley's avatar
Bob Halley committed
86 87
static isc_result_t
query_simplefind(void *arg, dns_name_t *name, dns_rdatatype_t type,
Bob Halley's avatar
Bob Halley committed
88
		 isc_stdtime_t now,
Bob Halley's avatar
Bob Halley committed
89 90 91 92 93 94
		 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);

static inline void
query_adda6rrset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset,
		      dns_rdataset_t *sigrdataset);

95 96 97 98 99 100 101 102 103 104 105 106 107
static void
query_find(ns_client_t *client, dns_fetchevent_t *event);


static inline void
query_maybeputqname(ns_client_t *client) {
	if (client->query.restarts > 0) {
		/*
		 * client->query.qname was dynamically allocated.
		 */
		dns_message_puttempname(client->message,
					&client->query.qname);
		client->query.qname = NULL;
108
	}
109
}
Bob Halley's avatar
Bob Halley committed
110

Bob Halley's avatar
Bob Halley committed
111
static inline void
112
query_reset(ns_client_t *client, isc_boolean_t everything) {
113
	isc_buffer_t *dbuf, *dbuf_next;
114 115 116
	ns_dbversion_t *dbversion, *dbversion_next;
	unsigned int i;

117 118 119
	/*
	 * Reset the query state of a client to its default state.
	 */
120

121
	/*
122
	 * Cancel the fetch if it's running.
123 124
	 */
	if (client->query.fetch != NULL) {
Bob Halley's avatar
Bob Halley committed
125 126
		dns_resolver_cancelfetch(client->query.fetch);
					 
127
		client->query.fetch = NULL;
128 129
	}

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	/*
	 * 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);
		ISC_LIST_APPEND(client->query.freeversions, dbversion, link);
	}
	ISC_LIST_INIT(client->query.activeversions);

	/*
	 * Clean up free versions.
	 */
	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);
		}
	}
Bob Halley's avatar
Bob Halley committed
162 163 164 165 166 167 168

	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);
169
			isc_buffer_free(&dbuf);
Bob Halley's avatar
Bob Halley committed
170 171
		}
	}
Bob Halley's avatar
Bob Halley committed
172

173 174
	query_maybeputqname(client);

175 176
	client->query.attributes = (NS_QUERYATTR_RECURSIONOK|
				    NS_QUERYATTR_CACHEOK);
177
	client->query.restarts = 0;
178
	client->query.origqname = NULL;
179
	client->query.qname = NULL;
180
	client->query.qrdataset = NULL;
181
	client->query.dboptions = 0;
182
	client->query.gluedb = NULL;
Bob Halley's avatar
Bob Halley committed
183 184 185 186
}

static void
query_next(ns_client_t *client, isc_result_t result) {
187 188
	(void)result;
	query_reset(client, ISC_FALSE);
Bob Halley's avatar
Bob Halley committed
189 190
}

191 192 193
void
ns_query_free(ns_client_t *client) {
	query_reset(client, ISC_TRUE);
Bob Halley's avatar
Bob Halley committed
194 195 196 197
}

static inline isc_result_t
query_newnamebuf(ns_client_t *client) {
198
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
199 200
	isc_result_t result;

201
	CTRACE("query_newnamebuf");
202 203 204 205
	/*
	 * Allocate a name buffer.
	 */

Bob Halley's avatar
Bob Halley committed
206
	dbuf = NULL;
207 208
	result = isc_buffer_allocate(client->mctx, &dbuf, 1024,
				     ISC_BUFFERTYPE_BINARY);
209 210
	if (result != ISC_R_SUCCESS) {
		CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
Bob Halley's avatar
Bob Halley committed
211
		return (result);
212
	}
Bob Halley's avatar
Bob Halley committed
213 214
	ISC_LIST_APPEND(client->query.namebufs, dbuf, link);

215
	CTRACE("query_newnamebuf: done");
Bob Halley's avatar
Bob Halley committed
216 217 218
	return (ISC_R_SUCCESS);
}

219
static inline isc_buffer_t *
Bob Halley's avatar
Bob Halley committed
220
query_getnamebuf(ns_client_t *client) {
221
	isc_buffer_t *dbuf;
Bob Halley's avatar
Bob Halley committed
222 223 224
	isc_result_t result;
	isc_region_t r;

225
	CTRACE("query_getnamebuf");
226 227 228 229 230
	/*
	 * Return a name buffer with space for a maximal name, allocating
	 * a new one if necessary.
	 */

Bob Halley's avatar
Bob Halley committed
231 232
	if (ISC_LIST_EMPTY(client->query.namebufs)) {
		result = query_newnamebuf(client);
233 234
		if (result != ISC_R_SUCCESS) {
		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
235
			return (NULL);
236
		}
Bob Halley's avatar
Bob Halley committed
237 238 239 240
	}

	dbuf = ISC_LIST_TAIL(client->query.namebufs);
	INSIST(dbuf != NULL);
241
	isc_buffer_available(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
242 243
	if (r.length < 255) {
		result = query_newnamebuf(client);
244 245
		if (result != ISC_R_SUCCESS) {
		    CTRACE("query_getnamebuf: query_newnamebuf failed: done");
Bob Halley's avatar
Bob Halley committed
246
			return (NULL);
247 248

		}
Bob Halley's avatar
Bob Halley committed
249
		dbuf = ISC_LIST_TAIL(client->query.namebufs);
250
		isc_buffer_available(dbuf, &r);
Bob Halley's avatar
Bob Halley committed
251 252
		INSIST(r.length >= 255);
	}
253
	CTRACE("query_getnamebuf: done");
Bob Halley's avatar
Bob Halley committed
254 255 256
	return (dbuf);
}

257
static inline void
258
query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
259 260
	isc_region_t r;

261
	CTRACE("query_keepname");
262 263 264 265 266 267 268 269
	/*
	 * '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);
270
	isc_buffer_add(dbuf, r.length);
271 272 273 274 275 276 277 278
	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;

279 280 281 282 283 284
	/*
	 * '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.
	 */

285
	CTRACE("query_releasename");
286 287 288 289 290
	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
291
	dns_message_puttempname(client->message, namep);
292
	CTRACE("query_releasename: done");
293 294 295
}

static inline dns_name_t *
296
query_newname(ns_client_t *client, isc_buffer_t *dbuf,
297 298 299 300 301 302 303 304
	      isc_buffer_t *nbuf)
{
	dns_name_t *name;
	isc_region_t r;
	isc_result_t result;

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

305
	CTRACE("query_newname");
Bob Halley's avatar
Bob Halley committed
306 307
	name = NULL;
	result = dns_message_gettempname(client->message, &name);
308 309
	if (result != ISC_R_SUCCESS) {
		CTRACE("query_newname: dns_message_gettempname failed: done");
Bob Halley's avatar
Bob Halley committed
310
		return (NULL);
311
	}
312
	isc_buffer_available(dbuf, &r);
313 314 315 316 317
	isc_buffer_init(nbuf, r.base, r.length, ISC_BUFFERTYPE_BINARY);
	dns_name_init(name, NULL);
	dns_name_setbuffer(name, nbuf);
	client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;

318
	CTRACE("query_newname: done");
319 320 321 322 323 324 325
	return (name);
}

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

327
	CTRACE("query_newrdataset");
Bob Halley's avatar
Bob Halley committed
328 329
	rdataset = NULL;
	result = dns_message_gettemprdataset(client->message, &rdataset);
330 331
	if (result != ISC_R_SUCCESS) {
	  CTRACE("query_newrdataset: dns_message_gettemprdataset failed: done");
Bob Halley's avatar
Bob Halley committed
332
		return (NULL);
333
	}
334 335
	dns_rdataset_init(rdataset);

336
	CTRACE("query_newrdataset: done");
337 338 339
	return (rdataset);
}

Bob Halley's avatar
Bob Halley committed
340 341 342 343
static inline void
query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
	dns_rdataset_t *rdataset = *rdatasetp;

344
	CTRACE("query_putrdataset");
Bob Halley's avatar
Bob Halley committed
345 346 347 348 349
	if (rdataset != NULL) {
		if (rdataset->methods != NULL)
			dns_rdataset_disassociate(rdataset);
		dns_message_puttemprdataset(client->message, rdatasetp);
	}
350
	CTRACE("query_putrdataset: done");
Bob Halley's avatar
Bob Halley committed
351 352
}

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
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++) {
		dbversion = isc_mem_get(client->mctx, sizeof *dbversion);
		if (dbversion != NULL) {
			dbversion->db = NULL;
			dbversion->version = NULL;
			ISC_LIST_APPEND(client->query.freeversions, dbversion,
					link);
		} 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);
	
	return (dbversion);
}

Bob Halley's avatar
Bob Halley committed
397 398
isc_result_t
ns_query_init(ns_client_t *client) {
399 400
	isc_result_t result;

Bob Halley's avatar
Bob Halley committed
401
	ISC_LIST_INIT(client->query.namebufs);
402 403
	ISC_LIST_INIT(client->query.activeversions);
	ISC_LIST_INIT(client->query.freeversions);
404 405 406
	client->query.restarts = 0;
	client->query.qname = NULL;
	client->query.fetch = NULL;
407
	query_reset(client, ISC_FALSE);
408 409 410
	result = query_newdbversion(client, 3);
	if (result != ISC_R_SUCCESS)
		return (result);
Bob Halley's avatar
Bob Halley committed
411 412
	dns_a6_init(&client->query.a6ctx, query_simplefind, query_adda6rrset,
		    NULL, NULL, client);
Bob Halley's avatar
Bob Halley committed
413 414 415
	return (query_newnamebuf(client));
}

416 417 418 419
static inline dns_dbversion_t *
query_findversion(ns_client_t *client, dns_db_t *db) {
	ns_dbversion_t *dbversion;

420
	CTRACE("query_findversion");
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
	/*
	 * 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;
	}	
	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);
		ISC_LIST_APPEND(client->query.activeversions,
				dbversion, link);
	}
	
446
	CTRACE("query_findversion: done");
447 448 449
	return (dbversion->version);
}

Bob Halley's avatar
Bob Halley committed
450 451
static isc_result_t
query_simplefind(void *arg, dns_name_t *name, dns_rdatatype_t type,
Bob Halley's avatar
Bob Halley committed
452
		 isc_stdtime_t now,
Bob Halley's avatar
Bob Halley committed
453 454 455 456 457 458 459
		 dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
{
	ns_client_t *client = arg;
	isc_result_t result;
	dns_fixedname_t foundname;
	dns_db_t *db;
	dns_dbversion_t *version;
460
	unsigned int dboptions;
Bob Halley's avatar
Bob Halley committed
461 462
	isc_boolean_t is_zone;
	dns_rdataset_t zrdataset, zsigrdataset;
Bob Halley's avatar
Bob Halley committed
463
	dns_zone_t *zone;
Bob Halley's avatar
Bob Halley committed
464 465

	REQUIRE(NS_CLIENT_VALID(client));
466 467
	REQUIRE(rdataset != NULL);
	REQUIRE(sigrdataset != NULL);
Bob Halley's avatar
Bob Halley committed
468

Bob Halley's avatar
Bob Halley committed
469 470 471
	dns_rdataset_init(&zrdataset);
	dns_rdataset_init(&zsigrdataset);

Bob Halley's avatar
Bob Halley committed
472 473 474
	/*
	 * Find a database to answer the query.
	 */
Bob Halley's avatar
Bob Halley committed
475
	zone = NULL;
Bob Halley's avatar
Bob Halley committed
476
	db = NULL;
Bob Halley's avatar
Bob Halley committed
477
	result = dns_zt_find(client->view->zonetable, name, NULL, &zone);
478
	if (result == DNS_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
479
		isc_result_t tresult;
480 481 482 483
		tresult = dns_zone_getdb(zone, &db);
		if (tresult != DNS_R_SUCCESS)
			result = tresult;
	}
Bob Halley's avatar
Bob Halley committed
484

Bob Halley's avatar
Bob Halley committed
485 486
	if (result == ISC_R_NOTFOUND && USECACHE(client))
		dns_db_attach(client->view->cachedb, &db);
487
	else if (result != DNS_R_SUCCESS && result != DNS_R_PARTIALMATCH)
Bob Halley's avatar
Bob Halley committed
488 489 490 491 492 493
		goto cleanup;

	/*
	 * Get the current version of this database.
	 */
	version = NULL;
Bob Halley's avatar
Bob Halley committed
494 495
	is_zone = dns_db_iszone(db);
	if (is_zone) {
Bob Halley's avatar
Bob Halley committed
496 497 498 499 500
		version = query_findversion(client, db);
		if (version == NULL)
			goto cleanup;
	}

Bob Halley's avatar
Bob Halley committed
501
 db_find:
Bob Halley's avatar
Bob Halley committed
502 503 504 505
	/*
	 * Now look for an answer in the database.
	 */
	dns_fixedname_init(&foundname);
506
	dboptions = client->query.dboptions;
Bob Halley's avatar
Bob Halley committed
507
	if (db == client->query.gluedb || (!is_zone && CACHEGLUEOK(client)))
508 509
		dboptions |= DNS_DBFIND_GLUEOK;
	result = dns_db_find(db, name, version, type, dboptions,
Bob Halley's avatar
Bob Halley committed
510
			     now, NULL, dns_fixedname_name(&foundname),
Bob Halley's avatar
Bob Halley committed
511 512
			     rdataset, sigrdataset);

Bob Halley's avatar
Bob Halley committed
513
	if (result == DNS_R_DELEGATION ||
Bob Halley's avatar
Bob Halley committed
514
	    result == DNS_R_NOTFOUND) {
Bob Halley's avatar
Bob Halley committed
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
		if (rdataset->methods != NULL)
			dns_rdataset_disassociate(rdataset);
		if (sigrdataset->methods != NULL)
			dns_rdataset_disassociate(sigrdataset);
		if (is_zone) {
			if (USECACHE(client)) {
				/*
				 * Either the answer is in the cache, or we
				 * don't know it.
				 */
				is_zone = ISC_FALSE;
				version = NULL;
				dns_db_detach(&db);
				dns_db_attach(client->view->cachedb, &db);
				goto db_find;
			}
		} else {
			/*
			 * We don't have the data in the cache.  If we've got
			 * glue from the zone, use it.
			 */
			if (zrdataset.methods != NULL) {
				dns_rdataset_clone(&zrdataset, rdataset);
				if (zsigrdataset.methods != NULL)
					dns_rdataset_clone(&zsigrdataset,
							   sigrdataset);
				result = ISC_R_SUCCESS;
				goto cleanup;
			}
		}
		/*
		 * We don't know the answer.
		 */
		result = DNS_R_NOTFOUND;
	} else if (result == DNS_R_GLUE) {
550
		if (USECACHE(client) && RECURSIONOK(client)) {
Bob Halley's avatar
Bob Halley committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
			/*
			 * We found an answer, but the cache may be better.
			 * Remember what we've got and go look in the cache.
			 */
			is_zone = ISC_FALSE;
			version = NULL;
			dns_rdataset_clone(rdataset, &zrdataset);
			dns_rdataset_disassociate(rdataset);
			if (sigrdataset->methods != NULL) {
				dns_rdataset_clone(sigrdataset, &zsigrdataset);
				dns_rdataset_disassociate(sigrdataset);
			}
			dns_db_detach(&db);
			dns_db_attach(client->view->cachedb, &db);
			goto db_find;
		}
		/*
		 * Otherwise, the glue is the best answer.
		 */
		result = ISC_R_SUCCESS;
571 572 573 574 575
	} else if (result != ISC_R_SUCCESS) {
		if (rdataset->methods != NULL)
			dns_rdataset_disassociate(rdataset);
		if (sigrdataset->methods != NULL)
			dns_rdataset_disassociate(sigrdataset);
Bob Halley's avatar
Bob Halley committed
576
		result = DNS_R_NOTFOUND;
577
	}
Bob Halley's avatar
Bob Halley committed
578

Bob Halley's avatar
Bob Halley committed
579
 cleanup:
Bob Halley's avatar
Bob Halley committed
580 581 582 583 584
	if (zrdataset.methods != NULL) {
		dns_rdataset_disassociate(&zrdataset);
		if (zsigrdataset.methods != NULL)
			dns_rdataset_disassociate(&zsigrdataset);
	}
Bob Halley's avatar
Bob Halley committed
585 586
	if (db != NULL)
		dns_db_detach(&db);
Bob Halley's avatar
Bob Halley committed
587 588
	if (zone != NULL)
		dns_zone_detach(&zone);
Bob Halley's avatar
Bob Halley committed
589 590 591 592

	return (result);
}

Bob Halley's avatar
Bob Halley committed
593 594
static inline isc_boolean_t
query_isduplicate(ns_client_t *client, dns_name_t *name,
595
		  dns_rdatatype_t type, dns_name_t **mnamep)
Bob Halley's avatar
Bob Halley committed
596 597
{
	dns_section_t section;
598
	dns_name_t *mname = NULL;
599
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
600

601 602
	CTRACE("query_isduplicate");

Bob Halley's avatar
Bob Halley committed
603 604 605
	for (section = DNS_SECTION_ANSWER;
	     section <= DNS_SECTION_ADDITIONAL;
	     section++) {
606 607 608
		result = dns_message_findname(client->message, section,
					      name, type, 0, &mname, NULL);
		if (result == ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
609 610 611
			/*
			 * We've already got this RRset in the response.
			 */
612
			CTRACE("query_isduplicate: true: done");
Bob Halley's avatar
Bob Halley committed
613
			return (ISC_TRUE);
614 615 616 617 618 619 620 621 622
		} else if (result == DNS_R_NXRDATASET) {
			/*
			 * The name exists, but the rdataset does not.
			 */
			if (section == DNS_SECTION_ADDITIONAL)
				break;
		} else
			RUNTIME_CHECK(result == DNS_R_NXDOMAIN);
		mname = NULL;
Bob Halley's avatar
Bob Halley committed
623 624
	}

625 626 627 628 629 630 631 632 633
	/*
	 * If the dns_name_t we're lookup up is already in the message,
	 * we don't want to trigger the caller's name replacement logic.
	 */
	if (name == mname)
		mname = NULL;

	*mnamep = mname;

634
	CTRACE("query_isduplicate: false: done");
Bob Halley's avatar
Bob Halley committed
635 636
	return (ISC_FALSE);
}
Bob Halley's avatar
Bob Halley committed
637

638
static isc_result_t
Bob Halley's avatar
Bob Halley committed
639
query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
640
	ns_client_t *client = arg;
641
	isc_result_t result, eresult;
Bob Halley's avatar
Bob Halley committed
642 643
	dns_dbnode_t *node, *znode;
	dns_db_t *db, *zdb;
644
	dns_name_t *fname, *zfname, *mname;
645
	dns_rdataset_t *rdataset, *sigrdataset, *a6rdataset, *trdataset;
Bob Halley's avatar
Bob Halley committed
646
	dns_rdataset_t *zrdataset, *zsigrdataset;
647
	isc_buffer_t *dbuf;
648
	isc_buffer_t b;
Bob Halley's avatar
Bob Halley committed
649
	dns_dbversion_t *version, *zversion;
650
	unsigned int dboptions;
651
	isc_boolean_t is_zone, added_something, need_addname;
Bob Halley's avatar
Bob Halley committed
652
	dns_zone_t *zone;
Bob Halley's avatar
Bob Halley committed
653
	dns_rdatatype_t type;
654 655

	REQUIRE(NS_CLIENT_VALID(client));
Bob Halley's avatar
Bob Halley committed
656
	REQUIRE(qtype != dns_rdatatype_any);
657

658 659
	CTRACE("query_addadditional");

660
	/*
661
	 * Initialization.
662
	 */
663
	eresult = ISC_R_SUCCESS;
664
	fname = NULL;
Bob Halley's avatar
Bob Halley committed
665
	zfname = NULL;
666
	rdataset = NULL;
Bob Halley's avatar
Bob Halley committed
667
	zrdataset = NULL;
668
	sigrdataset = NULL;
Bob Halley's avatar
Bob Halley committed
669
	zsigrdataset = NULL;
Bob Halley's avatar
Bob Halley committed
670
	a6rdataset = NULL;
671
	trdataset = NULL;
672
	db = NULL;
Bob Halley's avatar
Bob Halley committed
673
	zdb = NULL;
674
	version = NULL;
Bob Halley's avatar
Bob Halley committed
675
	zversion = NULL;
676
	node = NULL;
Bob Halley's avatar
Bob Halley committed
677 678
	znode = NULL;
	added_something = ISC_FALSE;
679
	need_addname = ISC_FALSE;
Bob Halley's avatar
Bob Halley committed
680
	zone = NULL;
Bob Halley's avatar
Bob Halley committed
681 682 683 684
	if (qtype == dns_rdatatype_a)
		type = dns_rdatatype_any;
	else
		type = qtype;
685 686 687 688

	/*
	 * Find a database to answer the query.
	 */
Bob Halley's avatar
Bob Halley committed
689
	result = dns_zt_find(client->view->zonetable, name, NULL, &zone);
690
	if (result == DNS_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
691
		isc_result_t tresult;
692 693 694 695
		tresult = dns_zone_getdb(zone, &db);
		if (tresult != DNS_R_SUCCESS)
			result = tresult;
	}
Bob Halley's avatar
Bob Halley committed
696

Bob Halley's avatar
Bob Halley committed
697 698
	if (result == ISC_R_NOTFOUND && USECACHE(client))
		dns_db_attach(client->view->cachedb, &db);
699
	else if (result != DNS_R_SUCCESS && result != DNS_R_PARTIALMATCH)
700
		goto cleanup;
701

702 703 704
	/*
	 * Get the current version of this database.
	 */
Bob Halley's avatar
Bob Halley committed
705 706
	is_zone = dns_db_iszone(db);
	if (is_zone) {
707 708 709 710 711
		version = query_findversion(client, db);
		if (version == NULL)
			goto cleanup;
	}

Bob Halley's avatar
Bob Halley committed
712
 db_find:
713
	CTRACE("query_addadditional: db_find");
Bob Halley's avatar
Bob Halley committed
714 715 716 717 718 719 720 721 722 723 724 725
	/*
	 * Get some resources...
	 */
	dbuf = query_getnamebuf(client);
	if (dbuf == NULL)
		goto cleanup;
	fname = query_newname(client, dbuf, &b);
	rdataset = query_newrdataset(client);
	sigrdataset = query_newrdataset(client);
	if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
		goto cleanup;

726 727 728 729
	/*
	 * Now look for an answer in the database.
	 */
	node = NULL;
730
	dboptions = client->query.dboptions;
Bob Halley's avatar
Bob Halley committed
731
	if (db == client->query.gluedb || (!is_zone && CACHEGLUEOK(client)))
732 733
		dboptions |= DNS_DBFIND_GLUEOK;
	result = dns_db_find(db, name, version, type, dboptions,
734
			     client->now, &node, fname, rdataset,
735
			     sigrdataset);
Bob Halley's avatar
Bob Halley committed
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767

	if (result == DNS_R_DELEGATION || result == DNS_R_NOTFOUND) {
		if (is_zone) {
			if (USECACHE(client)) {
				/*
				 * Either the answer is in the cache, or we
				 * don't know it.  Go look in the cache.
				 */
				query_releasename(client, &fname);
				is_zone = ISC_FALSE;
				version = NULL;
				query_putrdataset(client, &rdataset);
				query_putrdataset(client, &sigrdataset);
				dns_db_detachnode(db, &node);
				dns_db_detach(&db);
				dns_db_attach(client->view->cachedb, &db);
				goto db_find;
			} else {
				/*
				 * We don't know the answer.
				 */
				goto cleanup;
			}
		} else {
			/*
			 * We don't have the data in the cache.  If we've
			 * got glue from the zone, use it.
			 */
			if (zdb != NULL) {
				query_releasename(client, &fname);
				query_putrdataset(client, &rdataset);
				query_putrdataset(client, &sigrdataset);
768 769
				if (node != NULL)
					dns_db_detachnode(db, &node);
Bob Halley's avatar
Bob Halley committed
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
				dns_db_detach(&db);
				db = zdb;
				zdb = NULL;
				fname = zfname;
				dbuf = NULL;
				node = znode;
				version = zversion;
				rdataset = zrdataset;
				sigrdataset = zsigrdataset;
			} else {
				/*
				 * We don't know the answer.
				 */
				goto cleanup;
			}
		}
Bob Halley's avatar
Bob Halley committed
786
	} else if (result == DNS_R_GLUE) {
787
		if (USECACHE(client) && RECURSIONOK(client)) {
Bob Halley's avatar
Bob Halley committed
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
			/*
			 * We found an answer, but the cache may be
			 * better.  Remember what we've got and go look in
			 * the cache.
			 */
			query_keepname(client, fname, dbuf);
			zfname = fname;
			zdb = db;
			zversion = version;
			znode = node;
			zrdataset = rdataset;
			zsigrdataset = sigrdataset;
			version = NULL;
			db = NULL;
			dns_db_attach(client->view->cachedb, &db);
			is_zone = ISC_FALSE;
			goto db_find;
		}
Bob Halley's avatar
Bob Halley committed
806
	} else if (result != ISC_R_SUCCESS && result != DNS_R_ZONECUT)
807
		goto cleanup;
808

Bob Halley's avatar
Bob Halley committed
809 810
	if (dbuf != NULL)
		query_keepname(client, fname, dbuf);
Bob Halley's avatar
Bob Halley committed
811

812
	mname = NULL;
Bob Halley's avatar
Bob Halley committed
813
	if (rdataset->methods != NULL &&
814 815 816 817 818 819
	    !query_isduplicate(client, fname, type, &mname)) {
		if (mname != NULL) {
			query_releasename(client, &fname);
			fname = mname;
		} else
			need_addname = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
820
		ISC_LIST_APPEND(fname->list, rdataset, link);
821
		trdataset = rdataset;
Bob Halley's avatar
Bob Halley committed
822
		rdataset = NULL;
Bob Halley's avatar
Bob Halley committed
823
		added_something = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
824 825 826 827 828 829 830 831
		/*
		 * Note: we only add SIGs if we've added the type they cover,
		 * so we do not need to check if the SIG rdataset is already
		 * in the response.
		 */
		if (sigrdataset->methods != NULL) {
			ISC_LIST_APPEND(fname->list, sigrdataset, link);
			sigrdataset = NULL;
832 833
		}
	}
Bob Halley's avatar
Bob Halley committed
834

Bob Halley's avatar
Bob Halley committed
835
	if (qtype == dns_rdatatype_a) {
Bob Halley's avatar
Bob Halley committed
836 837 838 839
		/*
		 * We treat type A additional section processing as if it
		 * were "any address type" additional section processing.
		 *
Bob Halley's avatar
Bob Halley committed
840
		 * We now go looking for A, A6, and AAAA records, along with
Bob Halley's avatar
Bob Halley committed
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
		 * their signatures.
		 *
		 * XXXRTH  This code could be more efficient.
		 */
		if (rdataset != NULL) {
			if (rdataset->methods != NULL)
				dns_rdataset_disassociate(rdataset);
		} else {
			rdataset = query_newrdataset(client);
			if (rdataset == NULL)
				goto addname;
		}	
		if (sigrdataset != NULL) {
			if (sigrdataset->methods != NULL)
				dns_rdataset_disassociate(sigrdataset);
		} else {
			sigrdataset = query_newrdataset(client);
			if (sigrdataset == NULL)
				goto addname;
		}	
Bob Halley's avatar
Bob Halley committed
861 862
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_a, 0,
863
					     client->now, rdataset,
Bob Halley's avatar
Bob Halley committed
864
					     sigrdataset);
865 866 867 868 869 870 871 872 873
		if (result == DNS_R_NCACHENXDOMAIN)
			goto addname;
		if (result == DNS_R_NCACHENXRRSET) {
			dns_rdataset_disassociate(rdataset);
			/*
			 * Negative cache entries don't have sigrdatasets.
			 */
			INSIST(sigrdataset->methods == NULL);
		}
Bob Halley's avatar
Bob Halley committed
874 875 876 877 878 879 880
		if (zdb != NULL && result == ISC_R_NOTFOUND) {
			/*
			 * The cache doesn't have an A, but we may have
			 * one in the zone's glue.
			 */
			result = dns_db_findrdataset(zdb, znode, zversion,
						     dns_rdatatype_a, 0,
881
						     client->now,
Bob Halley's avatar
Bob Halley committed
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
						     rdataset,
						     sigrdataset);
		}
		if (result == ISC_R_SUCCESS) {
			mname = NULL;
			if (!query_isduplicate(client, fname,
					       dns_rdatatype_a, &mname)) {
				if (mname != NULL) {
					query_releasename(client, &fname);
					fname = mname;
				} else
					need_addname = ISC_TRUE;
				ISC_LIST_APPEND(fname->list, rdataset, link);
				added_something = ISC_TRUE;
				if (sigrdataset->methods != NULL) {
					ISC_LIST_APPEND(fname->list,
							sigrdataset, link);
					sigrdataset =
						query_newrdataset(client);
				}
				rdataset = query_newrdataset(client);
				if (rdataset == NULL || sigrdataset == NULL)
					goto addname;
			} else
				dns_rdataset_disassociate(rdataset);
		}
Bob Halley's avatar
Bob Halley committed
908 909
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_a6, 0,
910
					     client->now, rdataset,
Bob Halley's avatar
Bob Halley committed
911
					     sigrdataset);
912 913 914 915 916 917
		if (result == DNS_R_NCACHENXDOMAIN)
			goto addname;
		if (result == DNS_R_NCACHENXRRSET) {
			dns_rdataset_disassociate(rdataset);
			INSIST(sigrdataset->methods == NULL);
		}
Bob Halley's avatar
Bob Halley committed
918 919 920 921 922 923 924
		if (zdb != NULL && result == ISC_R_NOTFOUND) {
			/*
			 * The cache doesn't have an A6, but we may have
			 * one in the zone's glue.
			 */
			result = dns_db_findrdataset(zdb, znode, zversion,
						     dns_rdatatype_a6, 0,
925
						     client->now,
Bob Halley's avatar
Bob Halley committed
926 927 928
						     rdataset,
						     sigrdataset);
		}
Bob Halley's avatar
Bob Halley committed
929
		if (result == ISC_R_SUCCESS) {
930
			mname = NULL;
Bob Halley's avatar
Bob Halley committed
931
			if (!query_isduplicate(client, fname,
932 933 934 935 936 937
					       dns_rdatatype_a6, &mname)) {
				if (mname != NULL) {
					query_releasename(client, &fname);
					fname = mname;
				} else
					need_addname = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
938 939
				a6rdataset = rdataset;
				ISC_LIST_APPEND(fname->list, rdataset, link);
Bob Halley's avatar
Bob Halley committed
940
				added_something = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
941 942 943 944 945 946 947 948 949 950 951 952 953 954
				if (sigrdataset->methods != NULL) {
					ISC_LIST_APPEND(fname->list,
							sigrdataset, link);
					sigrdataset =
						query_newrdataset(client);
				}
				rdataset = query_newrdataset(client);
				if (rdataset == NULL || sigrdataset == NULL)
					goto addname;
			} else
				dns_rdataset_disassociate(rdataset);
		}
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_aaaa, 0,
955
					     client->now, rdataset,
Bob Halley's avatar
Bob Halley committed
956
					     sigrdataset);
957 958 959 960 961 962
		if (result == DNS_R_NCACHENXDOMAIN)
			goto addname;
		if (result == DNS_R_NCACHENXRRSET) {
			dns_rdataset_disassociate(rdataset);
			INSIST(sigrdataset->methods == NULL);
		}
Bob Halley's avatar
Bob Halley committed
963 964 965 966 967 968 969
		if (zdb != NULL && result == ISC_R_NOTFOUND) {
			/*
			 * The cache doesn't have an AAAA, but we may have
			 * one in the zone's glue.
			 */
			result = dns_db_findrdataset(zdb, znode, zversion,
						     dns_rdatatype_aaaa, 0,
970
						     client->now,
Bob Halley's avatar
Bob Halley committed
971 972 973
						     rdataset,
						     sigrdataset);
		}
Bob Halley's avatar
Bob Halley committed
974
		if (result == ISC_R_SUCCESS) {
975
			mname = NULL;
Bob Halley's avatar
Bob Halley committed
976
			if (!query_isduplicate(client, fname,
977 978 979 980 981 982
					       dns_rdatatype_aaaa, &mname)) {
				if (mname != NULL) {
					query_releasename(client, &fname);
					fname = mname;
				} else
					need_addname = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
983
				ISC_LIST_APPEND(fname->list, rdataset, link);
Bob Halley's avatar
Bob Halley committed
984
				added_something = ISC_TRUE;
Bob Halley's avatar
Bob Halley committed
985 986 987 988 989 990 991 992
				if (sigrdataset->methods != NULL) {
					ISC_LIST_APPEND(fname->list,
							sigrdataset, link);
					sigrdataset = NULL;
				}
				rdataset = NULL;
			}
		}
993
	}
994

Bob Halley's avatar
Bob Halley committed
995
 addname:
996
	CTRACE("query_addadditional: addname");
Bob Halley's avatar
Bob Halley committed
997 998 999 1000 1001 1002 1003
	/*
	 * If we haven't added anything, then we're done.
	 */
	if (!added_something)
		goto cleanup;

	/*
1004 1005 1006
	 * We may have added our rdatasets to an existing name, if so, then
	 * need_addname will be ISC_FALSE.  Whether we used an existing name
	 * or a new one, we must set fname to NULL to prevent cleanup.
Bob Halley's avatar
Bob Halley committed
1007
	 */
1008 1009 1010
	if (need_addname)
		dns_message_addname(client->message, fname,
				    DNS_SECTION_ADDITIONAL);
1011
	fname = NULL;
1012

Bob Halley's avatar
Bob Halley committed
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
	/*
	 * In a few cases, we want to add additional data for additional
	 * data.  It's simpler to just deal with special cases here than
	 * to try to create a general purpose mechanism and allow the
	 * rdata implementations to do it themselves.
	 *
	 * This involves recursion, but the depth is limited.  The
	 * most complex case is adding a SRV rdataset, which involves
	 * recursing to add address records, which in turn can cause
	 * recursion to add KEYs.
	 */
Bob Halley's avatar
Bob Halley committed
1024
	if (type == dns_rdatatype_a || type == dns_rdatatype_aaaa) {
Bob Halley's avatar
Bob Halley committed
1025 1026 1027
		/*
		 * RFC 2535 section 3.5 says that when A or AAAA records are
		 * retrieved as additional data, any KEY RRs for the owner name
Bob Halley's avatar
Bob Halley committed
1028
		 * should be added to the additional data section.  Note: we
Bob Halley's avatar