/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 | } |