nta.c 14.9 KB
Newer Older
Evan Hunt's avatar
Evan Hunt committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
Evan Hunt's avatar
Evan Hunt 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/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*! \file */

#include <config.h>

Evan Hunt's avatar
Evan Hunt committed
21
#include <isc/buffer.h>
Evan Hunt's avatar
Evan Hunt committed
22
23
#include <isc/log.h>
#include <isc/mem.h>
Mark Andrews's avatar
Mark Andrews committed
24
#include <isc/print.h>
Evan Hunt's avatar
Evan Hunt committed
25
26
#include <isc/rwlock.h>
#include <isc/string.h>
Evan Hunt's avatar
Evan Hunt committed
27
#include <isc/task.h>
Evan Hunt's avatar
Evan Hunt committed
28
#include <isc/time.h>
Evan Hunt's avatar
Evan Hunt committed
29
#include <isc/timer.h>
Evan Hunt's avatar
Evan Hunt committed
30
31
#include <isc/util.h>

Evan Hunt's avatar
Evan Hunt committed
32
#include <dns/db.h>
Evan Hunt's avatar
Evan Hunt committed
33
34
35
36
37
#include <dns/log.h>
#include <dns/nta.h>
#include <dns/fixedname.h>
#include <dns/name.h>
#include <dns/rbt.h>
Evan Hunt's avatar
Evan Hunt committed
38
39
#include <dns/rdataset.h>
#include <dns/resolver.h>
Evan Hunt's avatar
Evan Hunt committed
40
41
#include <dns/result.h>

Evan Hunt's avatar
Evan Hunt committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
struct dns_nta {
	unsigned int		magic;
	isc_refcount_t		refcount;
	dns_ntatable_t		*ntatable;
	isc_timer_t		*timer;
	dns_fetch_t		*fetch;
	dns_rdataset_t		rdataset;
	dns_rdataset_t		sigrdataset;
	dns_fixedname_t		fn;
	dns_name_t		*name;
	isc_stdtime_t		expiry;
};

#define NTA_MAGIC		ISC_MAGIC('N', 'T', 'A', 'n')
#define VALID_NTA(nn)	 	ISC_MAGIC_VALID(nn, NTA_MAGIC)

Evan Hunt's avatar
Evan Hunt committed
58
59
60
61
62
63
64
65
66
67
static void
nta_detach(isc_mem_t *mctx, dns_nta_t **ntap) {
	unsigned int refs;
	dns_nta_t *nta = *ntap;

	REQUIRE(VALID_NTA(nta));

	*ntap = NULL;
	isc_refcount_decrement(&nta->refcount, &refs);
	if (refs == 0) {
Evan Hunt's avatar
Evan Hunt committed
68
69
70
71
72
73
74
		nta->magic = 0;
		if (nta->timer != NULL) {
			(void) isc_timer_reset(nta->timer,
					       isc_timertype_inactive,
					       NULL, NULL, ISC_TRUE);
			isc_timer_detach(&nta->timer);
		}
Evan Hunt's avatar
Evan Hunt committed
75
		isc_refcount_destroy(&nta->refcount);
Evan Hunt's avatar
Evan Hunt committed
76
77
78
79
80
81
82
83
		if (dns_rdataset_isassociated(&nta->rdataset))
			dns_rdataset_disassociate(&nta->rdataset);
		if (dns_rdataset_isassociated(&nta->sigrdataset))
			dns_rdataset_disassociate(&nta->sigrdataset);
		if (nta->fetch != NULL) {
			dns_resolver_cancelfetch(nta->fetch);
			dns_resolver_destroyfetch(&nta->fetch);
		}
Evan Hunt's avatar
Evan Hunt committed
84
85
86
87
88
89
90
91
92
93
94
95
96
		isc_mem_put(mctx, nta, sizeof(dns_nta_t));
	}
}

static void
free_nta(void *data, void *arg) {
	dns_nta_t *nta = (dns_nta_t *) data;
	isc_mem_t *mctx = (isc_mem_t *) arg;

	nta_detach(mctx, &nta);
}

