db.c revision 170222
1/*
2 * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2001, 2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and 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: db.c,v 1.74.18.6 2005/10/13 02:12:24 marka Exp $ */
19
20/*! \file */
21
22/***
23 *** Imports
24 ***/
25
26#include <config.h>
27
28#include <isc/buffer.h>
29#include <isc/mem.h>
30#include <isc/once.h>
31#include <isc/rwlock.h>
32#include <isc/string.h>
33#include <isc/util.h>
34
35#include <dns/callbacks.h>
36#include <dns/db.h>
37#include <dns/log.h>
38#include <dns/master.h>
39#include <dns/rdata.h>
40#include <dns/rdataset.h>
41#include <dns/result.h>
42
43/***
44 *** Private Types
45 ***/
46
47struct dns_dbimplementation {
48	const char *				name;
49	dns_dbcreatefunc_t			create;
50	isc_mem_t *				mctx;
51	void *					driverarg;
52	ISC_LINK(dns_dbimplementation_t)	link;
53};
54
55/***
56 *** Supported DB Implementations Registry
57 ***/
58
59/*
60 * Built in database implementations are registered here.
61 */
62
63#include "rbtdb.h"
64#include "rbtdb64.h"
65
66static ISC_LIST(dns_dbimplementation_t) implementations;
67static isc_rwlock_t implock;
68static isc_once_t once = ISC_ONCE_INIT;
69
70static dns_dbimplementation_t rbtimp;
71static dns_dbimplementation_t rbt64imp;
72
73static void
74initialize(void) {
75	RUNTIME_CHECK(isc_rwlock_init(&implock, 0, 0) == ISC_R_SUCCESS);
76
77	rbtimp.name = "rbt";
78	rbtimp.create = dns_rbtdb_create;
79	rbtimp.mctx = NULL;
80	rbtimp.driverarg = NULL;
81	ISC_LINK_INIT(&rbtimp, link);
82
83	rbt64imp.name = "rbt64";
84	rbt64imp.create = dns_rbtdb64_create;
85	rbt64imp.mctx = NULL;
86	rbt64imp.driverarg = NULL;
87	ISC_LINK_INIT(&rbt64imp, link);
88
89	ISC_LIST_INIT(implementations);
90	ISC_LIST_APPEND(implementations, &rbtimp, link);
91	ISC_LIST_APPEND(implementations, &rbt64imp, link);
92}
93
94static inline dns_dbimplementation_t *
95impfind(const char *name) {
96	dns_dbimplementation_t *imp;
97
98	for (imp = ISC_LIST_HEAD(implementations);
99	     imp != NULL;
100	     imp = ISC_LIST_NEXT(imp, link))
101		if (strcasecmp(name, imp->name) == 0)
102			return (imp);
103	return (NULL);
104}
105
106
107/***
108 *** Basic DB Methods
109 ***/
110
111isc_result_t
112dns_db_create(isc_mem_t *mctx, const char *db_type, dns_name_t *origin,
113	      dns_dbtype_t type, dns_rdataclass_t rdclass,
114	      unsigned int argc, char *argv[], dns_db_t **dbp)
115{
116	dns_dbimplementation_t *impinfo;
117
118	RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
119
120	/*
121	 * Create a new database using implementation 'db_type'.
122	 */
123
124	REQUIRE(dbp != NULL && *dbp == NULL);
125	REQUIRE(dns_name_isabsolute(origin));
126
127	RWLOCK(&implock, isc_rwlocktype_read);
128	impinfo = impfind(db_type);
129	if (impinfo != NULL) {
130		isc_result_t result;
131		result = ((impinfo->create)(mctx, origin, type,
132					    rdclass, argc, argv,
133					    impinfo->driverarg, dbp));
134		RWUNLOCK(&implock, isc_rwlocktype_read);
135		return (result);
136	}
137
138	RWUNLOCK(&implock, isc_rwlocktype_read);
139
140	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
141		      DNS_LOGMODULE_DB, ISC_LOG_ERROR,
142		      "unsupported database type '%s'", db_type);
143
144	return (ISC_R_NOTFOUND);
145}
146
147void
148dns_db_attach(dns_db_t *source, dns_db_t **targetp) {
149
150	/*
151	 * Attach *targetp to source.
152	 */
153
154	REQUIRE(DNS_DB_VALID(source));
155	REQUIRE(targetp != NULL && *targetp == NULL);
156
157	(source->methods->attach)(source, targetp);
158
159	ENSURE(*targetp == source);
160}
161
162void
163dns_db_detach(dns_db_t **dbp) {
164
165	/*
166	 * Detach *dbp from its database.
167	 */
168
169	REQUIRE(dbp != NULL);
170	REQUIRE(DNS_DB_VALID(*dbp));
171
172	((*dbp)->methods->detach)(dbp);
173
174	ENSURE(*dbp == NULL);
175}
176
177isc_result_t
178dns_db_ondestroy(dns_db_t *db, isc_task_t *task, isc_event_t **eventp)
179{
180	REQUIRE(DNS_DB_VALID(db));
181
182	return (isc_ondestroy_register(&db->ondest, task, eventp));
183}
184
185
186isc_boolean_t
187dns_db_iscache(dns_db_t *db) {
188
189	/*
190	 * Does 'db' have cache semantics?
191	 */
192
193	REQUIRE(DNS_DB_VALID(db));
194
195	if ((db->attributes & DNS_DBATTR_CACHE) != 0)
196		return (ISC_TRUE);
197
198	return (ISC_FALSE);
199}
200
201isc_boolean_t
202dns_db_iszone(dns_db_t *db) {
203
204	/*
205	 * Does 'db' have zone semantics?
206	 */
207
208	REQUIRE(DNS_DB_VALID(db));
209
210	if ((db->attributes & (DNS_DBATTR_CACHE|DNS_DBATTR_STUB)) == 0)
211		return (ISC_TRUE);
212
213	return (ISC_FALSE);
214}
215
216isc_boolean_t
217dns_db_isstub(dns_db_t *db) {
218
219	/*
220	 * Does 'db' have stub semantics?
221	 */
222
223	REQUIRE(DNS_DB_VALID(db));
224
225	if ((db->attributes & DNS_DBATTR_STUB) != 0)
226		return (ISC_TRUE);
227
228	return (ISC_FALSE);
229}
230
231isc_boolean_t
232dns_db_issecure(dns_db_t *db) {
233
234	/*
235	 * Is 'db' secure?
236	 */
237
238	REQUIRE(DNS_DB_VALID(db));
239	REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
240
241	return ((db->methods->issecure)(db));
242}
243
244isc_boolean_t
245dns_db_ispersistent(dns_db_t *db) {
246
247	/*
248	 * Is 'db' persistent?
249	 */
250
251	REQUIRE(DNS_DB_VALID(db));
252
253	return ((db->methods->ispersistent)(db));
254}
255
256dns_name_t *
257dns_db_origin(dns_db_t *db) {
258	/*
259	 * The origin of the database.
260	 */
261
262	REQUIRE(DNS_DB_VALID(db));
263
264	return (&db->origin);
265}
266
267dns_rdataclass_t
268dns_db_class(dns_db_t *db) {
269	/*
270	 * The class of the database.
271	 */
272
273	REQUIRE(DNS_DB_VALID(db));
274
275	return (db->rdclass);
276}
277
278isc_result_t
279dns_db_beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp,
280		 dns_dbload_t **dbloadp) {
281	/*
282	 * Begin loading 'db'.
283	 */
284
285	REQUIRE(DNS_DB_VALID(db));
286	REQUIRE(addp != NULL && *addp == NULL);
287	REQUIRE(dbloadp != NULL && *dbloadp == NULL);
288
289	return ((db->methods->beginload)(db, addp, dbloadp));
290}
291
292isc_result_t
293dns_db_endload(dns_db_t *db, dns_dbload_t **dbloadp) {
294	/*
295	 * Finish loading 'db'.
296	 */
297
298	REQUIRE(DNS_DB_VALID(db));
299	REQUIRE(dbloadp != NULL && *dbloadp != NULL);
300
301	return ((db->methods->endload)(db, dbloadp));
302}
303
304isc_result_t
305dns_db_load(dns_db_t *db, const char *filename) {
306	return (dns_db_load2(db, filename, dns_masterformat_text));
307}
308
309isc_result_t
310dns_db_load2(dns_db_t *db, const char *filename, dns_masterformat_t format) {
311	isc_result_t result, eresult;
312	dns_rdatacallbacks_t callbacks;
313	unsigned int options = 0;
314
315	/*
316	 * Load master file 'filename' into 'db'.
317	 */
318
319	REQUIRE(DNS_DB_VALID(db));
320
321	if ((db->attributes & DNS_DBATTR_CACHE) != 0)
322		options |= DNS_MASTER_AGETTL;
323
324	dns_rdatacallbacks_init(&callbacks);
325
326	result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private);
327	if (result != ISC_R_SUCCESS)
328		return (result);
329	result = dns_master_loadfile2(filename, &db->origin, &db->origin,
330				      db->rdclass, options,
331				      &callbacks, db->mctx, format);
332	eresult = dns_db_endload(db, &callbacks.add_private);
333	/*
334	 * We always call dns_db_endload(), but we only want to return its
335	 * result if dns_master_loadfile() succeeded.  If dns_master_loadfile()
336	 * failed, we want to return the result code it gave us.
337	 */
338	if (eresult != ISC_R_SUCCESS &&
339	    (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE))
340		result = eresult;
341
342	return (result);
343}
344
345isc_result_t
346dns_db_dump(dns_db_t *db, dns_dbversion_t *version, const char *filename) {
347	return ((db->methods->dump)(db, version, filename,
348				    dns_masterformat_text));
349}
350
351isc_result_t
352dns_db_dump2(dns_db_t *db, dns_dbversion_t *version, const char *filename,
353	     dns_masterformat_t masterformat) {
354	/*
355	 * Dump 'db' into master file 'filename' in the 'masterformat' format.
356	 * XXXJT: is it okay to modify the interface to the existing "dump"
357	 * method?
358	 */
359
360	REQUIRE(DNS_DB_VALID(db));
361
362	return ((db->methods->dump)(db, version, filename, masterformat));
363}
364
365/***
366 *** Version Methods
367 ***/
368
369void
370dns_db_currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
371
372	/*
373	 * Open the current version for reading.
374	 */
375
376	REQUIRE(DNS_DB_VALID(db));
377	REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
378	REQUIRE(versionp != NULL && *versionp == NULL);
379
380	(db->methods->currentversion)(db, versionp);
381}
382
383isc_result_t
384dns_db_newversion(dns_db_t *db, dns_dbversion_t **versionp) {
385
386	/*
387	 * Open a new version for reading and writing.
388	 */
389
390	REQUIRE(DNS_DB_VALID(db));
391	REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
392	REQUIRE(versionp != NULL && *versionp == NULL);
393
394	return ((db->methods->newversion)(db, versionp));
395}
396
397void
398dns_db_attachversion(dns_db_t *db, dns_dbversion_t *source,
399		     dns_dbversion_t **targetp)
400{
401	/*
402	 * Attach '*targetp' to 'source'.
403	 */
404
405	REQUIRE(DNS_DB_VALID(db));
406	REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
407	REQUIRE(source != NULL);
408	REQUIRE(targetp != NULL && *targetp == NULL);
409
410	(db->methods->attachversion)(db, source, targetp);
411
412	ENSURE(*targetp != NULL);
413}
414
415void
416dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
417		    isc_boolean_t commit)
418{
419
420	/*
421	 * Close version '*versionp'.
422	 */
423
424	REQUIRE(DNS_DB_VALID(db));
425	REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
426	REQUIRE(versionp != NULL && *versionp != NULL);
427
428	(db->methods->closeversion)(db, versionp, commit);
429
430	ENSURE(*versionp == NULL);
431}
432
433/***
434 *** Node Methods
435 ***/
436
437isc_result_t
438dns_db_findnode(dns_db_t *db, dns_name_t *name,
439		isc_boolean_t create, dns_dbnode_t **nodep)
440{
441
442	/*
443	 * Find the node with name 'name'.
444	 */
445
446	REQUIRE(DNS_DB_VALID(db));
447	REQUIRE(nodep != NULL && *nodep == NULL);
448
449	return ((db->methods->findnode)(db, name, create, nodep));
450}
451
452isc_result_t
453dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
454	    dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
455	    dns_dbnode_t **nodep, dns_name_t *foundname,
456	    dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
457{
458
459	/*
460	 * Find the best match for 'name' and 'type' in version 'version'
461	 * of 'db'.
462	 */
463
464	REQUIRE(DNS_DB_VALID(db));
465	REQUIRE(type != dns_rdatatype_rrsig);
466	REQUIRE(nodep == NULL || (nodep != NULL && *nodep == NULL));
467	REQUIRE(dns_name_hasbuffer(foundname));
468	REQUIRE(rdataset == NULL ||
469		(DNS_RDATASET_VALID(rdataset) &&
470		 ! dns_rdataset_isassociated(rdataset)));
471	REQUIRE(sigrdataset == NULL ||
472		(DNS_RDATASET_VALID(sigrdataset) &&
473		 ! dns_rdataset_isassociated(sigrdataset)));
474
475	return ((db->methods->find)(db, name, version, type, options, now,
476				    nodep, foundname, rdataset, sigrdataset));
477}
478
479isc_result_t
480dns_db_findzonecut(dns_db_t *db, dns_name_t *name,
481		   unsigned int options, isc_stdtime_t now,
482		   dns_dbnode_t **nodep, dns_name_t *foundname,
483		   dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
484{
485	/*
486	 * Find the deepest known zonecut which encloses 'name' in 'db'.
487	 */
488
489	REQUIRE(DNS_DB_VALID(db));
490	REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
491	REQUIRE(nodep == NULL || (nodep != NULL && *nodep == NULL));
492	REQUIRE(dns_name_hasbuffer(foundname));
493	REQUIRE(sigrdataset == NULL ||
494		(DNS_RDATASET_VALID(sigrdataset) &&
495		 ! dns_rdataset_isassociated(sigrdataset)));
496
497	return ((db->methods->findzonecut)(db, name, options, now, nodep,
498					   foundname, rdataset, sigrdataset));
499}
500
501void
502dns_db_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
503
504	/*
505	 * Attach *targetp to source.
506	 */
507
508	REQUIRE(DNS_DB_VALID(db));
509	REQUIRE(source != NULL);
510	REQUIRE(targetp != NULL && *targetp == NULL);
511
512	(db->methods->attachnode)(db, source, targetp);
513}
514
515void
516dns_db_detachnode(dns_db_t *db, dns_dbnode_t **nodep) {
517
518	/*
519	 * Detach *nodep from its node.
520	 */
521
522	REQUIRE(DNS_DB_VALID(db));
523	REQUIRE(nodep != NULL && *nodep != NULL);
524
525	(db->methods->detachnode)(db, nodep);
526
527	ENSURE(*nodep == NULL);
528}
529
530isc_result_t
531dns_db_expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
532
533	/*
534	 * Mark as stale all records at 'node' which expire at or before 'now'.
535	 */
536
537	REQUIRE(DNS_DB_VALID(db));
538	REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
539	REQUIRE(node != NULL);
540
541	return ((db->methods->expirenode)(db, node, now));
542}
543
544void
545dns_db_printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
546	/*
547	 * Print a textual representation of the contents of the node to
548	 * 'out'.
549	 */
550
551	REQUIRE(DNS_DB_VALID(db));
552	REQUIRE(node != NULL);
553
554	(db->methods->printnode)(db, node, out);
555}
556
557/***
558 *** DB Iterator Creation
559 ***/
560
561isc_result_t
562dns_db_createiterator(dns_db_t *db, isc_boolean_t relative_names,
563		      dns_dbiterator_t **iteratorp)
564{
565	/*
566	 * Create an iterator for version 'version' of 'db'.
567	 */
568
569	REQUIRE(DNS_DB_VALID(db));
570	REQUIRE(iteratorp != NULL && *iteratorp == NULL);
571
572	return (db->methods->createiterator(db, relative_names, iteratorp));
573}
574
575/***
576 *** Rdataset Methods
577 ***/
578
579isc_result_t
580dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
581		    dns_rdatatype_t type, dns_rdatatype_t covers,
582		    isc_stdtime_t now, dns_rdataset_t *rdataset,
583		    dns_rdataset_t *sigrdataset)
584{
585	/*
586	 * Search for an rdataset of type 'type' at 'node' that are in version
587	 * 'version' of 'db'.  If found, make 'rdataset' refer to it.
588	 */
589
590	REQUIRE(DNS_DB_VALID(db));
591	REQUIRE(node != NULL);
592	REQUIRE(DNS_RDATASET_VALID(rdataset));
593	REQUIRE(! dns_rdataset_isassociated(rdataset));
594	REQUIRE(covers == 0 || type == dns_rdatatype_rrsig);
595	REQUIRE(type != dns_rdatatype_any);
596	REQUIRE(sigrdataset == NULL ||
597		(DNS_RDATASET_VALID(sigrdataset) &&
598		 ! dns_rdataset_isassociated(sigrdataset)));
599
600	return ((db->methods->findrdataset)(db, node, version, type, covers,
601					    now, rdataset, sigrdataset));
602}
603
604isc_result_t
605dns_db_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
606		    isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
607{
608	/*
609	 * Make '*iteratorp' an rdataset iteratator for all rdatasets at
610	 * 'node' in version 'version' of 'db'.
611	 */
612
613	REQUIRE(DNS_DB_VALID(db));
614	REQUIRE(iteratorp != NULL && *iteratorp == NULL);
615
616	return ((db->methods->allrdatasets)(db, node, version, now,
617					    iteratorp));
618}
619
620isc_result_t
621dns_db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
622		   isc_stdtime_t now, dns_rdataset_t *rdataset,
623		   unsigned int options, dns_rdataset_t *addedrdataset)
624{
625	/*
626	 * Add 'rdataset' to 'node' in version 'version' of 'db'.
627	 */
628
629	REQUIRE(DNS_DB_VALID(db));
630	REQUIRE(node != NULL);
631	REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL)||
632		((db->attributes & DNS_DBATTR_CACHE) != 0 &&
633		 version == NULL && (options & DNS_DBADD_MERGE) == 0));
634	REQUIRE((options & DNS_DBADD_EXACT) == 0 ||
635		(options & DNS_DBADD_MERGE) != 0);
636	REQUIRE(DNS_RDATASET_VALID(rdataset));
637	REQUIRE(dns_rdataset_isassociated(rdataset));
638	REQUIRE(rdataset->rdclass == db->rdclass);
639	REQUIRE(addedrdataset == NULL ||
640		(DNS_RDATASET_VALID(addedrdataset) &&
641		 ! dns_rdataset_isassociated(addedrdataset)));
642
643	return ((db->methods->addrdataset)(db, node, version, now, rdataset,
644					   options, addedrdataset));
645}
646
647isc_result_t
648dns_db_subtractrdataset(dns_db_t *db, dns_dbnode_t *node,
649			dns_dbversion_t *version, dns_rdataset_t *rdataset,
650			unsigned int options, dns_rdataset_t *newrdataset)
651{
652	/*
653	 * Remove any rdata in 'rdataset' from 'node' in version 'version' of
654	 * 'db'.
655	 */
656
657	REQUIRE(DNS_DB_VALID(db));
658	REQUIRE(node != NULL);
659	REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL);
660	REQUIRE(DNS_RDATASET_VALID(rdataset));
661	REQUIRE(dns_rdataset_isassociated(rdataset));
662	REQUIRE(rdataset->rdclass == db->rdclass);
663	REQUIRE(newrdataset == NULL ||
664		(DNS_RDATASET_VALID(newrdataset) &&
665		 ! dns_rdataset_isassociated(newrdataset)));
666
667	return ((db->methods->subtractrdataset)(db, node, version, rdataset,
668						options, newrdataset));
669}
670
671isc_result_t
672dns_db_deleterdataset(dns_db_t *db, dns_dbnode_t *node,
673		      dns_dbversion_t *version, dns_rdatatype_t type,
674		      dns_rdatatype_t covers)
675{
676	/*
677	 * Make it so that no rdataset of type 'type' exists at 'node' in
678	 * version version 'version' of 'db'.
679	 */
680
681	REQUIRE(DNS_DB_VALID(db));
682	REQUIRE(node != NULL);
683	REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL)||
684		((db->attributes & DNS_DBATTR_CACHE) != 0 && version == NULL));
685
686	return ((db->methods->deleterdataset)(db, node, version,
687					      type, covers));
688}
689
690void
691dns_db_overmem(dns_db_t *db, isc_boolean_t overmem) {
692
693	REQUIRE(DNS_DB_VALID(db));
694
695	(db->methods->overmem)(db, overmem);
696}
697
698isc_result_t
699dns_db_getsoaserial(dns_db_t *db, dns_dbversion_t *ver, isc_uint32_t *serialp)
700{
701	isc_result_t result;
702	dns_dbnode_t *node = NULL;
703	dns_rdataset_t rdataset;
704	dns_rdata_t rdata = DNS_RDATA_INIT;
705	isc_buffer_t buffer;
706
707	REQUIRE(dns_db_iszone(db) || dns_db_isstub(db));
708
709	result = dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node);
710	if (result != ISC_R_SUCCESS)
711		return (result);
712
713	dns_rdataset_init(&rdataset);
714	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0,
715				     (isc_stdtime_t)0, &rdataset, NULL);
716 	if (result != ISC_R_SUCCESS)
717		goto freenode;
718
719	result = dns_rdataset_first(&rdataset);
720 	if (result != ISC_R_SUCCESS)
721		goto freerdataset;
722	dns_rdataset_current(&rdataset, &rdata);
723	result = dns_rdataset_next(&rdataset);
724	INSIST(result == ISC_R_NOMORE);
725
726	INSIST(rdata.length > 20);
727	isc_buffer_init(&buffer, rdata.data, rdata.length);
728	isc_buffer_add(&buffer, rdata.length);
729	isc_buffer_forward(&buffer, rdata.length - 20);
730	*serialp = isc_buffer_getuint32(&buffer);
731
732	result = ISC_R_SUCCESS;
733
734 freerdataset:
735	dns_rdataset_disassociate(&rdataset);
736
737 freenode:
738	dns_db_detachnode(db, &node);
739	return (result);
740}
741
742unsigned int
743dns_db_nodecount(dns_db_t *db) {
744	REQUIRE(DNS_DB_VALID(db));
745
746	return ((db->methods->nodecount)(db));
747}
748
749void
750dns_db_settask(dns_db_t *db, isc_task_t *task) {
751	REQUIRE(DNS_DB_VALID(db));
752
753	(db->methods->settask)(db, task);
754}
755
756isc_result_t
757dns_db_register(const char *name, dns_dbcreatefunc_t create, void *driverarg,
758		isc_mem_t *mctx, dns_dbimplementation_t **dbimp)
759{
760	dns_dbimplementation_t *imp;
761
762	REQUIRE(name != NULL);
763	REQUIRE(dbimp != NULL && *dbimp == NULL);
764
765	RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
766
767	RWLOCK(&implock, isc_rwlocktype_write);
768	imp = impfind(name);
769	if (imp != NULL) {
770		RWUNLOCK(&implock, isc_rwlocktype_write);
771		return (ISC_R_EXISTS);
772	}
773
774	imp = isc_mem_get(mctx, sizeof(dns_dbimplementation_t));
775	if (imp == NULL) {
776		RWUNLOCK(&implock, isc_rwlocktype_write);
777		return (ISC_R_NOMEMORY);
778	}
779	imp->name = name;
780	imp->create = create;
781	imp->mctx = NULL;
782	imp->driverarg = driverarg;
783	isc_mem_attach(mctx, &imp->mctx);
784	ISC_LINK_INIT(imp, link);
785	ISC_LIST_APPEND(implementations, imp, link);
786	RWUNLOCK(&implock, isc_rwlocktype_write);
787
788	*dbimp = imp;
789
790	return (ISC_R_SUCCESS);
791}
792
793void
794dns_db_unregister(dns_dbimplementation_t **dbimp) {
795	dns_dbimplementation_t *imp;
796	isc_mem_t *mctx;
797
798	REQUIRE(dbimp != NULL && *dbimp != NULL);
799
800	RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
801
802	imp = *dbimp;
803	RWLOCK(&implock, isc_rwlocktype_write);
804	ISC_LIST_UNLINK(implementations, imp, link);
805	mctx = imp->mctx;
806	isc_mem_put(mctx, imp, sizeof(dns_dbimplementation_t));
807	isc_mem_detach(&mctx);
808	RWUNLOCK(&implock, isc_rwlocktype_write);
809}
810
811isc_result_t
812dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
813	REQUIRE(DNS_DB_VALID(db));
814	REQUIRE(dns_db_iszone(db) == ISC_TRUE);
815	REQUIRE(nodep != NULL && *nodep == NULL);
816
817	if (db->methods->getoriginnode != NULL)
818		return ((db->methods->getoriginnode)(db, nodep));
819
820	return (ISC_R_NOTFOUND);
821}
822