1 /* $NetBSD: dlz.c,v 1.7 2014/12/10 04:37:58 christos Exp $ */
4 * Portions Copyright (C) 2005, 2007, 2009-2013 Internet Systems Consortium, Inc. ("ISC")
5 * Portions Copyright (C) 1999-2001 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
21 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
23 * Permission to use, copy, modify, and distribute this software for any
24 * purpose with or without fee is hereby granted, provided that the
25 * above copyright notice and this permission notice appear in all
28 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
29 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
31 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
34 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
35 * USE OR PERFORMANCE OF THIS SOFTWARE.
37 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
38 * conceived and contributed by Rob Butler.
40 * Permission to use, copy, modify, and distribute this software for any
41 * purpose with or without fee is hereby granted, provided that the
42 * above copyright notice and this permission notice appear in all
45 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
46 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
48 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
49 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
50 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
51 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
52 * USE OR PERFORMANCE OF THIS SOFTWARE.
67 #include <dns/fixedname.h>
69 #include <dns/master.h>
74 #include <isc/buffer.h>
75 #include <isc/magic.h>
78 #include <isc/rwlock.h>
79 #include <isc/string.h>
83 *** Supported DLZ DB Implementations Registry
86 static ISC_LIST(dns_dlzimplementation_t
) dlz_implementations
;
87 static isc_rwlock_t dlz_implock
;
88 static isc_once_t once
= ISC_ONCE_INIT
;
91 dlz_initialize(void) {
92 RUNTIME_CHECK(isc_rwlock_init(&dlz_implock
, 0, 0) == ISC_R_SUCCESS
);
93 ISC_LIST_INIT(dlz_implementations
);
97 * Searches the dlz_implementations list for a driver matching name.
99 static inline dns_dlzimplementation_t
*
100 dlz_impfind(const char *name
) {
101 dns_dlzimplementation_t
*imp
;
103 for (imp
= ISC_LIST_HEAD(dlz_implementations
);
105 imp
= ISC_LIST_NEXT(imp
, link
))
106 if (strcasecmp(name
, imp
->name
) == 0)
112 *** Basic DLZ Methods
116 dns_dlzallowzonexfr(dns_view_t
*view
, dns_name_t
*name
,
117 isc_sockaddr_t
*clientaddr
, dns_db_t
**dbp
)
119 isc_result_t result
= ISC_R_NOTFOUND
;
120 dns_dlzallowzonexfr_t allowzonexfr
;
124 * Performs checks to make sure data is as we expect it to be.
126 REQUIRE(name
!= NULL
);
127 REQUIRE(dbp
!= NULL
&& *dbp
== NULL
);
130 * Find a driver in which the zone exists and transfer is supported
132 for (dlzdb
= ISC_LIST_HEAD(view
->dlz_searched
);
134 dlzdb
= ISC_LIST_NEXT(dlzdb
, link
))
136 REQUIRE(DNS_DLZ_VALID(dlzdb
));
138 allowzonexfr
= dlzdb
->implementation
->methods
->allowzonexfr
;
139 result
= (*allowzonexfr
)(dlzdb
->implementation
->driverarg
,
140 dlzdb
->dbdata
, dlzdb
->mctx
,
141 view
->rdclass
, name
, clientaddr
, dbp
);
144 * if ISC_R_NOPERM, we found the right database but
145 * the zone may not transfer.
147 if (result
== ISC_R_SUCCESS
|| result
== ISC_R_NOPERM
)
151 if (result
== ISC_R_NOTIMPLEMENTED
)
152 result
= ISC_R_NOTFOUND
;
158 dns_dlzcreate(isc_mem_t
*mctx
, const char *dlzname
, const char *drivername
,
159 unsigned int argc
, char *argv
[], dns_dlzdb_t
**dbp
)
161 dns_dlzimplementation_t
*impinfo
;
163 dns_dlzdb_t
*db
= NULL
;
166 * initialize the dlz_implementations list, this is guaranteed
167 * to only really happen once.
169 RUNTIME_CHECK(isc_once_do(&once
, dlz_initialize
) == ISC_R_SUCCESS
);
172 * Performs checks to make sure data is as we expect it to be.
174 REQUIRE(dbp
!= NULL
&& *dbp
== NULL
);
175 REQUIRE(dlzname
!= NULL
);
176 REQUIRE(drivername
!= NULL
);
177 REQUIRE(mctx
!= NULL
);
179 /* write log message */
180 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
181 DNS_LOGMODULE_DLZ
, ISC_LOG_INFO
,
182 "Loading '%s' using driver %s", dlzname
, drivername
);
184 /* lock the dlz_implementations list so we can search it. */
185 RWLOCK(&dlz_implock
, isc_rwlocktype_read
);
187 /* search for the driver implementation */
188 impinfo
= dlz_impfind(drivername
);
189 if (impinfo
== NULL
) {
190 RWUNLOCK(&dlz_implock
, isc_rwlocktype_read
);
192 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
193 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
194 "unsupported DLZ database driver '%s'."
196 drivername
, dlzname
);
198 return (ISC_R_NOTFOUND
);
201 /* Allocate memory to hold the DLZ database driver */
202 db
= isc_mem_get(mctx
, sizeof(dns_dlzdb_t
));
204 RWUNLOCK(&dlz_implock
, isc_rwlocktype_read
);
205 return (ISC_R_NOMEMORY
);
208 /* Make sure memory region is set to all 0's */
209 memset(db
, 0, sizeof(dns_dlzdb_t
));
211 ISC_LINK_INIT(db
, link
);
212 db
->implementation
= impinfo
;
214 db
->dlzname
= isc_mem_strdup(mctx
, dlzname
);
216 /* Create a new database using implementation 'drivername'. */
217 result
= ((impinfo
->methods
->create
)(mctx
, dlzname
, argc
, argv
,
221 /* mark the DLZ driver as valid */
222 if (result
== ISC_R_SUCCESS
) {
223 RWUNLOCK(&dlz_implock
, isc_rwlocktype_read
);
224 db
->magic
= DNS_DLZ_MAGIC
;
225 isc_mem_attach(mctx
, &db
->mctx
);
226 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
227 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
228 "DLZ driver loaded successfully.");
230 return (ISC_R_SUCCESS
);
232 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
233 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
234 "DLZ driver failed to load.");
237 /* impinfo->methods->create failed. */
238 RWUNLOCK(&dlz_implock
, isc_rwlocktype_read
);
239 isc_mem_put(mctx
, db
, sizeof(dns_dlzdb_t
));
244 dns_dlzdestroy(dns_dlzdb_t
**dbp
) {
246 dns_dlzdestroy_t destroy
;
248 /* Write debugging message to log */
249 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
250 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
251 "Unloading DLZ driver.");
254 * Perform checks to make sure data is as we expect it to be.
256 REQUIRE(dbp
!= NULL
&& DNS_DLZ_VALID(*dbp
));
258 if ((*dbp
)->ssutable
!= NULL
) {
259 dns_ssutable_detach(&(*dbp
)->ssutable
);
262 /* call the drivers destroy method */
263 if ((*dbp
) != NULL
) {
265 if ((*dbp
)->dlzname
!= NULL
)
266 isc_mem_free(mctx
, (*dbp
)->dlzname
);
267 destroy
= (*dbp
)->implementation
->methods
->destroy
;
268 (*destroy
)((*dbp
)->implementation
->driverarg
,(*dbp
)->dbdata
);
270 isc_mem_put(mctx
, (*dbp
), sizeof(dns_dlzdb_t
));
271 isc_mem_detach(&mctx
);
278 * Registers a DLZ driver. This basically just adds the dlz
279 * driver to the list of available drivers in the dlz_implementations list.
282 dns_dlzregister(const char *drivername
, const dns_dlzmethods_t
*methods
,
283 void *driverarg
, isc_mem_t
*mctx
,
284 dns_dlzimplementation_t
**dlzimp
)
287 dns_dlzimplementation_t
*dlz_imp
;
289 /* Write debugging message to log */
290 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
291 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
292 "Registering DLZ driver '%s'", drivername
);
295 * Performs checks to make sure data is as we expect it to be.
297 REQUIRE(drivername
!= NULL
);
298 REQUIRE(methods
!= NULL
);
299 REQUIRE(methods
->create
!= NULL
);
300 REQUIRE(methods
->destroy
!= NULL
);
301 REQUIRE(methods
->findzone
!= NULL
);
302 REQUIRE(mctx
!= NULL
);
303 REQUIRE(dlzimp
!= NULL
&& *dlzimp
== NULL
);
306 * initialize the dlz_implementations list, this is guaranteed
307 * to only really happen once.
309 RUNTIME_CHECK(isc_once_do(&once
, dlz_initialize
) == ISC_R_SUCCESS
);
311 /* lock the dlz_implementations list so we can modify it. */
312 RWLOCK(&dlz_implock
, isc_rwlocktype_write
);
315 * check that another already registered driver isn't using
318 dlz_imp
= dlz_impfind(drivername
);
319 if (dlz_imp
!= NULL
) {
320 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
321 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
322 "DLZ Driver '%s' already registered",
324 RWUNLOCK(&dlz_implock
, isc_rwlocktype_write
);
325 return (ISC_R_EXISTS
);
329 * Allocate memory for a dlz_implementation object. Error if
332 dlz_imp
= isc_mem_get(mctx
, sizeof(dns_dlzimplementation_t
));
333 if (dlz_imp
== NULL
) {
334 RWUNLOCK(&dlz_implock
, isc_rwlocktype_write
);
335 return (ISC_R_NOMEMORY
);
338 /* Make sure memory region is set to all 0's */
339 memset(dlz_imp
, 0, sizeof(dns_dlzimplementation_t
));
341 /* Store the data passed into this method */
342 dlz_imp
->name
= drivername
;
343 dlz_imp
->methods
= methods
;
344 dlz_imp
->mctx
= NULL
;
345 dlz_imp
->driverarg
= driverarg
;
347 /* attach the new dlz_implementation object to a memory context */
348 isc_mem_attach(mctx
, &dlz_imp
->mctx
);
351 * prepare the dlz_implementation object to be put in a list,
352 * and append it to the list
354 ISC_LINK_INIT(dlz_imp
, link
);
355 ISC_LIST_APPEND(dlz_implementations
, dlz_imp
, link
);
357 /* Unlock the dlz_implementations list. */
358 RWUNLOCK(&dlz_implock
, isc_rwlocktype_write
);
360 /* Pass back the dlz_implementation that we created. */
363 return (ISC_R_SUCCESS
);
367 * Helper function for dns_dlzstrtoargv().
368 * Pardon the gratuitous recursion.
371 dns_dlzstrtoargvsub(isc_mem_t
*mctx
, char *s
, unsigned int *argcp
,
372 char ***argvp
, unsigned int n
)
377 /* Discard leading whitespace. */
378 while (*s
== ' ' || *s
== '\t')
382 /* We have reached the end of the string. */
384 *argvp
= isc_mem_get(mctx
, n
* sizeof(char *));
386 return (ISC_R_NOMEMORY
);
389 while (*p
!= ' ' && *p
!= '\t' && *p
!= '\0' && *p
!= '{') {
397 /* do "grouping", items between { and } are one arg */
401 * shift all characters to left by 1 to get rid of '{'
407 while (*p
!= '\0' && *p
!= '}') {
410 /* get rid of '}' character */
415 /* normal case, no "grouping" */
416 } else if (*p
!= '\0')
419 result
= dns_dlzstrtoargvsub(mctx
, p
, argcp
, argvp
, n
+ 1);
420 if (result
!= ISC_R_SUCCESS
)
424 return (ISC_R_SUCCESS
);
428 * Tokenize the string "s" into whitespace-separated words,
429 * return the number of words in '*argcp' and an array
430 * of pointers to the words in '*argvp'. The caller
431 * must free the array using isc_mem_put(). The string
432 * is modified in-place.
435 dns_dlzstrtoargv(isc_mem_t
*mctx
, char *s
,
436 unsigned int *argcp
, char ***argvp
)
438 return(dns_dlzstrtoargvsub(mctx
, s
, argcp
, argvp
, 0));
442 * Unregisters a DLZ driver. This basically just removes the dlz
443 * driver from the list of available drivers in the dlz_implementations list.
446 dns_dlzunregister(dns_dlzimplementation_t
**dlzimp
) {
447 dns_dlzimplementation_t
*dlz_imp
;
450 /* Write debugging message to log */
451 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
452 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
453 "Unregistering DLZ driver.");
456 * Performs checks to make sure data is as we expect it to be.
458 REQUIRE(dlzimp
!= NULL
&& *dlzimp
!= NULL
);
461 * initialize the dlz_implementations list, this is guaranteed
462 * to only really happen once.
464 RUNTIME_CHECK(isc_once_do(&once
, dlz_initialize
) == ISC_R_SUCCESS
);
468 /* lock the dlz_implementations list so we can modify it. */
469 RWLOCK(&dlz_implock
, isc_rwlocktype_write
);
471 /* remove the dlz_implementation object from the list */
472 ISC_LIST_UNLINK(dlz_implementations
, dlz_imp
, link
);
473 mctx
= dlz_imp
->mctx
;
476 * Return the memory back to the available memory pool and
477 * remove it from the memory context.
479 isc_mem_put(mctx
, dlz_imp
, sizeof(dns_dlzimplementation_t
));
480 isc_mem_detach(&mctx
);
482 /* Unlock the dlz_implementations list. */
483 RWUNLOCK(&dlz_implock
, isc_rwlocktype_write
);
487 * Create a writeable DLZ zone. This can be called by DLZ drivers
488 * during configure() to create a zone that can be updated. The zone
489 * type is set to dns_zone_dlz, which is equivalent to a master zone
491 * This function uses a callback setup in dns_dlzconfigure() to call
492 * into the server zone code to setup the remaining pieces of server
493 * specific functionality on the zone
496 dns_dlz_writeablezone(dns_view_t
*view
, dns_dlzdb_t
*dlzdb
,
497 const char *zone_name
)
499 dns_zone_t
*zone
= NULL
;
500 dns_zone_t
*dupzone
= NULL
;
503 dns_fixedname_t fixorigin
;
506 REQUIRE(DNS_DLZ_VALID(dlzdb
));
508 REQUIRE(dlzdb
->configure_callback
!= NULL
);
510 isc_buffer_constinit(&buffer
, zone_name
, strlen(zone_name
));
511 isc_buffer_add(&buffer
, strlen(zone_name
));
512 dns_fixedname_init(&fixorigin
);
513 result
= dns_name_fromtext(dns_fixedname_name(&fixorigin
),
514 &buffer
, dns_rootname
, 0, NULL
);
515 if (result
!= ISC_R_SUCCESS
)
517 origin
= dns_fixedname_name(&fixorigin
);
519 if (!dlzdb
->search
) {
520 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
521 DNS_LOGMODULE_DLZ
, ISC_LOG_WARNING
,
522 "DLZ %s has 'search no;', but attempted to "
523 "register writeable zone %s.",
524 dlzdb
->dlzname
, zone_name
);
525 result
= ISC_R_SUCCESS
;
529 /* See if the zone already exists */
530 result
= dns_view_findzone(view
, origin
, &dupzone
);
531 if (result
== ISC_R_SUCCESS
) {
532 dns_zone_detach(&dupzone
);
533 result
= ISC_R_EXISTS
;
536 INSIST(dupzone
== NULL
);
539 result
= dns_zone_create(&zone
, view
->mctx
);
540 if (result
!= ISC_R_SUCCESS
)
542 result
= dns_zone_setorigin(zone
, origin
);
543 if (result
!= ISC_R_SUCCESS
)
545 dns_zone_setview(zone
, view
);
547 dns_zone_setadded(zone
, ISC_TRUE
);
549 if (dlzdb
->ssutable
== NULL
) {
550 result
= dns_ssutable_createdlz(dlzdb
->mctx
,
551 &dlzdb
->ssutable
, dlzdb
);
552 if (result
!= ISC_R_SUCCESS
)
555 dns_zone_setssutable(zone
, dlzdb
->ssutable
);
557 result
= dlzdb
->configure_callback(view
, dlzdb
, zone
);
558 if (result
!= ISC_R_SUCCESS
)
561 result
= dns_view_addzone(view
, zone
);
566 dns_zone_detach(&zone
);
572 * Configure a DLZ driver. This is optional, and if supplied gives
573 * the backend an opportunity to configure parameters related to DLZ.
576 dns_dlzconfigure(dns_view_t
*view
, dns_dlzdb_t
*dlzdb
,
577 dlzconfigure_callback_t callback
)
579 dns_dlzimplementation_t
*impl
;
582 REQUIRE(DNS_DLZ_VALID(dlzdb
));
583 REQUIRE(dlzdb
->implementation
!= NULL
);
585 impl
= dlzdb
->implementation
;
587 if (impl
->methods
->configure
== NULL
)
588 return (ISC_R_SUCCESS
);
590 dlzdb
->configure_callback
= callback
;
592 result
= impl
->methods
->configure(impl
->driverarg
, dlzdb
->dbdata
,
598 dns_dlz_ssumatch(dns_dlzdb_t
*dlzdatabase
, dns_name_t
*signer
,
599 dns_name_t
*name
, isc_netaddr_t
*tcpaddr
,
600 dns_rdatatype_t type
, const dst_key_t
*key
)
602 dns_dlzimplementation_t
*impl
;
605 REQUIRE(dlzdatabase
!= NULL
);
606 REQUIRE(dlzdatabase
->implementation
!= NULL
);
607 REQUIRE(dlzdatabase
->implementation
->methods
!= NULL
);
608 impl
= dlzdatabase
->implementation
;
610 if (impl
->methods
->ssumatch
== NULL
) {
611 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
612 DNS_LOGMODULE_DLZ
, ISC_LOG_INFO
,
613 "No ssumatch method for DLZ database");
617 r
= impl
->methods
->ssumatch(signer
, name
, tcpaddr
, type
, key
,
618 impl
->driverarg
, dlzdatabase
->dbdata
);