isc_result_t
Evan Hunt's avatar
Evan Hunt committed
97
98
99
100
dns_ntatable_create(dns_view_t *view,
		    isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
		    dns_ntatable_t **ntatablep)
{
Evan Hunt's avatar
Evan Hunt committed
101
102
103
104
105
	dns_ntatable_t *ntatable;
	isc_result_t result;

	REQUIRE(ntatablep != NULL && *ntatablep == NULL);

Evan Hunt's avatar
Evan Hunt committed
106
	ntatable = isc_mem_get(view->mctx, sizeof(*ntatable));
Evan Hunt's avatar
Evan Hunt committed
107
108
109
	if (ntatable == NULL)
		return (ISC_R_NOMEMORY);

Evan Hunt's avatar
Evan Hunt committed
110
111
	ntatable->task = NULL;
	result = isc_task_create(taskmgr, 0, &ntatable->task);
Evan Hunt's avatar
Evan Hunt committed
112
113
	if (result != ISC_R_SUCCESS)
		goto cleanup_ntatable;
Evan Hunt's avatar
Evan Hunt committed
114
115
116
117
118
119
120
	isc_task_setname(ntatable->task, "ntatable", ntatable);

	ntatable->table = NULL;
	result = dns_rbt_create(view->mctx, free_nta, view->mctx,
				&ntatable->table);
	if (result != ISC_R_SUCCESS)
		goto cleanup_task;
Evan Hunt's avatar
Evan Hunt committed
121
122
123
124
125

	result = isc_rwlock_init(&ntatable->rwlock, 0, 0);
	if (result != ISC_R_SUCCESS)
		goto cleanup_rbt;

Evan Hunt's avatar
Evan Hunt committed
126
127
128
129
	ntatable->timermgr = timermgr;
	ntatable->taskmgr = taskmgr;

	ntatable->view = view;
Evan Hunt's avatar
Evan Hunt committed
130
	ntatable->references = 1;
Evan Hunt's avatar
Evan Hunt committed
131

Evan Hunt's avatar
Evan Hunt committed
132
133
134
135
136
137
138
139
	ntatable->magic = NTATABLE_MAGIC;
	*ntatablep = ntatable;

	return (ISC_R_SUCCESS);

   cleanup_rbt:
	dns_rbt_destroy(&ntatable->table);

Evan Hunt's avatar
Evan Hunt committed
140
141
142
   cleanup_task:
	isc_task_detach(&ntatable->task);

Evan Hunt's avatar
Evan Hunt committed
143
   cleanup_ntatable:
Evan Hunt's avatar
Evan Hunt committed
144
	isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
Evan Hunt's avatar
Evan Hunt committed
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

	return (result);
}

void
dns_ntatable_attach(dns_ntatable_t *source, dns_ntatable_t **targetp) {
	REQUIRE(VALID_NTATABLE(source));
	REQUIRE(targetp != NULL && *targetp == NULL);

	RWLOCK(&source->rwlock, isc_rwlocktype_write);

	INSIST(source->references > 0);
	source->references++;
	INSIST(source->references != 0);

	RWUNLOCK(&source->rwlock, isc_rwlocktype_write);

	*targetp = source;
}

void
dns_ntatable_detach(dns_ntatable_t **ntatablep) {
	isc_boolean_t destroy = ISC_FALSE;
	dns_ntatable_t *ntatable;

	REQUIRE(ntatablep != NULL && VALID_NTATABLE(*ntatablep));

	ntatable = *ntatablep;
	*ntatablep = NULL;

	RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
	INSIST(ntatable->references > 0);
	ntatable->references--;
	if (ntatable->references == 0)
		destroy = ISC_TRUE;
	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);

	if (destroy) {
		dns_rbt_destroy(&ntatable->table);
		isc_rwlock_destroy(&ntatable->rwlock);
Evan Hunt's avatar
Evan Hunt committed
185
186
187
188
		if (ntatable->task != NULL)
			isc_task_detach(&ntatable->task);
		ntatable->timermgr = NULL;
		ntatable->taskmgr = NULL;
Evan Hunt's avatar
Evan Hunt committed
189
		ntatable->magic = 0;
Evan Hunt's avatar
Evan Hunt committed
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
		isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
	}
}

