Coverage Report

Created: 2025-11-24 06:17

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