Coverage Report

Created: 2025-12-14 06:30

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