static void
fetch_done(isc_task_t *task, isc_event_t *event) {
	dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
	dns_nta_t *nta = devent->ev_arg;
	isc_result_t eresult = devent->result;
	dns_ntatable_t *ntatable = nta->ntatable;
	isc_stdtime_t now;

	UNUSED(task);

	if (dns_rdataset_isassociated(&nta->rdataset))
		dns_rdataset_disassociate(&nta->rdataset);
	if (dns_rdataset_isassociated(&nta->sigrdataset))
		dns_rdataset_disassociate(&nta->sigrdataset);
	dns_resolver_destroyfetch(&nta->fetch);

	if (devent->node != NULL)
		dns_db_detachnode(devent->db, &devent->node);
	if (devent->db != NULL)
		dns_db_detach(&devent->db);

	isc_event_free(&event);
Mark Andrews's avatar
Mark Andrews committed
216
	isc_stdtime_get(&now);
Evan Hunt's avatar
Evan Hunt committed
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

	switch (eresult) {
	case ISC_R_SUCCESS:
	case DNS_R_NCACHENXDOMAIN:
	case DNS_R_NXDOMAIN:
	case DNS_R_NCACHENXRRSET:
	case DNS_R_NXRRSET:
		nta->expiry = now;
		break;
	default:
		break;
	}

	/*
	 * If we're expiring before the next recheck, we might
	 * as well stop the timer now.
	 */
	if (nta->timer != NULL && nta->expiry - now < ntatable->recheck)
		(void) isc_timer_reset(nta->timer, isc_timertype_inactive,
				       NULL, NULL, ISC_TRUE);
}

static void
checkbogus(isc_task_t *task, isc_event_t *event) {
	dns_nta_t *nta = event->ev_arg;
	dns_ntatable_t *ntatable = nta->ntatable;
	dns_view_t *view = ntatable->view;

	if (nta->fetch != NULL) {
		dns_resolver_cancelfetch(nta->fetch);
		dns_resolver_destroyfetch(&nta->fetch);
	}
	if (dns_rdataset_isassociated(&nta->rdataset))
		dns_rdataset_disassociate(&nta->rdataset);
	if (dns_rdataset_isassociated(&nta->sigrdataset))
		dns_rdataset_disassociate(&nta->sigrdataset);

	isc_event_free(&event);

	(void)dns_resolver_createfetch(view->resolver, nta->name,
				       dns_rdatatype_nsec,
				       NULL, NULL, NULL,
				       DNS_FETCHOPT_NONTA,
				       task, fetch_done, nta,
				       &nta->rdataset,
				       &nta->sigrdataset,
				       &nta->fetch);
}

static isc_result_t
settimer(dns_ntatable_t *ntatable, dns_nta_t *nta, isc_uint32_t lifetime) {
	isc_result_t result;
	isc_interval_t interval;
	dns_view_t *view;

	REQUIRE(VALID_NTATABLE(ntatable));
	REQUIRE(VALID_NTA(nta));

	if (ntatable->timermgr == NULL)
		return (ISC_R_SUCCESS);

	view = ntatable->view;
	if (view->nta_recheck == 0 || lifetime <= view->nta_recheck)
		return (ISC_R_SUCCESS);

	isc_interval_set(&interval, view->nta_recheck, 0);
	result = isc_timer_create(ntatable->timermgr, isc_timertype_ticker,
				  NULL, &interval, ntatable->task,
				  checkbogus, nta, &nta->timer);
	return (result);
}

