Home | History | Annotate | Line # | Download | only in dns
keytable.c revision 1.3
      1 /*	$NetBSD: keytable.c,v 1.3 2019/01/09 16:55:11 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * This Source Code Form is subject to the terms of the Mozilla Public
      7  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9  *
     10  * See the COPYRIGHT file distributed with this work for additional
     11  * information regarding copyright ownership.
     12  */
     13 
     14 
     15 /*! \file */
     16 
     17 #include <config.h>
     18 
     19 #include <stdbool.h>
     20 
     21 #include <isc/mem.h>
     22 #include <isc/print.h>
     23 #include <isc/refcount.h>
     24 #include <isc/rwlock.h>
     25 #include <isc/string.h>		/* Required for HP/UX (and others?) */
     26 #include <isc/util.h>
     27 
     28 #include <dns/keytable.h>
     29 #include <dns/fixedname.h>
     30 #include <dns/rbt.h>
     31 #include <dns/result.h>
     32 
     33 #define KEYTABLE_MAGIC                  ISC_MAGIC('K', 'T', 'b', 'l')
     34 #define VALID_KEYTABLE(kt)              ISC_MAGIC_VALID(kt, KEYTABLE_MAGIC)
     35 
     36 #define KEYNODE_MAGIC                   ISC_MAGIC('K', 'N', 'o', 'd')
     37 #define VALID_KEYNODE(kn)               ISC_MAGIC_VALID(kn, KEYNODE_MAGIC)
     38 
     39 struct dns_keytable {
     40 	/* Unlocked. */
     41 	unsigned int            magic;
     42 	isc_mem_t               *mctx;
     43 	isc_refcount_t          active_nodes;
     44 	isc_refcount_t          references;
     45 	isc_rwlock_t            rwlock;
     46 	/* Locked by rwlock. */
     47 	dns_rbt_t               *table;
     48 };
     49 
     50 struct dns_keynode {
     51 	unsigned int            magic;
     52 	isc_refcount_t          refcount;
     53 	dst_key_t *             key;
     54 	bool           managed;
     55 	bool		initial;
     56 	struct dns_keynode *    next;
     57 };
     58 
     59 static void
     60 free_keynode(void *node, void *arg) {
     61 	dns_keynode_t *keynode = node;
     62 	isc_mem_t *mctx = arg;
     63 
     64 	dns_keynode_detachall(mctx, &keynode);
     65 }
     66 
     67 isc_result_t
     68 dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) {
     69 	dns_keytable_t *keytable;
     70 	isc_result_t result;
     71 
     72 	/*
     73 	 * Create a keytable.
     74 	 */
     75 
     76 	REQUIRE(keytablep != NULL && *keytablep == NULL);
     77 
     78 	keytable = isc_mem_get(mctx, sizeof(*keytable));
     79 	if (keytable == NULL) {
     80 		return (ISC_R_NOMEMORY);
     81 	}
     82 
     83 	keytable->table = NULL;
     84 	result = dns_rbt_create(mctx, free_keynode, mctx, &keytable->table);
     85 	if (result != ISC_R_SUCCESS) {
     86 		goto cleanup_keytable;
     87 	}
     88 
     89 	result = isc_rwlock_init(&keytable->rwlock, 0, 0);
     90 	if (result != ISC_R_SUCCESS) {
     91 		goto cleanup_rbt;
     92 	}
     93 
     94 	isc_refcount_init(&keytable->active_nodes, 0);
     95 	isc_refcount_init(&keytable->references, 1);
     96 
     97 	keytable->mctx = NULL;
     98 	isc_mem_attach(mctx, &keytable->mctx);
     99 	keytable->magic = KEYTABLE_MAGIC;
    100 	*keytablep = keytable;
    101 
    102 	return (ISC_R_SUCCESS);
    103 
    104  cleanup_rbt:
    105 	dns_rbt_destroy(&keytable->table);
    106 
    107  cleanup_keytable:
    108 	isc_mem_putanddetach(&mctx, keytable, sizeof(*keytable));
    109 
    110 	return (result);
    111 }
    112 
    113 void
    114 dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp) {
    115 
    116 	/*
    117 	 * Attach *targetp to source.
    118 	 */
    119 
    120 	REQUIRE(VALID_KEYTABLE(source));
    121 	REQUIRE(targetp != NULL && *targetp == NULL);
    122 
    123 	isc_refcount_increment(&source->references);
    124 
    125 	*targetp = source;
    126 }
    127 
    128 void
    129 dns_keytable_detach(dns_keytable_t **keytablep) {
    130 	REQUIRE(keytablep != NULL && VALID_KEYTABLE(*keytablep));
    131 	dns_keytable_t *keytable = *keytablep;
    132 	*keytablep = NULL;
    133 
    134 	if (isc_refcount_decrement(&keytable->references) == 1) {
    135 		isc_refcount_destroy(&keytable->references);
    136 		isc_refcount_destroy(&keytable->active_nodes);
    137 		dns_rbt_destroy(&keytable->table);
    138 		isc_rwlock_destroy(&keytable->rwlock);
    139 		keytable->magic = 0;
    140 		isc_mem_putanddetach(&keytable->mctx,
    141 				     keytable, sizeof(*keytable));
    142 	}
    143 }
    144 
    145 /*%
    146  * Search "node" for either a null key node or a key node for the exact same
    147  * key as the one supplied in "keyp" and, if found, update it accordingly.
    148  */
    149 static isc_result_t
    150 update_keynode(dst_key_t **keyp, dns_rbtnode_t *node, bool initial) {
    151 	dns_keynode_t *knode;
    152 
    153 	REQUIRE(keyp != NULL && *keyp != NULL);
    154 	REQUIRE(node != NULL);
    155 
    156 	for (knode = node->data; knode != NULL; knode = knode->next) {
    157 		if (knode->key == NULL) {
    158 			/*
    159 			 * Null key node found.  Attach the supplied key to it,
    160 			 * making it a non-null key node and transferring key
    161 			 * ownership to the keytable.
    162 			 */
    163 			knode->key = *keyp;
    164 			*keyp = NULL;
    165 			return (ISC_R_SUCCESS);
    166 		} else if (dst_key_compare(knode->key, *keyp)) {
    167 			/*
    168 			 * Key node found for the supplied key.  Free the
    169 			 * supplied copy of the key and update the found key
    170 			 * node's flags if necessary.
    171 			 */
    172 			dst_key_free(keyp);
    173 			if (!initial) {
    174 				dns_keynode_trust(knode);
    175 			}
    176 			return (ISC_R_SUCCESS);
    177 		}
    178 	}
    179 
    180 	return (ISC_R_NOTFOUND);
    181 }
    182 
    183 /*%
    184  * Create a key node for "keyp" (or a null key node if "keyp" is NULL), set
    185  * "managed" and "initial" as requested and make the created key node the first
    186  * one attached to "node" in "keytable".
    187  */
    188 static isc_result_t
    189 prepend_keynode(dst_key_t **keyp, dns_rbtnode_t *node,
    190 		dns_keytable_t *keytable, bool managed,
    191 		bool initial)
    192 {
    193 	dns_keynode_t *knode = NULL;
    194 	isc_result_t result;
    195 
    196 	REQUIRE(keyp == NULL || *keyp != NULL);
    197 	REQUIRE(VALID_KEYTABLE(keytable));
    198 	REQUIRE(!initial || managed);
    199 
    200 	result = dns_keynode_create(keytable->mctx, &knode);
    201 	if (result != ISC_R_SUCCESS) {
    202 		return (result);
    203 	}
    204 
    205 	/*
    206 	 * If a key was supplied, transfer its ownership to the keytable.
    207 	 */
    208 	if (keyp) {
    209 		knode->key = *keyp;
    210 		*keyp = NULL;
    211 	}
    212 
    213 	knode->managed = managed;
    214 	knode->initial = initial;
    215 	knode->next = node->data;
    216 	node->data = knode;
    217 
    218 	return (ISC_R_SUCCESS);
    219 }
    220 
    221 /*%
    222  * Add key "keyp" at "keyname" in "keytable".  If the key already exists at the
    223  * requested name, update its flags.  If "keyp" is NULL, add a null key to
    224  * indicate that "keyname" should be treated as a secure domain without
    225  * supplying key data which would allow the domain to be validated.
    226  */
    227 static isc_result_t
    228 insert(dns_keytable_t *keytable, bool managed, bool initial,
    229        const dns_name_t *keyname, dst_key_t **keyp)
    230 {
    231 	dns_rbtnode_t *node = NULL;
    232 	isc_result_t result;
    233 
    234 	REQUIRE(VALID_KEYTABLE(keytable));
    235 
    236 	RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
    237 
    238 	result = dns_rbt_addnode(keytable->table, keyname, &node);
    239 	if (result == ISC_R_SUCCESS) {
    240 		/*
    241 		 * There was no node for "keyname" in "keytable" yet, so one
    242 		 * was created.  Create a new key node for the supplied key (or
    243 		 * a null key node if "keyp" is NULL) and attach it to the
    244 		 * created node.
    245 		 */
    246 		result = prepend_keynode(keyp, node, keytable, managed,
    247 					 initial);
    248 	} else if (result == ISC_R_EXISTS) {
    249 		/*
    250 		 * A node already exists for "keyname" in "keytable".
    251 		 */
    252 		if (keyp == NULL) {
    253 			/*
    254 			 * We were told to add a null key at "keyname", which
    255 			 * means there is nothing left to do as there is either
    256 			 * a null key at this node already or there is a
    257 			 * non-null key node which would not be affected.
    258 			 * Reset result to reflect the fact that the node for
    259 			 * "keyname" is already marked as secure.
    260 			 */
    261 			result = ISC_R_SUCCESS;
    262 		} else {
    263 			/*
    264 			 * We were told to add the key supplied in "keyp" at
    265 			 * "keyname".  Try to find an already existing key node
    266 			 * we could reuse for the supplied key (i.e. a null key
    267 			 * node or a key node for the exact same key) and, if
    268 			 * found, update it accordingly.
    269 			 */
    270 			result = update_keynode(keyp, node, initial);
    271 			if (result == ISC_R_NOTFOUND) {
    272 				/*
    273 				 * The node for "keyname" only contains key
    274 				 * nodes for keys different than the supplied
    275 				 * one.  Create a new key node for the supplied
    276 				 * key and prepend it before the others.
    277 				 */
    278 				result = prepend_keynode(keyp, node, keytable,
    279 							 managed, initial);
    280 			}
    281 		}
    282 	}
    283 
    284 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
    285 
    286 	return (result);
    287 }
    288 
    289 isc_result_t
    290 dns_keytable_add(dns_keytable_t *keytable, bool managed,
    291 		 bool initial, dst_key_t **keyp)
    292 {
    293 	REQUIRE(keyp != NULL && *keyp != NULL);
    294 	REQUIRE(!initial || managed);
    295 	return (insert(keytable, managed, initial, dst_key_name(*keyp), keyp));
    296 }
    297 
    298 isc_result_t
    299 dns_keytable_marksecure(dns_keytable_t *keytable, const dns_name_t *name) {
    300 	return (insert(keytable, true, false, name, NULL));
    301 }
    302 
    303 isc_result_t
    304 dns_keytable_delete(dns_keytable_t *keytable, const dns_name_t *keyname) {
    305 	isc_result_t result;
    306 	dns_rbtnode_t *node = NULL;
    307 
    308 	REQUIRE(VALID_KEYTABLE(keytable));
    309 	REQUIRE(keyname != NULL);
    310 
    311 	RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
    312 	result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
    313 				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
    314 	if (result == ISC_R_SUCCESS) {
    315 		if (node->data != NULL)
    316 			result = dns_rbt_deletenode(keytable->table,
    317 						    node, false);
    318 		else
    319 			result = ISC_R_NOTFOUND;
    320 	} else if (result == DNS_R_PARTIALMATCH)
    321 		result = ISC_R_NOTFOUND;
    322 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
    323 
    324 	return (result);
    325 }
    326 
    327 isc_result_t
    328 dns_keytable_deletekeynode(dns_keytable_t *keytable, dst_key_t *dstkey) {
    329 	isc_result_t result;
    330 	dns_name_t *keyname;
    331 	dns_rbtnode_t *node = NULL;
    332 	dns_keynode_t *knode = NULL, **kprev = NULL;
    333 
    334 	REQUIRE(VALID_KEYTABLE(keytable));
    335 	REQUIRE(dstkey != NULL);
    336 
    337 	keyname = dst_key_name(dstkey);
    338 
    339 	RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
    340 	result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
    341 				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
    342 
    343 	if (result == DNS_R_PARTIALMATCH)
    344 		result = ISC_R_NOTFOUND;
    345 	if (result != ISC_R_SUCCESS)
    346 		goto finish;
    347 
    348 	if (node->data == NULL) {
    349 		result = ISC_R_NOTFOUND;
    350 		goto finish;
    351 	}
    352 
    353 	knode = node->data;
    354 	if (knode->next == NULL && knode->key != NULL &&
    355 	    dst_key_compare(knode->key, dstkey) == true)
    356 	{
    357 		result = dns_rbt_deletenode(keytable->table, node, false);
    358 		goto finish;
    359 	}
    360 
    361 	kprev = (dns_keynode_t **)(void *)&node->data;
    362 	while (knode != NULL) {
    363 		if (knode->key != NULL &&
    364 		    dst_key_compare(knode->key, dstkey) == true)
    365 			break;
    366 		kprev = &knode->next;
    367 		knode = knode->next;
    368 	}
    369 
    370 	if (knode != NULL) {
    371 		if (knode->key != NULL)
    372 			dst_key_free(&knode->key);
    373 		/*
    374 		 * This is equivalent to:
    375 		 * dns_keynode_attach(knode->next, &tmp);
    376 		 * dns_keynode_detach(kprev);
    377 		 * dns_keynode_attach(tmp, &kprev);
    378 		 * dns_keynode_detach(&tmp);
    379 		 */
    380 		*kprev = knode->next;
    381 		knode->next = NULL;
    382 		dns_keynode_detach(keytable->mctx, &knode);
    383 	} else
    384 		result = DNS_R_PARTIALMATCH;
    385   finish:
    386 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
    387 	return (result);
    388 }
    389 
    390 isc_result_t
    391 dns_keytable_find(dns_keytable_t *keytable, const dns_name_t *keyname,
    392 		  dns_keynode_t **keynodep)
    393 {
    394 	isc_result_t result;
    395 	dns_rbtnode_t *node = NULL;
    396 
    397 	REQUIRE(VALID_KEYTABLE(keytable));
    398 	REQUIRE(keyname != NULL);
    399 	REQUIRE(keynodep != NULL && *keynodep == NULL);
    400 
    401 	RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
    402 	result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL,
    403 				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
    404 	if (result == ISC_R_SUCCESS) {
    405 		if (node->data != NULL) {
    406 			isc_refcount_increment0(&keytable->active_nodes);
    407 			dns_keynode_attach(node->data, keynodep);
    408 		} else
    409 			result = ISC_R_NOTFOUND;
    410 	} else if (result == DNS_R_PARTIALMATCH)
    411 		result = ISC_R_NOTFOUND;
    412 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
    413 
    414 	return (result);
    415 }
    416 
    417 isc_result_t
    418 dns_keytable_nextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
    419 			 dns_keynode_t **nextnodep)
    420 {
    421 	/*
    422 	 * Return the next key after 'keynode', regardless of
    423 	 * properties.
    424 	 */
    425 
    426 	REQUIRE(VALID_KEYTABLE(keytable));
    427 	REQUIRE(VALID_KEYNODE(keynode));
    428 	REQUIRE(nextnodep != NULL && *nextnodep == NULL);
    429 
    430 	if (keynode->next == NULL)
    431 		return (ISC_R_NOTFOUND);
    432 
    433 	dns_keynode_attach(keynode->next, nextnodep);
    434 	isc_refcount_increment(&keytable->active_nodes);
    435 
    436 	return (ISC_R_SUCCESS);
    437 }
    438 
    439 isc_result_t
    440 dns_keytable_findkeynode(dns_keytable_t *keytable, const dns_name_t *name,
    441 			 dns_secalg_t algorithm, dns_keytag_t tag,
    442 			 dns_keynode_t **keynodep)
    443 {
    444 	isc_result_t result;
    445 	dns_keynode_t *knode;
    446 	void *data;
    447 
    448 	/*
    449 	 * Search for a key named 'name', matching 'algorithm' and 'tag' in
    450 	 * 'keytable'.
    451 	 */
    452 
    453 	REQUIRE(VALID_KEYTABLE(keytable));
    454 	REQUIRE(dns_name_isabsolute(name));
    455 	REQUIRE(keynodep != NULL && *keynodep == NULL);
    456 
    457 	RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
    458 
    459 	/*
    460 	 * Note we don't want the DNS_R_PARTIALMATCH from dns_rbt_findname()
    461 	 * as that indicates that 'name' was not found.
    462 	 *
    463 	 * DNS_R_PARTIALMATCH indicates that the name was found but we
    464 	 * didn't get a match on algorithm and key id arguments.
    465 	 */
    466 	knode = NULL;
    467 	data = NULL;
    468 	result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
    469 
    470 	if (result == ISC_R_SUCCESS) {
    471 		INSIST(data != NULL);
    472 		for (knode = data; knode != NULL; knode = knode->next) {
    473 			if (knode->key == NULL) {
    474 				knode = NULL;
    475 				break;
    476 			}
    477 			if (algorithm == dst_key_alg(knode->key)
    478 			    && tag == dst_key_id(knode->key))
    479 				break;
    480 		}
    481 		if (knode != NULL) {
    482 			isc_refcount_increment0(&keytable->active_nodes);
    483 			dns_keynode_attach(knode, keynodep);
    484 		} else
    485 			result = DNS_R_PARTIALMATCH;
    486 	} else if (result == DNS_R_PARTIALMATCH)
    487 		result = ISC_R_NOTFOUND;
    488 
    489 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
    490 
    491 	return (result);
    492 }
    493 
    494 isc_result_t
    495 dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
    496 			     dns_keynode_t **nextnodep)
    497 {
    498 	isc_result_t result;
    499 	dns_keynode_t *knode;
    500 
    501 	/*
    502 	 * Search for the next key with the same properties as 'keynode' in
    503 	 * 'keytable'.
    504 	 */
    505 
    506 	REQUIRE(VALID_KEYTABLE(keytable));
    507 	REQUIRE(VALID_KEYNODE(keynode));
    508 	REQUIRE(nextnodep != NULL && *nextnodep == NULL);
    509 
    510 	for (knode = keynode->next; knode != NULL; knode = knode->next) {
    511 		if (knode->key == NULL) {
    512 			knode = NULL;
    513 			break;
    514 		}
    515 		if (dst_key_alg(keynode->key) == dst_key_alg(knode->key) &&
    516 		    dst_key_id(keynode->key) == dst_key_id(knode->key))
    517 			break;
    518 	}
    519 	if (knode != NULL) {
    520 		isc_refcount_increment(&keytable->active_nodes);
    521 		result = ISC_R_SUCCESS;
    522 		dns_keynode_attach(knode, nextnodep);
    523 	} else
    524 		result = ISC_R_NOTFOUND;
    525 
    526 	return (result);
    527 }
    528 
    529 isc_result_t
    530 dns_keytable_finddeepestmatch(dns_keytable_t *keytable, const dns_name_t *name,
    531 			      dns_name_t *foundname)
    532 {
    533 	isc_result_t result;
    534 	void *data;
    535 
    536 	/*
    537 	 * Search for the deepest match in 'keytable'.
    538 	 */
    539 
    540 	REQUIRE(VALID_KEYTABLE(keytable));
    541 	REQUIRE(dns_name_isabsolute(name));
    542 	REQUIRE(foundname != NULL);
    543 
    544 	RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
    545 
    546 	data = NULL;
    547 	result = dns_rbt_findname(keytable->table, name, 0, foundname, &data);
    548 
    549 	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
    550 		result = ISC_R_SUCCESS;
    551 
    552 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
    553 
    554 	return (result);
    555 }
    556 
    557 void
    558 dns_keytable_attachkeynode(dns_keytable_t *keytable, dns_keynode_t *source,
    559 			   dns_keynode_t **target)
    560 {
    561 	/*
    562 	 * Give back a keynode found via dns_keytable_findkeynode().
    563 	 */
    564 
    565 	REQUIRE(VALID_KEYTABLE(keytable));
    566 	REQUIRE(VALID_KEYNODE(source));
    567 	REQUIRE(target != NULL && *target == NULL);
    568 
    569 	isc_refcount_increment(&keytable->active_nodes);
    570 
    571 	dns_keynode_attach(source, target);
    572 }
    573 
    574 void
    575 dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep)
    576 {
    577 	/*
    578 	 * Give back a keynode found via dns_keytable_findkeynode().
    579 	 */
    580 
    581 	REQUIRE(VALID_KEYTABLE(keytable));
    582 	REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep));
    583 
    584 	INSIST(isc_refcount_decrement(&keytable->active_nodes) > 0);
    585 	dns_keynode_detach(keytable->mctx, keynodep);
    586 }
    587 
    588 isc_result_t
    589 dns_keytable_issecuredomain(dns_keytable_t *keytable, const dns_name_t *name,
    590 			    dns_name_t *foundname, bool *wantdnssecp)
    591 {
    592 	isc_result_t result;
    593 	dns_rbtnode_t *node = NULL;
    594 
    595 	/*
    596 	 * Is 'name' at or beneath a trusted key?
    597 	 */
    598 
    599 	REQUIRE(VALID_KEYTABLE(keytable));
    600 	REQUIRE(dns_name_isabsolute(name));
    601 	REQUIRE(wantdnssecp != NULL);
    602 
    603 	RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
    604 
    605 	result = dns_rbt_findnode(keytable->table, name, foundname, &node,
    606 				  NULL, DNS_RBTFIND_NOOPTIONS, NULL, NULL);
    607 	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
    608 		INSIST(node->data != NULL);
    609 		*wantdnssecp = true;
    610 		result = ISC_R_SUCCESS;
    611 	} else if (result == ISC_R_NOTFOUND) {
    612 		*wantdnssecp = false;
    613 		result = ISC_R_SUCCESS;
    614 	}
    615 
    616 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
    617 
    618 	return (result);
    619 }
    620 
    621 static isc_result_t
    622 putstr(isc_buffer_t **b, const char *str) {
    623 	isc_result_t result;
    624 
    625 	result = isc_buffer_reserve(b, strlen(str));
    626 	if (result != ISC_R_SUCCESS)
    627 		return (result);
    628 
    629 	isc_buffer_putstr(*b, str);
    630 	return (ISC_R_SUCCESS);
    631 }
    632 
    633 isc_result_t
    634 dns_keytable_dump(dns_keytable_t *keytable, FILE *fp) {
    635 	isc_result_t result;
    636 	isc_buffer_t *text = NULL;
    637 
    638 	REQUIRE(VALID_KEYTABLE(keytable));
    639 	REQUIRE(fp != NULL);
    640 
    641 	result = isc_buffer_allocate(keytable->mctx, &text, 4096);
    642 	if (result != ISC_R_SUCCESS)
    643 		return (result);
    644 
    645 	result = dns_keytable_totext(keytable, &text);
    646 
    647 	if (isc_buffer_usedlength(text) != 0) {
    648 		(void) putstr(&text, "\n");
    649 	} else if (result == ISC_R_SUCCESS)
    650 		(void) putstr(&text, "none");
    651 	else {
    652 		(void) putstr(&text, "could not dump key table: ");
    653 		(void) putstr(&text, isc_result_totext(result));
    654 	}
    655 
    656 	fprintf(fp, "%.*s", (int) isc_buffer_usedlength(text),
    657 		(char *) isc_buffer_base(text));
    658 
    659 	isc_buffer_free(&text);
    660 	return (result);
    661 }
    662 
    663 isc_result_t
    664 dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) {
    665 	isc_result_t result;
    666 	dns_keynode_t *knode;
    667 	dns_rbtnode_t *node;
    668 	dns_rbtnodechain_t chain;
    669 
    670 	REQUIRE(VALID_KEYTABLE(keytable));
    671 	REQUIRE(text != NULL && *text != NULL);
    672 
    673 	RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
    674 	dns_rbtnodechain_init(&chain, keytable->mctx);
    675 	result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
    676 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
    677 		if (result == ISC_R_NOTFOUND)
    678 			result = ISC_R_SUCCESS;
    679 		goto cleanup;
    680 	}
    681 	for (;;) {
    682 		char pbuf[DST_KEY_FORMATSIZE];
    683 
    684 		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
    685 		for (knode = node->data; knode != NULL; knode = knode->next) {
    686 			char obuf[DNS_NAME_FORMATSIZE + 200];
    687 			if (knode->key == NULL)
    688 				continue;
    689 			dst_key_format(knode->key, pbuf, sizeof(pbuf));
    690 			snprintf(obuf, sizeof(obuf), "%s ; %s%s\n", pbuf,
    691 				 knode->initial ? "initializing " : "",
    692 				 knode->managed ? "managed" : "trusted");
    693 			result = putstr(text, obuf);
    694 			if (result != ISC_R_SUCCESS)
    695 				break;
    696 		}
    697 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
    698 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
    699 			if (result == ISC_R_NOMORE)
    700 				result = ISC_R_SUCCESS;
    701 			break;
    702 		}
    703 	}
    704 
    705    cleanup:
    706 	dns_rbtnodechain_invalidate(&chain);
    707 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
    708 	return (result);
    709 }
    710 
    711 isc_result_t
    712 dns_keytable_forall(dns_keytable_t *keytable,
    713 		    void (*func)(dns_keytable_t *, dns_keynode_t *, void *),
    714 		    void *arg)
    715 {
    716 	isc_result_t result;
    717 	dns_rbtnode_t *node;
    718 	dns_rbtnodechain_t chain;
    719 
    720 	REQUIRE(VALID_KEYTABLE(keytable));
    721 
    722 	RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
    723 	dns_rbtnodechain_init(&chain, keytable->mctx);
    724 	result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
    725 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
    726 		if (result == ISC_R_NOTFOUND)
    727 			result = ISC_R_SUCCESS;
    728 		goto cleanup;
    729 	}
    730 	isc_refcount_increment0(&keytable->active_nodes);
    731 	for (;;) {
    732 		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
    733 		if (node->data != NULL)
    734 			(*func)(keytable, node->data, arg);
    735 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
    736 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
    737 			if (result == ISC_R_NOMORE)
    738 				result = ISC_R_SUCCESS;
    739 			break;
    740 		}
    741 	}
    742 	INSIST(isc_refcount_decrement(&keytable->active_nodes) > 0);
    743 
    744    cleanup:
    745 	dns_rbtnodechain_invalidate(&chain);
    746 	RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
    747 	return (result);
    748 }
    749 
    750 dst_key_t *
    751 dns_keynode_key(dns_keynode_t *keynode) {
    752 	REQUIRE(VALID_KEYNODE(keynode));
    753 
    754 	return (keynode->key);
    755 }
    756 
    757 bool
    758 dns_keynode_managed(dns_keynode_t *keynode) {
    759 	REQUIRE(VALID_KEYNODE(keynode));
    760 
    761 	return (keynode->managed);
    762 }
    763 
    764 bool
    765 dns_keynode_initial(dns_keynode_t *keynode) {
    766 	REQUIRE(VALID_KEYNODE(keynode));
    767 
    768 	return (keynode->initial);
    769 }
    770 
    771 void
    772 dns_keynode_trust(dns_keynode_t *keynode) {
    773 	REQUIRE(VALID_KEYNODE(keynode));
    774 
    775 	keynode->initial = false;
    776 }
    777 
    778 isc_result_t
    779 dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) {
    780 	dns_keynode_t *knode;
    781 
    782 	REQUIRE(target != NULL && *target == NULL);
    783 
    784 	knode = isc_mem_get(mctx, sizeof(dns_keynode_t));
    785 	if (knode == NULL)
    786 		return (ISC_R_NOMEMORY);
    787 
    788 	knode->magic = KEYNODE_MAGIC;
    789 	knode->managed = false;
    790 	knode->initial = false;
    791 	knode->key = NULL;
    792 	knode->next = NULL;
    793 
    794 	isc_refcount_init(&knode->refcount, 1);
    795 
    796 	*target = knode;
    797 	return (ISC_R_SUCCESS);
    798 }
    799 
    800 void
    801 dns_keynode_attach(dns_keynode_t *source, dns_keynode_t **target) {
    802 	REQUIRE(VALID_KEYNODE(source));
    803 	isc_refcount_increment(&source->refcount);
    804 	*target = source;
    805 }
    806 
    807 void
    808 dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynode) {
    809 	REQUIRE(keynode != NULL && VALID_KEYNODE(*keynode));
    810 	dns_keynode_t *node = *keynode;
    811 	*keynode = NULL;
    812 
    813 	if (isc_refcount_decrement(&node->refcount) == 1) {
    814 		isc_refcount_destroy(&node->refcount);
    815 		if (node->key != NULL) {
    816 			dst_key_free(&node->key);
    817 		}
    818 		isc_mem_put(mctx, node, sizeof(dns_keynode_t));
    819 	}
    820 }
    821 
    822 void
    823 dns_keynode_detachall(isc_mem_t *mctx, dns_keynode_t **keynode) {
    824 	dns_keynode_t *next = NULL, *node = *keynode;
    825 	REQUIRE(VALID_KEYNODE(node));
    826 	while (node != NULL) {
    827 		next = node->next;
    828 		dns_keynode_detach(mctx, &node);
    829 		node = next;
    830 	}
    831 	*keynode = NULL;
    832 }
    833