Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / dns / dlz.c
blob3df5c95b02849714d72de3849511c5d2116a8dd1
1 /* $NetBSD$ */
3 /*
4 * Portions Copyright (C) 2005, 2007, 2009 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
26 * copies.
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
43 * copies.
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.
55 /* Id: dlz.c,v 1.7 2009/01/17 23:47:42 tbox Exp */
57 /*! \file */
59 /***
60 *** Imports
61 ***/
63 #include <config.h>
65 #include <dns/fixedname.h>
66 #include <dns/log.h>
67 #include <dns/master.h>
68 #include <dns/dlz.h>
71 #include <isc/buffer.h>
72 #include <isc/magic.h>
73 #include <isc/mem.h>
74 #include <isc/once.h>
75 #include <isc/rwlock.h>
76 #include <isc/string.h>
77 #include <isc/util.h>
79 /***
80 *** Supported DLZ DB Implementations Registry
81 ***/
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;
87 static void
88 dlz_initialize(void) {
89 RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
90 ISC_LIST_INIT(dlz_implementations);
93 /*%
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);
101 imp != NULL;
102 imp = ISC_LIST_NEXT(imp, link))
103 if (strcasecmp(name, imp->name) == 0)
104 return (imp);
105 return (NULL);
108 /***
109 *** Basic DLZ Methods
110 ***/
112 isc_result_t
113 dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
114 isc_sockaddr_t *clientaddr, dns_db_t **dbp)
116 isc_result_t result;
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);
136 return (result);
139 isc_result_t
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;
144 isc_result_t result;
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'."
176 " %s not loaded.",
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,
196 impinfo->driverarg,
197 &(*dbp)->dbdata));
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);
208 } else {
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));
217 return (result);
220 void
221 dns_dlzdestroy(dns_dlzdb_t **dbp) {
222 isc_mem_t *mctx;
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));
235 /* call the drivers destroy method */
236 if ((*dbp) != NULL) {
237 mctx = (*dbp)->mctx;
238 destroy = (*dbp)->implementation->methods->destroy;
239 (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
240 /* return memory */
241 isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
242 isc_mem_detach(&mctx);
245 *dbp = NULL;
249 isc_result_t
250 dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
251 dns_db_t **dbp)
253 dns_fixedname_t fname;
254 dns_name_t *zonename;
255 unsigned int namelabels;
256 unsigned int i;
257 isc_result_t result;
258 dns_dlzfindzone_t findzone;
259 dns_dlzdb_t *dlzdatabase;
262 * Performs checks to make sure data is as we expect it to be.
264 REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
265 REQUIRE(name != NULL);
266 REQUIRE(dbp != NULL && *dbp == NULL);
268 /* setup a "fixed" dns name */
269 dns_fixedname_init(&fname);
270 zonename = dns_fixedname_name(&fname);
272 /* count the number of labels in the name */
273 namelabels = dns_name_countlabels(name);
276 * loop through starting with the longest domain name and
277 * trying shorter names portions of the name until we find a
278 * match, have an error, or are below the 'minlabels'
279 * threshold. minlabels is 0, if the standard database didn't
280 * have a zone name match. Otherwise minlabels is the number
281 * of labels in that name. We need to beat that for a
282 * "better" match for the DLZ database to be authoritative
283 * instead of the standard database.
285 for (i = namelabels; i > minlabels && i > 1; i--) {
286 if (i == namelabels) {
287 result = dns_name_copy(name, zonename, NULL);
288 if (result != ISC_R_SUCCESS)
289 return (result);
290 } else
291 dns_name_split(name, i, NULL, zonename);
293 /* ask SDLZ driver if the zone is supported */
294 dlzdatabase = view->dlzdatabase;
295 findzone = dlzdatabase->implementation->methods->findzone;
296 result = (*findzone)(dlzdatabase->implementation->driverarg,
297 dlzdatabase->dbdata, dlzdatabase->mctx,
298 view->rdclass, zonename, dbp);
299 if (result != ISC_R_NOTFOUND)
300 return (result);
302 return (ISC_R_NOTFOUND);
306 * Registers a DLZ driver. This basically just adds the dlz
307 * driver to the list of available drivers in the dlz_implementations list.
309 isc_result_t
310 dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
311 void *driverarg, isc_mem_t *mctx,
312 dns_dlzimplementation_t **dlzimp)
315 dns_dlzimplementation_t *dlz_imp;
317 /* Write debugging message to log */
318 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
319 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
320 "Registering DLZ driver '%s'", drivername);
323 * Performs checks to make sure data is as we expect it to be.
325 REQUIRE(drivername != NULL);
326 REQUIRE(methods != NULL);
327 REQUIRE(methods->create != NULL);
328 REQUIRE(methods->destroy != NULL);
329 REQUIRE(methods->findzone != NULL);
330 REQUIRE(mctx != NULL);
331 REQUIRE(dlzimp != NULL && *dlzimp == NULL);
334 * initialize the dlz_implementations list, this is guaranteed
335 * to only really happen once.
337 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
339 /* lock the dlz_implementations list so we can modify it. */
340 RWLOCK(&dlz_implock, isc_rwlocktype_write);
343 * check that another already registered driver isn't using
344 * the same name
346 dlz_imp = dlz_impfind(drivername);
347 if (dlz_imp != NULL) {
348 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
349 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
350 "DLZ Driver '%s' already registered",
351 drivername);
352 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
353 return (ISC_R_EXISTS);
357 * Allocate memory for a dlz_implementation object. Error if
358 * we cannot.
360 dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
361 if (dlz_imp == NULL) {
362 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
363 return (ISC_R_NOMEMORY);
366 /* Make sure memory region is set to all 0's */
367 memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
369 /* Store the data passed into this method */
370 dlz_imp->name = drivername;
371 dlz_imp->methods = methods;
372 dlz_imp->mctx = NULL;
373 dlz_imp->driverarg = driverarg;
375 /* attach the new dlz_implementation object to a memory context */
376 isc_mem_attach(mctx, &dlz_imp->mctx);
379 * prepare the dlz_implementation object to be put in a list,
380 * and append it to the list
382 ISC_LINK_INIT(dlz_imp, link);
383 ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
385 /* Unlock the dlz_implementations list. */
386 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
388 /* Pass back the dlz_implementation that we created. */
389 *dlzimp = dlz_imp;
391 return (ISC_R_SUCCESS);
395 * Helper function for dns_dlzstrtoargv().
396 * Pardon the gratuitous recursion.
398 static isc_result_t
399 dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
400 char ***argvp, unsigned int n)
402 isc_result_t result;
404 restart:
405 /* Discard leading whitespace. */
406 while (*s == ' ' || *s == '\t')
407 s++;
409 if (*s == '\0') {
410 /* We have reached the end of the string. */
411 *argcp = n;
412 *argvp = isc_mem_get(mctx, n * sizeof(char *));
413 if (*argvp == NULL)
414 return (ISC_R_NOMEMORY);
415 } else {
416 char *p = s;
417 while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
418 if (*p == '\n') {
419 *p = ' ';
420 goto restart;
422 p++;
425 /* do "grouping", items between { and } are one arg */
426 if (*p == '{') {
427 char *t = p;
429 * shift all characters to left by 1 to get rid of '{'
431 while (*t != '\0') {
432 t++;
433 *(t-1) = *t;
435 while (*p != '\0' && *p != '}') {
436 p++;
438 /* get rid of '}' character */
439 if (*p == '}') {
440 *p = '\0';
441 p++;
443 /* normal case, no "grouping" */
444 } else if (*p != '\0')
445 *p++ = '\0';
447 result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1);
448 if (result != ISC_R_SUCCESS)
449 return (result);
450 (*argvp)[n] = s;
452 return (ISC_R_SUCCESS);
456 * Tokenize the string "s" into whitespace-separated words,
457 * return the number of words in '*argcp' and an array
458 * of pointers to the words in '*argvp'. The caller
459 * must free the array using isc_mem_put(). The string
460 * is modified in-place.
462 isc_result_t
463 dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
464 unsigned int *argcp, char ***argvp)
466 return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0));
470 * Unregisters a DLZ driver. This basically just removes the dlz
471 * driver from the list of available drivers in the dlz_implementations list.
473 void
474 dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
475 dns_dlzimplementation_t *dlz_imp;
476 isc_mem_t *mctx;
478 /* Write debugging message to log */
479 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
480 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
481 "Unregistering DLZ driver.");
484 * Performs checks to make sure data is as we expect it to be.
486 REQUIRE(dlzimp != NULL && *dlzimp != NULL);
489 * initialize the dlz_implementations list, this is guaranteed
490 * to only really happen once.
492 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
494 dlz_imp = *dlzimp;
496 /* lock the dlz_implementations list so we can modify it. */
497 RWLOCK(&dlz_implock, isc_rwlocktype_write);
499 /* remove the dlz_implementation object from the list */
500 ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
501 mctx = dlz_imp->mctx;
504 * return the memory back to the available memory pool and
505 * remove it from the memory context.
507 isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
508 isc_mem_detach(&mctx);
510 /* Unlock the dlz_implementations list. */
511 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);