Coverage Report

Created: 2026-06-15 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/dyndb.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
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
#include <isc/buffer.h>
15
#include <isc/log.h>
16
#include <isc/mem.h>
17
#include <isc/mutex.h>
18
#include <isc/region.h>
19
#include <isc/result.h>
20
#include <isc/types.h>
21
#include <isc/util.h>
22
#include <isc/uv.h>
23
24
#include <dns/dyndb.h>
25
#include <dns/types.h>
26
#include <dns/view.h>
27
#include <dns/zone.h>
28
#include <dns/zonemgr.h>
29
30
#include "dyndb_p.h"
31
32
typedef struct dyndb_implementation dyndb_implementation_t;
33
struct dyndb_implementation {
34
  isc_mem_t *mctx;
35
  uv_lib_t handle;
36
  dns_dyndb_register_t *register_func;
37
  dns_dyndb_destroy_t *destroy_func;
38
  char *name;
39
  void *inst;
40
  ISC_LINK(dyndb_implementation_t) link;
41
};
42
43
/*
44
 * List of dyndb implementations. Locked by dyndb_lock.
45
 *
46
 * These are stored here so they can be cleaned up on shutdown.
47
 * (The order in which they are stored is not important.)
48
 */
49
static ISC_LIST(dyndb_implementation_t) dyndb_implementations;
50
51
/* Locks dyndb_implementations. */
52
static isc_mutex_t dyndb_lock;
53
54
void
55
22
dns__dyndb_initialize(void) {
56
22
  isc_mutex_init(&dyndb_lock);
57
22
  ISC_LIST_INIT(dyndb_implementations);
58
22
}
59
60
void
61
0
dns__dyndb_shutdown(void) {
62
0
  isc_mutex_destroy(&dyndb_lock);
63
0
}
64
65
static dyndb_implementation_t *
66
0
impfind(const char *name) {
67
0
  ISC_LIST_FOREACH(dyndb_implementations, imp, link) {
68
0
    if (strcasecmp(name, imp->name) == 0) {
69
0
      return imp;
70
0
    }
71
0
  }
72
0
  return NULL;
73
0
}
74
75
static isc_result_t
76
load_symbol(uv_lib_t *handle, const char *filename, const char *symbol_name,
77
0
      void **symbolp) {
78
0
  void *symbol;
79
0
  int r;
80
81
0
  REQUIRE(handle != NULL);
82
0
  REQUIRE(symbolp != NULL && *symbolp == NULL);
83
84
0
  r = uv_dlsym(handle, symbol_name, &symbol);
85
0
  if (r != 0) {
86
0
    const char *errmsg = uv_dlerror(handle);
87
0
    if (errmsg == NULL) {
88
0
      errmsg = "returned function pointer is NULL";
89
0
    }
90
0
    isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
91
0
            ISC_LOG_ERROR,
92
0
            "failed to lookup symbol %s in "
93
0
            "DynDB module '%s': %s",
94
0
            symbol_name, filename, errmsg);
95
0
    return ISC_R_FAILURE;
96
0
  }
97
98
0
  *symbolp = symbol;
99
100
0
  return ISC_R_SUCCESS;
101
0
}
102
103
static void
104
unload_library(dyndb_implementation_t **impp);
105
106
static isc_result_t
107
load_library(isc_mem_t *mctx, const char *filename, const char *instname,
108
0
       dyndb_implementation_t **impp) {
109
0
  isc_result_t result;
110
0
  dyndb_implementation_t *imp = NULL;
111
0
  dns_dyndb_version_t *version_func = NULL;
112
0
  int version;
113
0
  int r;
114
115
0
  REQUIRE(impp != NULL && *impp == NULL);
116
117
0
  isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
118
0
          ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
119
0
          instname, filename);
120
121
0
  imp = isc_mem_get(mctx, sizeof(*imp));
122
0
  *imp = (dyndb_implementation_t){
123
0
    .name = isc_mem_strdup(mctx, instname),
124
0
  };
125
126
0
  isc_mem_attach(mctx, &imp->mctx);
127
128
0
  ISC_LINK_INIT(imp, link);
129
130
0
  r = uv_dlopen(filename, &imp->handle);
131
0
  if (r != 0) {
132
0
    const char *errmsg = uv_dlerror(&imp->handle);
133
0
    if (errmsg == NULL) {
134
0
      errmsg = "unknown error";
135
0
    }
136
0
    isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
137
0
            ISC_LOG_ERROR,
138
0
            "failed to dlopen() DynDB instance '%s' driver "
139
0
            "'%s': %s",
140
0
            instname, filename, errmsg);
141
0
    CLEANUP(ISC_R_FAILURE);
142
0
  }
143
144
0
  CHECK(load_symbol(&imp->handle, filename, "dyndb_version",
145
0
        (void **)&version_func));
146
147
0
  version = version_func(NULL);
148
0
  if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
149
0
      version > DNS_DYNDB_VERSION)
150
0
  {
151
0
    isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
152
0
            ISC_LOG_ERROR,
153
0
            "driver API version mismatch: %d/%d", version,
154
0
            DNS_DYNDB_VERSION);
155
0
    CLEANUP(ISC_R_FAILURE);