static isc_result_t
nta_create(dns_ntatable_t *ntatable, dns_name_t *name, dns_nta_t **target) {
	isc_result_t result;
	dns_nta_t *nta = NULL;
	dns_view_t *view;

	REQUIRE(VALID_NTATABLE(ntatable));
	REQUIRE(target != NULL && *target == NULL);

	view = ntatable->view;

	nta = isc_mem_get(view->mctx, sizeof(dns_nta_t));
	if (nta == NULL)
		return (ISC_R_NOMEMORY);

	nta->ntatable = ntatable;
	nta->expiry = 0;
	nta->timer = NULL;
	nta->fetch = NULL;
	dns_rdataset_init(&nta->rdataset);
	dns_rdataset_init(&nta->sigrdataset);

	result = isc_refcount_init(&nta->refcount, 1);
	if (result != ISC_R_SUCCESS) {
		isc_mem_put(view->mctx, nta, sizeof(dns_nta_t));
		return (result);
Evan Hunt's avatar
Evan Hunt committed
315
	}
Evan Hunt's avatar
Evan Hunt committed
316
317
318
319
320
321
322
323
324

	dns_fixedname_init(&nta->fn);
	nta->name = dns_fixedname_name(&nta->fn);
	dns_name_copy(name, nta->name, NULL);

	nta->magic = NTA_MAGIC;

	*target = nta;
	return (ISC_R_SUCCESS);
Evan Hunt's avatar
Evan Hunt committed
325
326
327
328
}

isc_result_t
dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
Evan Hunt's avatar
Evan Hunt committed
329
330
		 isc_boolean_t force, isc_stdtime_t now,
		 isc_uint32_t lifetime)
Evan Hunt's avatar
Evan Hunt committed
331
332
333
334
{
	isc_result_t result;
	dns_nta_t *nta = NULL;
	dns_rbtnode_t *node;
Evan Hunt's avatar
Evan Hunt committed
335
	dns_view_t *view;
Evan Hunt's avatar
Evan Hunt committed
336
337
338

	REQUIRE(VALID_NTATABLE(ntatable));

Evan Hunt's avatar
Evan Hunt committed
339
340
341
	view = ntatable->view;

	result = nta_create(ntatable, name, &nta);
Evan Hunt's avatar
Evan Hunt committed
342
343
344
	if (result != ISC_R_SUCCESS)
		return (result);

Evan Hunt's avatar
Evan Hunt committed
345
	nta->expiry = now + lifetime;
Evan Hunt's avatar
Evan Hunt committed
346
347
348
349
350
351

	RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);

	node = NULL;
	result = dns_rbt_addnode(ntatable->table, name, &node);
	if (result == ISC_R_SUCCESS) {
Evan Hunt's avatar
Evan Hunt committed
352
353
		if (!force)
			(void)settimer(ntatable, nta, lifetime);
Evan Hunt's avatar
Evan Hunt committed
354
355
356
357
358
		node->data = nta;
		nta = NULL;
	} else if (result == ISC_R_EXISTS) {
		dns_nta_t *n = node->data;
		if (n == NULL) {
Evan Hunt's avatar
Evan Hunt committed
359
360
			if (!force)
				(void)settimer(ntatable, nta, lifetime);
Evan Hunt's avatar
Evan Hunt committed
361
362
363
364
			node->data = nta;
			nta = NULL;
		} else {
			n->expiry = nta->expiry;
Evan Hunt's avatar
Evan Hunt committed
365
			nta_detach(view->mctx, &nta);
Evan Hunt's avatar
Evan Hunt committed
366
367
		}
		result = ISC_R_SUCCESS;
Tinderbox User's avatar
Tinderbox User committed
368
	}
Evan Hunt's avatar
Evan Hunt committed
369
370
371
372

	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);

	if (nta != NULL)
Evan Hunt's avatar
Evan Hunt committed
373
		nta_detach(view->mctx, &nta);
Evan Hunt's avatar
Evan Hunt committed
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

	return (result);
}

/*
 * Caller must hold a write lock on rwlock.
 */
