Coverage Report

Created: 2025-11-09 06:45

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