156
0
  }
157
158
0
  CHECK(load_symbol(&imp->handle, filename, "dyndb_init",
159
0
        (void **)&imp->register_func));
160
0
  CHECK(load_symbol(&imp->handle, filename, "dyndb_destroy",
161
0
        (void **)&imp->destroy_func));
162
163
0
  *impp = imp;
164
165
0
  return ISC_R_SUCCESS;
166
167
0
cleanup:
168
0
  isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
169
0
          ISC_LOG_ERROR,
170
0
          "failed to dynamically load DynDB instance '%s' driver "
171
0
          "'%s': %s",
172
0
          instname, filename, isc_result_totext(result));
173
174
0
  unload_library(&imp);
175
176
0
  return result;
177
0
}
178
179
static void
180
0
unload_library(dyndb_implementation_t **impp) {
181
0
  dyndb_implementation_t *imp;
182
183
0
  REQUIRE(impp != NULL && *impp != NULL);
184
185
0
  imp = *impp;
186
0
  *impp = NULL;
187
188
  /*
189
   * This is a resource leak, but there is nothing we can currently do
190
   * about it due to how configuration loading/reloading is designed.
191
   */
192
  /* uv_dlclose(&imp->handle); */
193
0
  isc_mem_free(imp->mctx, imp->name);
194
0
  isc_mem_putanddetach(&imp->mctx, imp, sizeof(*imp));
195
0
}
196
197
isc_result_t
198
dns_dyndb_load(const char *libname, const char *name, const char *parameters,
199
         const char *file, unsigned long line, isc_mem_t *mctx,
200
0
         const dns_dyndbctx_t *dctx) {
201
0
  isc_result_t result;
202
0
  dyndb_implementation_t *implementation = NULL;
203
204
0
  REQUIRE(DNS_DYNDBCTX_VALID(dctx));
205
0
  REQUIRE(name != NULL);
206
207
0
  LOCK(&dyndb_lock);
208
209
  /* duplicate instance names are not allowed */
210
0
  if (impfind(name) != NULL) {
211
0
    CLEANUP(ISC_R_EXISTS);
212
0
  }
213
214
0
  CHECK(load_library(mctx, libname, name, &implementation));
215
0
  CHECK(implementation->register_func(mctx, name, parameters, file, line,
216
0
              dctx, &implementation->inst));
217
218
0
  ISC_LIST_APPEND(dyndb_implementations, implementation, link);
219
0
  result = ISC_R_SUCCESS;
220
221
0
cleanup:
222
0
  if (result != ISC_R_SUCCESS) {
223
0
    if (implementation != NULL) {
224
0
      unload_library(&implementation);
225
0
    }
226
0
  }
227
228
0
  UNLOCK(&dyndb_lock);
229
0
  return result;
230
0
}
231
232
void
233
0
dns_dyndb_cleanup(void) {
234
0
  dyndb_implementation_t *elem;
235
0
  dyndb_implementation_t *prev;
236
237
0
  LOCK(&dyndb_lock);
238
0
  elem = ISC_LIST_TAIL(dyndb_implementations);
239
0
  while (elem != NULL) {
240
0
    prev = ISC_LIST_PREV(elem, link);
241
0
    ISC_LIST_UNLINK(dyndb_implementations, elem, link);
242
0
    isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
243
0
            ISC_LOG_INFO, "unloading DynDB instance '%s'",
244
0
            elem->name);
245
0
    elem->destroy_func(&elem->inst);
246
0
    ENSURE(elem->inst == NULL);
247
0
    unload_library(&elem);
248
0
    elem = prev;
249
0
  }
250
0
  UNLOCK(&dyndb_lock);
251
0
}
252
253
void
254
dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, dns_view_t *view,
255
0
        dns_zonemgr_t *zmgr, dns_dyndbctx_t **dctxp) {
256
0
  dns_dyndbctx_t *dctx;
257
258
0
  REQUIRE(dctxp != NULL && *dctxp == NULL);
259
260
0
  dctx = isc_mem_get(mctx, sizeof(*dctx));
261
0
  *dctx = (dns_dyndbctx_t){
262
0
    .hashinit = hashinit,
263
0
  };
264
265
0
  if (view != NULL) {
266
0
    dns_view_attach(view, &dctx->view);
267
0
  }
268
0
  if (zmgr != NULL) {
269
0
    dns_zonemgr_attach(zmgr, &dctx->zmgr);
270
0
  }
271
272
0
  isc_mem_attach(mctx, &dctx->mctx);
273
0
  dctx->magic = DNS_DYNDBCTX_MAGIC;
274
275
0
  *dctxp = dctx;
276
0
}
277
278
void
279
0
dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
280
0
  dns_dyndbctx_t *dctx;
281
282
0
  REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
283
284
0
  dctx = *dctxp;
285
0
  *dctxp = NULL;
286
287
0
  dctx->magic = 0;
288
289
0
  if (dctx->view != NULL) {
290
0
    dns_view_detach(&dctx->view);
291
0
  }
292
0
  if (dctx->zmgr != NULL) {
293
0
    dns_zonemgr_detach(&dctx->zmgr);
294
0
  }
295
296
  isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
297
0
}