static isc_result_t
delete(dns_ntatable_t *ntatable, dns_name_t *name) {
	isc_result_t result;
	dns_rbtnode_t *node = NULL;

	REQUIRE(VALID_NTATABLE(ntatable));
	REQUIRE(name != NULL);

	result = dns_rbt_findnode(ntatable->table, name, NULL, &node, NULL,
				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
	if (result == ISC_R_SUCCESS) {
		if (node->data != NULL)
			result = dns_rbt_deletenode(ntatable->table,
						    node, ISC_FALSE);
		else
			result = ISC_R_NOTFOUND;
	} else if (result == DNS_R_PARTIALMATCH)
		result = ISC_R_NOTFOUND;

	return (result);
}

isc_result_t
dns_ntatable_delete(dns_ntatable_t *ntatable, dns_name_t *name) {
	isc_result_t result;

	RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
	result = delete(ntatable, name);
	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
Tinderbox User's avatar
Tinderbox User committed
410

Evan Hunt's avatar
Evan Hunt committed
411
412
413
414
415
416
417
418
419
420
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
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
	return (result);
}

isc_boolean_t
dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
		     dns_name_t *name, dns_name_t *anchor)
{
	isc_result_t result;
	dns_fixedname_t fn;
	dns_rbtnode_t *node;
	dns_name_t *foundname;
	dns_nta_t *nta = NULL;
	isc_boolean_t answer = ISC_FALSE;
	isc_rwlocktype_t locktype = isc_rwlocktype_read;

	REQUIRE(ntatable == NULL || VALID_NTATABLE(ntatable));
	REQUIRE(dns_name_isabsolute(name));

	if (ntatable == NULL)
		return (ISC_FALSE);

	dns_fixedname_init(&fn);
	foundname = dns_fixedname_name(&fn);

 relock:
	RWLOCK(&ntatable->rwlock, locktype);
 again:
	node = NULL;
	result = dns_rbt_findnode(ntatable->table, name, foundname, &node, NULL,
				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
	if (result == DNS_R_PARTIALMATCH) {
		if (dns_name_issubdomain(foundname, anchor))
			result = ISC_R_SUCCESS;
	}
	if (result == ISC_R_SUCCESS) {
		nta = (dns_nta_t *) node->data;
		answer = ISC_TF(nta->expiry > now);
	}

	/* Deal with expired NTA */
	if (result == ISC_R_SUCCESS && !answer) {
		char nb[DNS_NAME_FORMATSIZE];

		if (locktype == isc_rwlocktype_read) {
			RWUNLOCK(&ntatable->rwlock, locktype);
			locktype = isc_rwlocktype_write;
			goto relock;
		}

		dns_name_format(foundname, nb, sizeof(nb));
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
			      DNS_LOGMODULE_NTA, ISC_LOG_INFO,
			      "deleting expired NTA at %s", nb);

Evan Hunt's avatar
Evan Hunt committed
465
466
467
468
469
470
471
		if (nta->timer != NULL) {
			(void) isc_timer_reset(nta->timer,
					       isc_timertype_inactive,
					       NULL, NULL, ISC_TRUE);
			isc_timer_detach(&nta->timer);
		}

Evan Hunt's avatar
Evan Hunt committed
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
		result = delete(ntatable, foundname);
		if (result != ISC_R_SUCCESS) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
				      DNS_LOGMODULE_NTA, ISC_LOG_INFO,
				      "deleting NTA failed: %s",
				      isc_result_totext(result));
		}
		goto again;
	}
	RWUNLOCK(&ntatable->rwlock, locktype);

	return (answer);
}

