bind - Upgraded vendor branch to 9.5.2-P1
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / dlz.c
1 /*
2  * Portions Copyright (C) 2005, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3  * Portions Copyright (C) 1999-2001  Internet Software Consortium.
4  *
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.
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 /*
19  * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
20  *
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
24  * copies.
25  *
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.
34  *
35  * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
36  * conceived and contributed by Rob Butler.
37  *
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
41  * copies.
42  *
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.
51  */
52
53 /* $Id: dlz.c,v 1.5.128.2 2009/01/19 23:47:02 tbox Exp $ */
54
55 /*! \file */
56
57 /***
58  *** Imports
59  ***/
60
61 #include <config.h>
62
63 #include <dns/fixedname.h>
64 #include <dns/log.h>
65 #include <dns/master.h>
66 #include <dns/dlz.h>
67
68
69 #include <isc/buffer.h>
70 #include <isc/magic.h>
71 #include <isc/mem.h>
72 #include <isc/once.h>
73 #include <isc/rwlock.h>
74 #include <isc/string.h>
75 #include <isc/util.h>
76
77 /***
78  *** Supported DLZ DB Implementations Registry
79  ***/
80
81 static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
82 static isc_rwlock_t dlz_implock;
83 static isc_once_t once = ISC_ONCE_INIT;
84
85 static void
86 dlz_initialize(void) {
87         RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
88         ISC_LIST_INIT(dlz_implementations);
89 }
90
91 /*%
92  * Searches the dlz_implementations list for a driver matching name.
93  */
94 static inline dns_dlzimplementation_t *
95 dlz_impfind(const char *name) {
96         dns_dlzimplementation_t *imp;
97
98         for (imp = ISC_LIST_HEAD(dlz_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  *** Basic DLZ Methods
108  ***/
109
110 isc_result_t
111 dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
112                     isc_sockaddr_t *clientaddr, dns_db_t **dbp)
113 {
114         isc_result_t result;
115         dns_dlzallowzonexfr_t allowzonexfr;
116         dns_dlzdb_t *dlzdatabase;
117
118         /*
119          * Performs checks to make sure data is as we expect it to be.
120          */
121         REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
122         REQUIRE(name != NULL);
123         REQUIRE(dbp != NULL && *dbp == NULL);
124
125         /* ask driver if the zone is supported */
126         dlzdatabase = view->dlzdatabase;
127         allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr;
128         result = (*allowzonexfr)(dlzdatabase->implementation->driverarg,
129                                  dlzdatabase->dbdata, dlzdatabase->mctx,
130                                  view->rdclass, name, clientaddr, dbp);
131
132         if (result == ISC_R_NOTIMPLEMENTED)
133                 return (ISC_R_NOTFOUND);
134         return (result);
135 }
136
137 isc_result_t
138 dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
139               unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
140 {
141         dns_dlzimplementation_t *impinfo;
142         isc_result_t result;
143
144         /*
145          * initialize the dlz_implementations list, this is guaranteed
146          * to only really happen once.
147          */
148         RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
149
150         /*
151          * Performs checks to make sure data is as we expect it to be.
152          */
153         REQUIRE(dbp != NULL && *dbp == NULL);
154         REQUIRE(dlzname != NULL);
155         REQUIRE(drivername != NULL);
156         REQUIRE(mctx != NULL);
157
158         /* write log message */
159         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
160                       DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
161                       "Loading '%s' using driver %s", dlzname, drivername);
162
163         /* lock the dlz_implementations list so we can search it. */
164         RWLOCK(&dlz_implock, isc_rwlocktype_read);
165
166         /* search for the driver implementation  */
167         impinfo = dlz_impfind(drivername);
168         if (impinfo == NULL) {
169                 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
170
171                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
172                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
173                               "unsupported DLZ database driver '%s'."
174                               "  %s not loaded.",
175                               drivername, dlzname);
176
177                 return (ISC_R_NOTFOUND);
178         }
179
180         /* Allocate memory to hold the DLZ database driver */
181         (*dbp) = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
182         if ((*dbp) == NULL) {
183                 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
184                 return (ISC_R_NOMEMORY);
185         }
186
187         /* Make sure memory region is set to all 0's */
188         memset((*dbp), 0, sizeof(dns_dlzdb_t));
189
190         (*dbp)->implementation = impinfo;
191
192         /* Create a new database using implementation 'drivername'. */
193         result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
194                                              impinfo->driverarg,
195                                              &(*dbp)->dbdata));
196
197         /* mark the DLZ driver as valid */
198         if (result == ISC_R_SUCCESS) {
199                 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
200                 (*dbp)->magic = DNS_DLZ_MAGIC;
201                 isc_mem_attach(mctx, &(*dbp)->mctx);
202                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
203                               DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
204                               "DLZ driver loaded successfully.");
205                 return (ISC_R_SUCCESS);
206         } else {
207                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
208                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
209                               "DLZ driver failed to load.");
210         }
211
212         /* impinfo->methods->create failed. */
213         RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
214         isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
215         return (result);
216 }
217
218 void
219 dns_dlzdestroy(dns_dlzdb_t **dbp) {
220         isc_mem_t *mctx;
221         dns_dlzdestroy_t destroy;
222
223         /* Write debugging message to log */
224         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
225                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
226                       "Unloading DLZ driver.");
227
228         /*
229          * Perform checks to make sure data is as we expect it to be.
230          */
231         REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
232
233         /* call the drivers destroy method */
234         if ((*dbp) != NULL) {
235                 mctx = (*dbp)->mctx;
236                 destroy = (*dbp)->implementation->methods->destroy;
237                 (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
238                 /* return memory */
239                 isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
240                 isc_mem_detach(&mctx);
241         }
242
243         *dbp = NULL;
244 }
245
246
247 isc_result_t
248 dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
249                 dns_db_t **dbp)
250 {
251         dns_fixedname_t fname;
252         dns_name_t *zonename;
253         unsigned int namelabels;
254         unsigned int i;
255         isc_result_t result;
256         dns_dlzfindzone_t findzone;
257         dns_dlzdb_t *dlzdatabase;
258
259         /*
260          * Performs checks to make sure data is as we expect it to be.
261          */
262         REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
263         REQUIRE(name != NULL);
264         REQUIRE(dbp != NULL && *dbp == NULL);
265
266         /* setup a "fixed" dns name */
267         dns_fixedname_init(&fname);
268         zonename = dns_fixedname_name(&fname);
269
270         /* count the number of labels in the name */
271         namelabels = dns_name_countlabels(name);
272
273         /*
274          * loop through starting with the longest domain name and
275          * trying shorter names portions of the name until we find a
276          * match, have an error, or are below the 'minlabels'
277          * threshold.  minlabels is 0, if the standard database didn't
278          * have a zone name match.  Otherwise minlabels is the number
279          * of labels in that name.  We need to beat that for a
280          * "better" match for the DLZ database to be authoritative
281          * instead of the standard database.
282          */
283         for (i = namelabels; i > minlabels && i > 1; i--) {
284                 if (i == namelabels) {
285                         result = dns_name_copy(name, zonename, NULL);
286                         if (result != ISC_R_SUCCESS)
287                                 return (result);
288                 } else
289                         dns_name_split(name, i, NULL, zonename);
290
291                 /* ask SDLZ driver if the zone is supported */
292                 dlzdatabase = view->dlzdatabase;
293                 findzone = dlzdatabase->implementation->methods->findzone;
294                 result = (*findzone)(dlzdatabase->implementation->driverarg,
295                                      dlzdatabase->dbdata, dlzdatabase->mctx,
296                                      view->rdclass, zonename, dbp);
297                 if (result != ISC_R_NOTFOUND)
298                         return (result);
299         }
300         return (ISC_R_NOTFOUND);
301 }
302
303 /*%
304  * Registers a DLZ driver.  This basically just adds the dlz
305  * driver to the list of available drivers in the dlz_implementations list.
306  */
307 isc_result_t
308 dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
309                 void *driverarg, isc_mem_t *mctx,
310                 dns_dlzimplementation_t **dlzimp)
311 {
312
313         dns_dlzimplementation_t *dlz_imp;
314
315         /* Write debugging message to log */
316         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
317                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
318                       "Registering DLZ driver '%s'", drivername);
319
320         /*
321          * Performs checks to make sure data is as we expect it to be.
322          */
323         REQUIRE(drivername != NULL);
324         REQUIRE(methods != NULL);
325         REQUIRE(methods->create != NULL);
326         REQUIRE(methods->destroy != NULL);
327         REQUIRE(methods->findzone != NULL);
328         REQUIRE(mctx != NULL);
329         REQUIRE(dlzimp != NULL && *dlzimp == NULL);
330
331         /*
332          * initialize the dlz_implementations list, this is guaranteed
333          * to only really happen once.
334          */
335         RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
336
337         /* lock the dlz_implementations list so we can modify it. */
338         RWLOCK(&dlz_implock, isc_rwlocktype_write);
339
340         /*
341          * check that another already registered driver isn't using
342          * the same name
343          */
344         dlz_imp = dlz_impfind(drivername);
345         if (dlz_imp != NULL) {
346                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
347                               DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
348                               "DLZ Driver '%s' already registered",
349                               drivername);
350                 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
351                 return (ISC_R_EXISTS);
352         }
353
354         /*
355          * Allocate memory for a dlz_implementation object.  Error if
356          * we cannot.
357          */
358         dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
359         if (dlz_imp == NULL) {
360                 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
361                 return (ISC_R_NOMEMORY);
362         }
363
364         /* Make sure memory region is set to all 0's */
365         memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
366
367         /* Store the data passed into this method */
368         dlz_imp->name = drivername;
369         dlz_imp->methods = methods;
370         dlz_imp->mctx = NULL;
371         dlz_imp->driverarg = driverarg;
372
373         /* attach the new dlz_implementation object to a memory context */
374         isc_mem_attach(mctx, &dlz_imp->mctx);
375
376         /*
377          * prepare the dlz_implementation object to be put in a list,
378          * and append it to the list
379          */
380         ISC_LINK_INIT(dlz_imp, link);
381         ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
382
383         /* Unlock the dlz_implementations list.  */
384         RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
385
386         /* Pass back the dlz_implementation that we created. */
387         *dlzimp = dlz_imp;
388
389         return (ISC_R_SUCCESS);
390 }
391
392 /*%
393  * Helper function for dns_dlzstrtoargv().
394  * Pardon the gratuitous recursion.
395  */
396 static isc_result_t
397 dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
398                     char ***argvp, unsigned int n)
399 {
400         isc_result_t result;
401
402  restart:
403         /* Discard leading whitespace. */
404         while (*s == ' ' || *s == '\t')
405                 s++;
406
407         if (*s == '\0') {
408                 /* We have reached the end of the string. */
409                 *argcp = n;
410                 *argvp = isc_mem_get(mctx, n * sizeof(char *));
411                 if (*argvp == NULL)
412                         return (ISC_R_NOMEMORY);
413         } else {
414                 char *p = s;
415                 while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
416                         if (*p == '\n') {
417                                 *p = ' ';
418                                 goto restart;
419                         }
420                         p++;
421                 }
422
423                 /* do "grouping", items between { and } are one arg */
424                 if (*p == '{') {
425                         char *t = p;
426                         /*
427                          * shift all characters to left by 1 to get rid of '{'
428                          */
429                         while (*t != '\0') {
430                                 t++;
431                                 *(t-1) = *t;
432                         }
433                         while (*p != '\0' && *p != '}') {
434                                 p++;
435                         }
436                         /* get rid of '}' character */
437                         if (*p == '}') {
438                                 *p = '\0';
439                                 p++;
440                         }
441                         /* normal case, no "grouping" */
442                 } else if (*p != '\0')
443                         *p++ = '\0';
444
445                 result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1);
446                 if (result != ISC_R_SUCCESS)
447                         return (result);
448                 (*argvp)[n] = s;
449         }
450         return (ISC_R_SUCCESS);
451 }
452
453 /*%
454  * Tokenize the string "s" into whitespace-separated words,
455  * return the number of words in '*argcp' and an array
456  * of pointers to the words in '*argvp'.  The caller
457  * must free the array using isc_mem_put().  The string
458  * is modified in-place.
459  */
460 isc_result_t
461 dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
462                  unsigned int *argcp, char ***argvp)
463 {
464         return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0));
465 }
466
467 /*%
468  * Unregisters a DLZ driver.  This basically just removes the dlz
469  * driver from the list of available drivers in the dlz_implementations list.
470  */
471 void
472 dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
473         dns_dlzimplementation_t *dlz_imp;
474         isc_mem_t *mctx;
475
476         /* Write debugging message to log */
477         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
478                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
479                       "Unregistering DLZ driver.");
480
481         /*
482          * Performs checks to make sure data is as we expect it to be.
483          */
484         REQUIRE(dlzimp != NULL && *dlzimp != NULL);
485
486         /*
487          * initialize the dlz_implementations list, this is guaranteed
488          * to only really happen once.
489          */
490         RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
491
492         dlz_imp = *dlzimp;
493
494         /* lock the dlz_implementations list so we can modify it. */
495         RWLOCK(&dlz_implock, isc_rwlocktype_write);
496
497         /* remove the dlz_implementation object from the list */
498         ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
499         mctx = dlz_imp->mctx;
500
501         /*
502          * return the memory back to the available memory pool and
503          * remove it from the memory context.
504          */
505         isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
506         isc_mem_detach(&mctx);
507
508         /* Unlock the dlz_implementations list. */
509         RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
510 }