2 * Portions Copyright (C) 2005, 2007, 2009-2012 Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 1999-2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
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.
19 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
21 * Permission to use, copy, modify, and distribute this software for any
22 * purpose with or without fee is hereby granted, provided that the
23 * above copyright notice and this permission notice appear in all
26 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
27 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
29 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
30 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
31 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
32 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
33 * USE OR PERFORMANCE OF THIS SOFTWARE.
35 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
36 * conceived and contributed by Rob Butler.
38 * Permission to use, copy, modify, and distribute this software for any
39 * purpose with or without fee is hereby granted, provided that the
40 * above copyright notice and this permission notice appear in all
43 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
44 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
46 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
47 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
48 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
49 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
50 * USE OR PERFORMANCE OF THIS SOFTWARE.
63 #include <dns/fixedname.h>
65 #include <dns/master.h>
71 #include <isc/buffer.h>
72 #include <isc/magic.h>
75 #include <isc/rwlock.h>
76 #include <isc/string.h>
80 *** Supported DLZ DB Implementations Registry
83 static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
84 static isc_rwlock_t dlz_implock;
85 static isc_once_t once = ISC_ONCE_INIT;
88 dlz_initialize(void) {
89 RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
90 ISC_LIST_INIT(dlz_implementations);
94 * Searches the dlz_implementations list for a driver matching name.
96 static inline dns_dlzimplementation_t *
97 dlz_impfind(const char *name) {
98 dns_dlzimplementation_t *imp;
100 for (imp = ISC_LIST_HEAD(dlz_implementations);
102 imp = ISC_LIST_NEXT(imp, link))
103 if (strcasecmp(name, imp->name) == 0)
109 *** Basic DLZ Methods
113 dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
114 isc_sockaddr_t *clientaddr, dns_db_t **dbp)
117 dns_dlzallowzonexfr_t allowzonexfr;
118 dns_dlzdb_t *dlzdatabase;
121 * Performs checks to make sure data is as we expect it to be.
123 REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
124 REQUIRE(name != NULL);
125 REQUIRE(dbp != NULL && *dbp == NULL);
127 /* ask driver if the zone is supported */
128 dlzdatabase = view->dlzdatabase;
129 allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr;
130 result = (*allowzonexfr)(dlzdatabase->implementation->driverarg,
131 dlzdatabase->dbdata, dlzdatabase->mctx,
132 view->rdclass, name, clientaddr, dbp);
134 if (result == ISC_R_NOTIMPLEMENTED)
135 return (ISC_R_NOTFOUND);
140 dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
141 unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
143 dns_dlzimplementation_t *impinfo;
147 * initialize the dlz_implementations list, this is guaranteed
148 * to only really happen once.
150 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
153 * Performs checks to make sure data is as we expect it to be.
155 REQUIRE(dbp != NULL && *dbp == NULL);
156 REQUIRE(dlzname != NULL);
157 REQUIRE(drivername != NULL);
158 REQUIRE(mctx != NULL);
160 /* write log message */
161 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
162 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
163 "Loading '%s' using driver %s", dlzname, drivername);
165 /* lock the dlz_implementations list so we can search it. */
166 RWLOCK(&dlz_implock, isc_rwlocktype_read);
168 /* search for the driver implementation */
169 impinfo = dlz_impfind(drivername);
170 if (impinfo == NULL) {
171 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
173 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
174 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
175 "unsupported DLZ database driver '%s'."
177 drivername, dlzname);
179 return (ISC_R_NOTFOUND);
182 /* Allocate memory to hold the DLZ database driver */
183 (*dbp) = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
184 if ((*dbp) == NULL) {
185 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
186 return (ISC_R_NOMEMORY);
189 /* Make sure memory region is set to all 0's */
190 memset((*dbp), 0, sizeof(dns_dlzdb_t));
192 (*dbp)->implementation = impinfo;
194 /* Create a new database using implementation 'drivername'. */
195 result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
199 /* mark the DLZ driver as valid */
200 if (result == ISC_R_SUCCESS) {
201 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
202 (*dbp)->magic = DNS_DLZ_MAGIC;
203 isc_mem_attach(mctx, &(*dbp)->mctx);
204 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
205 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
206 "DLZ driver loaded successfully.");
207 return (ISC_R_SUCCESS);
209 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
210 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
211 "DLZ driver failed to load.");
214 /* impinfo->methods->create failed. */
215 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
216 isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
221 dns_dlzdestroy(dns_dlzdb_t **dbp) {
223 dns_dlzdestroy_t destroy;
225 /* Write debugging message to log */
226 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
227 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
228 "Unloading DLZ driver.");
231 * Perform checks to make sure data is as we expect it to be.
233 REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
236 if ((*dbp)->ssutable != NULL) {
237 dns_ssutable_detach(&(*dbp)->ssutable);
241 /* call the drivers destroy method */
242 if ((*dbp) != NULL) {
244 destroy = (*dbp)->implementation->methods->destroy;
245 (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
247 isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
248 isc_mem_detach(&mctx);
256 dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
259 dns_fixedname_t fname;
260 dns_name_t *zonename;
261 unsigned int namelabels;
264 dns_dlzfindzone_t findzone;
265 dns_dlzdb_t *dlzdatabase;
268 * Performs checks to make sure data is as we expect it to be.
270 REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
271 REQUIRE(name != NULL);
272 REQUIRE(dbp != NULL && *dbp == NULL);
274 /* setup a "fixed" dns name */
275 dns_fixedname_init(&fname);
276 zonename = dns_fixedname_name(&fname);
278 /* count the number of labels in the name */
279 namelabels = dns_name_countlabels(name);
282 * loop through starting with the longest domain name and
283 * trying shorter names portions of the name until we find a
284 * match, have an error, or are below the 'minlabels'
285 * threshold. minlabels is 0, if the standard database didn't
286 * have a zone name match. Otherwise minlabels is the number
287 * of labels in that name. We need to beat that for a
288 * "better" match for the DLZ database to be authoritative
289 * instead of the standard database.
291 for (i = namelabels; i > minlabels && i > 1; i--) {
292 if (i == namelabels) {
293 result = dns_name_copy(name, zonename, NULL);
294 if (result != ISC_R_SUCCESS)
297 dns_name_split(name, i, NULL, zonename);
299 /* ask SDLZ driver if the zone is supported */
300 dlzdatabase = view->dlzdatabase;
301 findzone = dlzdatabase->implementation->methods->findzone;
302 result = (*findzone)(dlzdatabase->implementation->driverarg,
303 dlzdatabase->dbdata, dlzdatabase->mctx,
304 view->rdclass, zonename, dbp);
305 if (result != ISC_R_NOTFOUND)
308 return (ISC_R_NOTFOUND);
312 * Registers a DLZ driver. This basically just adds the dlz
313 * driver to the list of available drivers in the dlz_implementations list.
316 dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
317 void *driverarg, isc_mem_t *mctx,
318 dns_dlzimplementation_t **dlzimp)
321 dns_dlzimplementation_t *dlz_imp;
323 /* Write debugging message to log */
324 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
325 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
326 "Registering DLZ driver '%s'", drivername);
329 * Performs checks to make sure data is as we expect it to be.
331 REQUIRE(drivername != NULL);
332 REQUIRE(methods != NULL);
333 REQUIRE(methods->create != NULL);
334 REQUIRE(methods->destroy != NULL);
335 REQUIRE(methods->findzone != NULL);
336 REQUIRE(mctx != NULL);
337 REQUIRE(dlzimp != NULL && *dlzimp == NULL);
340 * initialize the dlz_implementations list, this is guaranteed
341 * to only really happen once.
343 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
345 /* lock the dlz_implementations list so we can modify it. */
346 RWLOCK(&dlz_implock, isc_rwlocktype_write);
349 * check that another already registered driver isn't using
352 dlz_imp = dlz_impfind(drivername);
353 if (dlz_imp != NULL) {
354 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
355 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
356 "DLZ Driver '%s' already registered",
358 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
359 return (ISC_R_EXISTS);
363 * Allocate memory for a dlz_implementation object. Error if
366 dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
367 if (dlz_imp == NULL) {
368 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
369 return (ISC_R_NOMEMORY);
372 /* Make sure memory region is set to all 0's */
373 memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
375 /* Store the data passed into this method */
376 dlz_imp->name = drivername;
377 dlz_imp->methods = methods;
378 dlz_imp->mctx = NULL;
379 dlz_imp->driverarg = driverarg;
381 /* attach the new dlz_implementation object to a memory context */
382 isc_mem_attach(mctx, &dlz_imp->mctx);
385 * prepare the dlz_implementation object to be put in a list,
386 * and append it to the list
388 ISC_LINK_INIT(dlz_imp, link);
389 ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
391 /* Unlock the dlz_implementations list. */
392 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
394 /* Pass back the dlz_implementation that we created. */
397 return (ISC_R_SUCCESS);
401 * Helper function for dns_dlzstrtoargv().
402 * Pardon the gratuitous recursion.
405 dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
406 char ***argvp, unsigned int n)
411 /* Discard leading whitespace. */
412 while (*s == ' ' || *s == '\t')
416 /* We have reached the end of the string. */
418 *argvp = isc_mem_get(mctx, n * sizeof(char *));
420 return (ISC_R_NOMEMORY);
423 while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
431 /* do "grouping", items between { and } are one arg */
435 * shift all characters to left by 1 to get rid of '{'
441 while (*p != '\0' && *p != '}') {
444 /* get rid of '}' character */
449 /* normal case, no "grouping" */
450 } else if (*p != '\0')
453 result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1);
454 if (result != ISC_R_SUCCESS)
458 return (ISC_R_SUCCESS);
462 * Tokenize the string "s" into whitespace-separated words,
463 * return the number of words in '*argcp' and an array
464 * of pointers to the words in '*argvp'. The caller
465 * must free the array using isc_mem_put(). The string
466 * is modified in-place.
469 dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
470 unsigned int *argcp, char ***argvp)
472 return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0));
476 * Unregisters a DLZ driver. This basically just removes the dlz
477 * driver from the list of available drivers in the dlz_implementations list.
480 dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
481 dns_dlzimplementation_t *dlz_imp;
484 /* Write debugging message to log */
485 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
486 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
487 "Unregistering DLZ driver.");
490 * Performs checks to make sure data is as we expect it to be.
492 REQUIRE(dlzimp != NULL && *dlzimp != NULL);
495 * initialize the dlz_implementations list, this is guaranteed
496 * to only really happen once.
498 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
502 /* lock the dlz_implementations list so we can modify it. */
503 RWLOCK(&dlz_implock, isc_rwlocktype_write);
505 /* remove the dlz_implementation object from the list */
506 ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
507 mctx = dlz_imp->mctx;
510 * Return the memory back to the available memory pool and
511 * remove it from the memory context.
513 isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
514 isc_mem_detach(&mctx);
516 /* Unlock the dlz_implementations list. */
517 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
522 * Create a writeable DLZ zone. This can be called by DLZ drivers
523 * during configure() to create a zone that can be updated. The zone
524 * type is set to dns_zone_dlz, which is equivalent to a master zone
526 * This function uses a callback setup in dns_dlzconfigure() to call
527 * into the server zone code to setup the remaining pieces of server
528 * specific functionality on the zone
531 dns_dlz_writeablezone(dns_view_t *view, const char *zone_name) {
532 dns_zone_t *zone = NULL;
533 dns_zone_t *dupzone = NULL;
536 dns_fixedname_t fixorigin;
538 dns_dlzdb_t *dlzdatabase;
540 REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
542 dlzdatabase = view->dlzdatabase;
544 REQUIRE(dlzdatabase->configure_callback != NULL);
546 isc_buffer_init(&buffer, zone_name, strlen(zone_name));
547 isc_buffer_add(&buffer, strlen(zone_name));
548 dns_fixedname_init(&fixorigin);
549 result = dns_name_fromtext(dns_fixedname_name(&fixorigin),
550 &buffer, dns_rootname, 0, NULL);
551 if (result != ISC_R_SUCCESS)
553 origin = dns_fixedname_name(&fixorigin);
555 /* See if the zone already exists */
556 result = dns_view_findzone(view, origin, &dupzone);
557 if (result == ISC_R_SUCCESS) {
558 dns_zone_detach(&dupzone);
559 result = ISC_R_EXISTS;
562 INSIST(dupzone == NULL);
565 result = dns_zone_create(&zone, view->mctx);
566 if (result != ISC_R_SUCCESS)
568 result = dns_zone_setorigin(zone, origin);
569 if (result != ISC_R_SUCCESS)
571 dns_zone_setview(zone, view);
573 dns_zone_setadded(zone, ISC_TRUE);
575 if (dlzdatabase->ssutable == NULL) {
576 result = dns_ssutable_createdlz(dlzdatabase->mctx,
577 &dlzdatabase->ssutable,
579 if (result != ISC_R_SUCCESS)
582 dns_zone_setssutable(zone, dlzdatabase->ssutable);
584 result = dlzdatabase->configure_callback(view, zone);
585 if (result != ISC_R_SUCCESS)
589 * Add the zone to its view in the new view list.
591 result = dns_view_addzone(view, zone);
595 dns_zone_detach(&zone);
602 * Configure a DLZ driver. This is optional, and if supplied gives
603 * the backend an opportunity to configure parameters related to DLZ.
606 dns_dlzconfigure(dns_view_t *view, isc_result_t (*callback)(dns_view_t *,
609 dns_dlzimplementation_t *impl;
610 dns_dlzdb_t *dlzdatabase;
613 REQUIRE(view != NULL);
614 REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
615 REQUIRE(view->dlzdatabase->implementation != NULL);
617 dlzdatabase = view->dlzdatabase;
618 impl = dlzdatabase->implementation;
620 if (impl->methods->configure == NULL)
621 return (ISC_R_SUCCESS);
623 dlzdatabase->configure_callback = callback;
625 result = impl->methods->configure(impl->driverarg,
626 dlzdatabase->dbdata, view);
631 dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase,
632 dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr,
633 dns_rdatatype_t type, const dst_key_t *key)
635 dns_dlzimplementation_t *impl;
638 REQUIRE(dlzdatabase != NULL);
639 REQUIRE(dlzdatabase->implementation != NULL);
640 REQUIRE(dlzdatabase->implementation->methods != NULL);
641 impl = dlzdatabase->implementation;
643 if (impl->methods->ssumatch == NULL) {
644 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
645 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
646 "No ssumatch method for DLZ database");
650 r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
651 impl->driverarg, dlzdatabase->dbdata);