isc_result_t
Evan Hunt's avatar
Evan Hunt committed
487
dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf) {
Evan Hunt's avatar
Evan Hunt committed
488
489
490
	isc_result_t result;
	dns_rbtnode_t *node;
	dns_rbtnodechain_t chain;
Evan Hunt's avatar
Evan Hunt committed
491
492
	isc_boolean_t first = ISC_TRUE;
	isc_stdtime_t now;
Evan Hunt's avatar
Evan Hunt committed
493
494
495

	REQUIRE(VALID_NTATABLE(ntatable));

Evan Hunt's avatar
Evan Hunt committed
496
497
	isc_stdtime_get(&now);

Evan Hunt's avatar
Evan Hunt committed
498
	RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
Evan Hunt's avatar
Evan Hunt committed
499
	dns_rbtnodechain_init(&chain, ntatable->view->mctx);
Evan Hunt's avatar
Evan Hunt committed
500
501
502
503
504
505
506
507
	result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
		goto cleanup;
	for (;;) {
		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
		if (node->data != NULL) {
			dns_nta_t *n = (dns_nta_t *) node->data;
			char nbuf[DNS_NAME_FORMATSIZE], tbuf[80];
Evan Hunt's avatar
Evan Hunt committed
508
509
510
			char obuf[DNS_NAME_FORMATSIZE + 200];
			dns_fixedname_t fn;
			dns_name_t *name;
Evan Hunt's avatar
Evan Hunt committed
511
512
			isc_time_t t;

Evan Hunt's avatar
Evan Hunt committed
513
514
515
516
			dns_fixedname_init(&fn);
			name = dns_fixedname_name(&fn);
			dns_rbt_fullnamefromnode(node, name);
			dns_name_format(name, nbuf, sizeof(nbuf));
Evan Hunt's avatar
Evan Hunt committed
517
518
			isc_time_set(&t, n->expiry, 0);
			isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
Evan Hunt's avatar
Evan Hunt committed
519
520
521
522
523
524
525
526
527
528
529

			snprintf(obuf, sizeof(obuf), "%s%s: %s %s",
				 first ? "" : "\n", nbuf,
				 n->expiry < now ? "expired" : "expiry",
				 tbuf);
			first = ISC_FALSE;
			if (strlen(obuf) >= isc_buffer_availablelength(buf)) {
				result = ISC_R_NOSPACE;
				goto cleanup;
			} else
				isc_buffer_putstr(buf, obuf);
Evan Hunt's avatar
Evan Hunt committed
530
531
532
533
534
535
536
537
538
		}
		result = dns_rbtnodechain_next(&chain, NULL, NULL);
		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
			if (result == ISC_R_NOMORE)
				result = ISC_R_SUCCESS;
			break;
		}
	}

Evan Hunt's avatar
Evan Hunt committed
539
540
541
	if (isc_buffer_availablelength(buf) != 0)
		isc_buffer_putuint8(buf, 0);

Evan Hunt's avatar
Evan Hunt committed
542
543
544
545
546
547
548
   cleanup:
	dns_rbtnodechain_invalidate(&chain);
	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
	return (result);
}

isc_result_t
Evan Hunt's avatar
Evan Hunt committed
549
dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
Evan Hunt's avatar
Evan Hunt committed
550
	isc_result_t result;
Evan Hunt's avatar
Evan Hunt committed
551
552
553
	dns_rbtnode_t *node;
	dns_rbtnodechain_t chain;
	isc_stdtime_t now;
Evan Hunt's avatar
Evan Hunt committed
554

Evan Hunt's avatar
Evan Hunt committed
555
	REQUIRE(VALID_NTATABLE(ntatable));
Evan Hunt's avatar
Evan Hunt committed
556

Evan Hunt's avatar
Evan Hunt committed
557
	isc_stdtime_get(&now);
Evan Hunt's avatar
Evan Hunt committed
558

Evan Hunt's avatar
Evan Hunt committed
559
560
561
562
563
564
565
566
567
568
569
570
571
	RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
	dns_rbtnodechain_init(&chain, ntatable->view->mctx);
	result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
		goto cleanup;
	for (;;) {
		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
		if (node->data != NULL) {
			dns_nta_t *n = (dns_nta_t *) node->data;
			char nbuf[DNS_NAME_FORMATSIZE], tbuf[80];
			dns_fixedname_t fn;
			dns_name_t *name;
			isc_time_t t;
Evan Hunt's avatar
Evan Hunt committed
572

Evan Hunt's avatar
Evan Hunt committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
			dns_fixedname_init(&fn);
			name = dns_fixedname_name(&fn);
			dns_rbt_fullnamefromnode(node, name);
			dns_name_format(name, nbuf, sizeof(nbuf));
			isc_time_set(&t, n->expiry, 0);
			isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
			fprintf(fp, "%s: %s %s\n", nbuf,
				n->expiry < now ? "expired" : "expiry",
				tbuf);
		}
		result = dns_rbtnodechain_next(&chain, NULL, NULL);
		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
			if (result == ISC_R_NOMORE)
				result = ISC_R_SUCCESS;
			break;
		}
Evan Hunt's avatar
Evan Hunt committed
589
590
	}

Evan Hunt's avatar
Evan Hunt committed
591
592
593
594
   cleanup:
	dns_rbtnodechain_invalidate(&chain);
	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
	return (result);
Evan Hunt's avatar
Evan Hunt committed
595
}