1 /*
2 * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*
13 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
14 *
15 * Permission to use, copy, modify, and distribute this software for any
16 * purpose with or without fee is hereby granted, provided that the
17 * above copyright notice and this permission notice appear in all
18 * copies.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
21 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
23 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
24 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
25 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
26 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
27 * USE OR PERFORMANCE OF THIS SOFTWARE.
28 *
29 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
30 * conceived and contributed by Rob Butler.
31 *
32 * Permission to use, copy, modify, and distribute this software for any
33 * purpose with or without fee is hereby granted, provided that the
34 * above copyright notice and this permission notice appear in all
35 * copies.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
38 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
40 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
41 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
42 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
43 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
44 * USE OR PERFORMANCE OF THIS SOFTWARE.
45 */
46
47
48 /*! \file */
49
50 /***
51 *** Imports
52 ***/
53
54 #include <config.h>
55
56 #include <stdbool.h>
57
58 #include <dns/db.h>
59 #include <dns/dlz.h>
60 #include <dns/fixedname.h>
61 #include <dns/log.h>
62 #include <dns/master.h>
63 #include <dns/ssu.h>
64 #include <dns/zone.h>
65
66
67 #include <isc/buffer.h>
68 #include <isc/commandline.h>
69 #include <isc/magic.h>
70 #include <isc/mem.h>
71 #include <isc/once.h>
72 #include <isc/rwlock.h>
73 #include <isc/string.h>
74 #include <isc/util.h>
75
76 /***
77 *** Supported DLZ DB Implementations Registry
78 ***/
79
80 static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
81 static isc_rwlock_t dlz_implock;
82 static isc_once_t once = ISC_ONCE_INIT;
83
84 static void
dlz_initialize(void)85 dlz_initialize(void) {
86 RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
87 ISC_LIST_INIT(dlz_implementations);
88 }
89
90 /*%
91 * Searches the dlz_implementations list for a driver matching name.
92 */
93 static inline dns_dlzimplementation_t *
dlz_impfind(const char * name)94 dlz_impfind(const char *name) {
95 dns_dlzimplementation_t *imp;
96
97 for (imp = ISC_LIST_HEAD(dlz_implementations);
98 imp != NULL;
99 imp = ISC_LIST_NEXT(imp, link))
100 if (strcasecmp(name, imp->name) == 0)
101 return (imp);
102 return (NULL);
103 }
104
105 /***
106 *** Basic DLZ Methods
107 ***/
108
109 isc_result_t
dns_dlzallowzonexfr(dns_view_t * view,dns_name_t * name,isc_sockaddr_t * clientaddr,dns_db_t ** dbp)110 dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
111 isc_sockaddr_t *clientaddr, dns_db_t **dbp)
112 {
113 isc_result_t result = ISC_R_NOTFOUND;
114 dns_dlzallowzonexfr_t allowzonexfr;
115 dns_dlzdb_t *dlzdb;
116
117 /*
118 * Performs checks to make sure data is as we expect it to be.
119 */
120 REQUIRE(name != NULL);
121 REQUIRE(dbp != NULL && *dbp == NULL);
122
123 /*
124 * Find a driver in which the zone exists and transfer is supported
125 */
126 for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
127 dlzdb != NULL;
128 dlzdb = ISC_LIST_NEXT(dlzdb, link))
129 {
130 REQUIRE(DNS_DLZ_VALID(dlzdb));
131
132 allowzonexfr = dlzdb->implementation->methods->allowzonexfr;
133 result = (*allowzonexfr)(dlzdb->implementation->driverarg,
134 dlzdb->dbdata, dlzdb->mctx,
135 view->rdclass, name, clientaddr, dbp);
136
137 /*
138 * if ISC_R_NOPERM, we found the right database but
139 * the zone may not transfer.
140 */
141 if (result == ISC_R_SUCCESS || result == ISC_R_NOPERM)
142 return (result);
143 }
144
145 if (result == ISC_R_NOTIMPLEMENTED)
146 result = ISC_R_NOTFOUND;
147
148 return (result);
149 }
150
151 isc_result_t
dns_dlzcreate(isc_mem_t * mctx,const char * dlzname,const char * drivername,unsigned int argc,char * argv[],dns_dlzdb_t ** dbp)152 dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
153 unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
154 {
155 dns_dlzimplementation_t *impinfo;
156 isc_result_t result;
157 dns_dlzdb_t *db = NULL;
158
159 /*
160 * initialize the dlz_implementations list, this is guaranteed
161 * to only really happen once.
162 */
163 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
164
165 /*
166 * Performs checks to make sure data is as we expect it to be.
167 */
168 REQUIRE(dbp != NULL && *dbp == NULL);
169 REQUIRE(dlzname != NULL);
170 REQUIRE(drivername != NULL);
171 REQUIRE(mctx != NULL);
172
173 /* write log message */
174 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
175 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
176 "Loading '%s' using driver %s", dlzname, drivername);
177
178 /* lock the dlz_implementations list so we can search it. */
179 RWLOCK(&dlz_implock, isc_rwlocktype_read);
180
181 /* search for the driver implementation */
182 impinfo = dlz_impfind(drivername);
183 if (impinfo == NULL) {
184 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
185
186 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
187 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
188 "unsupported DLZ database driver '%s'."
189 " %s not loaded.",
190 drivername, dlzname);
191
192 return (ISC_R_NOTFOUND);
193 }
194
195 /* Allocate memory to hold the DLZ database driver */
196 db = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
197 if (db == NULL) {
198 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
199 return (ISC_R_NOMEMORY);
200 }
201
202 /* Make sure memory region is set to all 0's */
203 memset(db, 0, sizeof(dns_dlzdb_t));
204
205 ISC_LINK_INIT(db, link);
206 db->implementation = impinfo;
207 if (dlzname != NULL)
208 db->dlzname = isc_mem_strdup(mctx, dlzname);
209
210 /* Create a new database using implementation 'drivername'. */
211 result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
212 impinfo->driverarg,
213 &db->dbdata));
214
215 /* mark the DLZ driver as valid */
216 if (result == ISC_R_SUCCESS) {
217 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
218 db->magic = DNS_DLZ_MAGIC;
219 isc_mem_attach(mctx, &db->mctx);
220 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
221 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
222 "DLZ driver loaded successfully.");
223 *dbp = db;
224 return (ISC_R_SUCCESS);
225 } else {
226 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
227 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
228 "DLZ driver failed to load.");
229 }
230
231 /* impinfo->methods->create failed. */
232 RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
233 isc_mem_put(mctx, db, sizeof(dns_dlzdb_t));
234 return (result);
235 }
236
237 void
dns_dlzdestroy(dns_dlzdb_t ** dbp)238 dns_dlzdestroy(dns_dlzdb_t **dbp) {
239 dns_dlzdestroy_t destroy;
240 dns_dlzdb_t *db;
241
242 /* Write debugging message to log */
243 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
244 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
245 "Unloading DLZ driver.");
246
247 /*
248 * Perform checks to make sure data is as we expect it to be.
249 */
250 REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
251
252 db = *dbp;
253 *dbp = NULL;
254
255 if (db->ssutable != NULL)
256 dns_ssutable_detach(&db->ssutable);
257
258 /* call the drivers destroy method */
259 if (db->dlzname != NULL)
260 isc_mem_free(db->mctx, db->dlzname);
261 destroy = db->implementation->methods->destroy;
262 (*destroy)(db->implementation->driverarg, db->dbdata);
263 /* return memory and detach */
264 isc_mem_putanddetach(&db->mctx, db, sizeof(dns_dlzdb_t));
265 }
266
267 /*%
268 * Registers a DLZ driver. This basically just adds the dlz
269 * driver to the list of available drivers in the dlz_implementations list.
270 */
271 isc_result_t
dns_dlzregister(const char * drivername,const dns_dlzmethods_t * methods,void * driverarg,isc_mem_t * mctx,dns_dlzimplementation_t ** dlzimp)272 dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
273 void *driverarg, isc_mem_t *mctx,
274 dns_dlzimplementation_t **dlzimp)
275 {
276
277 dns_dlzimplementation_t *dlz_imp;
278
279 /* Write debugging message to log */
280 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
281 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
282 "Registering DLZ driver '%s'", drivername);
283
284 /*
285 * Performs checks to make sure data is as we expect it to be.
286 */
287 REQUIRE(drivername != NULL);
288 REQUIRE(methods != NULL);
289 REQUIRE(methods->create != NULL);
290 REQUIRE(methods->destroy != NULL);
291 REQUIRE(methods->findzone != NULL);
292 REQUIRE(mctx != NULL);
293 REQUIRE(dlzimp != NULL && *dlzimp == NULL);
294
295 /*
296 * initialize the dlz_implementations list, this is guaranteed
297 * to only really happen once.
298 */
299 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
300
301 /* lock the dlz_implementations list so we can modify it. */
302 RWLOCK(&dlz_implock, isc_rwlocktype_write);
303
304 /*
305 * check that another already registered driver isn't using
306 * the same name
307 */
308 dlz_imp = dlz_impfind(drivername);
309 if (dlz_imp != NULL) {
310 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
311 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
312 "DLZ Driver '%s' already registered",
313 drivername);
314 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
315 return (ISC_R_EXISTS);
316 }
317
318 /*
319 * Allocate memory for a dlz_implementation object. Error if
320 * we cannot.
321 */
322 dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
323 if (dlz_imp == NULL) {
324 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
325 return (ISC_R_NOMEMORY);
326 }
327
328 /* Make sure memory region is set to all 0's */
329 memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
330
331 /* Store the data passed into this method */
332 dlz_imp->name = drivername;
333 dlz_imp->methods = methods;
334 dlz_imp->mctx = NULL;
335 dlz_imp->driverarg = driverarg;
336
337 /* attach the new dlz_implementation object to a memory context */
338 isc_mem_attach(mctx, &dlz_imp->mctx);
339
340 /*
341 * prepare the dlz_implementation object to be put in a list,
342 * and append it to the list
343 */
344 ISC_LINK_INIT(dlz_imp, link);
345 ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
346
347 /* Unlock the dlz_implementations list. */
348 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
349
350 /* Pass back the dlz_implementation that we created. */
351 *dlzimp = dlz_imp;
352
353 return (ISC_R_SUCCESS);
354 }
355
356 /*%
357 * Tokenize the string "s" into whitespace-separated words,
358 * return the number of words in '*argcp' and an array
359 * of pointers to the words in '*argvp'. The caller
360 * must free the array using isc_mem_put(). The string
361 * is modified in-place.
362 */
363 isc_result_t
dns_dlzstrtoargv(isc_mem_t * mctx,char * s,unsigned int * argcp,char *** argvp)364 dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
365 unsigned int *argcp, char ***argvp)
366 {
367 return(isc_commandline_strtoargv(mctx, s, argcp, argvp, 0));
368 }
369
370 /*%
371 * Unregisters a DLZ driver. This basically just removes the dlz
372 * driver from the list of available drivers in the dlz_implementations list.
373 */
374 void
dns_dlzunregister(dns_dlzimplementation_t ** dlzimp)375 dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
376 dns_dlzimplementation_t *dlz_imp;
377 isc_mem_t *mctx;
378
379 /* Write debugging message to log */
380 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
381 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
382 "Unregistering DLZ driver.");
383
384 /*
385 * Performs checks to make sure data is as we expect it to be.
386 */
387 REQUIRE(dlzimp != NULL && *dlzimp != NULL);
388
389 /*
390 * initialize the dlz_implementations list, this is guaranteed
391 * to only really happen once.
392 */
393 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
394
395 dlz_imp = *dlzimp;
396
397 /* lock the dlz_implementations list so we can modify it. */
398 RWLOCK(&dlz_implock, isc_rwlocktype_write);
399
400 /* remove the dlz_implementation object from the list */
401 ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
402 mctx = dlz_imp->mctx;
403
404 /*
405 * Return the memory back to the available memory pool and
406 * remove it from the memory context.
407 */
408 isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
409 isc_mem_detach(&mctx);
410
411 /* Unlock the dlz_implementations list. */
412 RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
413 }
414
415 /*
416 * Create a writeable DLZ zone. This can be called by DLZ drivers
417 * during configure() to create a zone that can be updated. The zone
418 * type is set to dns_zone_dlz, which is equivalent to a master zone
419 *
420 * This function uses a callback setup in dns_dlzconfigure() to call
421 * into the server zone code to setup the remaining pieces of server
422 * specific functionality on the zone
423 */
424 isc_result_t
dns_dlz_writeablezone(dns_view_t * view,dns_dlzdb_t * dlzdb,const char * zone_name)425 dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb,
426 const char *zone_name)
427 {
428 dns_zone_t *zone = NULL;
429 dns_zone_t *dupzone = NULL;
430 isc_result_t result;
431 isc_buffer_t buffer;
432 dns_fixedname_t fixorigin;
433 dns_name_t *origin;
434
435 REQUIRE(DNS_DLZ_VALID(dlzdb));
436
437 REQUIRE(dlzdb->configure_callback != NULL);
438
439 isc_buffer_constinit(&buffer, zone_name, strlen(zone_name));
440 isc_buffer_add(&buffer, strlen(zone_name));
441 dns_fixedname_init(&fixorigin);
442 result = dns_name_fromtext(dns_fixedname_name(&fixorigin),
443 &buffer, dns_rootname, 0, NULL);
444 if (result != ISC_R_SUCCESS)
445 goto cleanup;
446 origin = dns_fixedname_name(&fixorigin);
447
448 if (!dlzdb->search) {
449 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
450 DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
451 "DLZ %s has 'search no;', but attempted to "
452 "register writeable zone %s.",
453 dlzdb->dlzname, zone_name);
454 result = ISC_R_SUCCESS;
455 goto cleanup;
456 }
457
458 /* See if the zone already exists */
459 result = dns_view_findzone(view, origin, &dupzone);
460 if (result == ISC_R_SUCCESS) {
461 dns_zone_detach(&dupzone);
462 result = ISC_R_EXISTS;
463 goto cleanup;
464 }
465 INSIST(dupzone == NULL);
466
467 /* Create it */
468 result = dns_zone_create(&zone, view->mctx);
469 if (result != ISC_R_SUCCESS)
470 goto cleanup;
471 result = dns_zone_setorigin(zone, origin);
472 if (result != ISC_R_SUCCESS)
473 goto cleanup;
474 dns_zone_setview(zone, view);
475
476 dns_zone_setadded(zone, true);
477
478 if (dlzdb->ssutable == NULL) {
479 result = dns_ssutable_createdlz(dlzdb->mctx,
480 &dlzdb->ssutable, dlzdb);
481 if (result != ISC_R_SUCCESS)
482 goto cleanup;
483 }
484 dns_zone_setssutable(zone, dlzdb->ssutable);
485
486 result = dlzdb->configure_callback(view, dlzdb, zone);
487 if (result != ISC_R_SUCCESS)
488 goto cleanup;
489
490 result = dns_view_addzone(view, zone);
491
492
493 cleanup:
494 if (zone != NULL)
495 dns_zone_detach(&zone);
496
497 return (result);
498 }
499
500 /*%
501 * Configure a DLZ driver. This is optional, and if supplied gives
502 * the backend an opportunity to configure parameters related to DLZ.
503 */
504 isc_result_t
dns_dlzconfigure(dns_view_t * view,dns_dlzdb_t * dlzdb,dlzconfigure_callback_t callback)505 dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
506 dlzconfigure_callback_t callback)
507 {
508 dns_dlzimplementation_t *impl;
509 isc_result_t result;
510
511 REQUIRE(DNS_DLZ_VALID(dlzdb));
512 REQUIRE(dlzdb->implementation != NULL);
513
514 impl = dlzdb->implementation;
515
516 if (impl->methods->configure == NULL)
517 return (ISC_R_SUCCESS);
518
519 dlzdb->configure_callback = callback;
520
521 result = impl->methods->configure(impl->driverarg, dlzdb->dbdata,
522 view, dlzdb);
523 return (result);
524 }
525
526 bool
dns_dlz_ssumatch(dns_dlzdb_t * dlzdatabase,dns_name_t * signer,dns_name_t * name,isc_netaddr_t * tcpaddr,dns_rdatatype_t type,const dst_key_t * key)527 dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, dns_name_t *signer,
528 dns_name_t *name, isc_netaddr_t *tcpaddr,
529 dns_rdatatype_t type, const dst_key_t *key)
530 {
531 dns_dlzimplementation_t *impl;
532 bool r;
533
534 REQUIRE(dlzdatabase != NULL);
535 REQUIRE(dlzdatabase->implementation != NULL);
536 REQUIRE(dlzdatabase->implementation->methods != NULL);
537 impl = dlzdatabase->implementation;
538
539 if (impl->methods->ssumatch == NULL) {
540 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
541 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
542 "No ssumatch method for DLZ database");
543 return (false);
544 }
545
546 r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
547 impl->driverarg, dlzdatabase->dbdata);
548 return (r);
549 }
550