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