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