1 /*	$NetBSD: dlz_dlopen_driver.c,v 1.4 2015/07/08 17:28:55 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2011-2014  Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* Id: dlz_dlopen_driver.c,v 1.5 2011/10/14 00:52:32 marka Exp  */
20 
21 #include <config.h>
22 
23 #include <windows.h>
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #include <dns/log.h>
30 #include <dns/result.h>
31 #include <dns/dlz_dlopen.h>
32 
33 #include <isc/mem.h>
34 #include <isc/print.h>
35 #include <isc/result.h>
36 #include <isc/util.h>
37 
38 #include <named/globals.h>
39 
40 #include <dlz/dlz_dlopen_driver.h>
41 
42 #ifdef ISC_DLZ_DLOPEN
43 static dns_sdlzimplementation_t *dlz_dlopen = NULL;
44 
45 
46 typedef struct dlopen_data {
47 	isc_mem_t *mctx;
48 	char *dl_path;
49 	char *dlzname;
50 	HMODULE dl_handle;
51 	void *dbdata;
52 	unsigned int flags;
53 	isc_mutex_t lock;
54 	int version;
55 	isc_boolean_t in_configure;
56 
57 	dlz_dlopen_version_t *dlz_version;
58 	dlz_dlopen_create_t *dlz_create;
59 	dlz_dlopen_findzonedb_t *dlz_findzonedb;
60 	dlz_dlopen_lookup_t *dlz_lookup;
61 	dlz_dlopen_authority_t *dlz_authority;
62 	dlz_dlopen_allnodes_t *dlz_allnodes;
63 	dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
64 	dlz_dlopen_newversion_t *dlz_newversion;
65 	dlz_dlopen_closeversion_t *dlz_closeversion;
66 	dlz_dlopen_configure_t *dlz_configure;
67 	dlz_dlopen_ssumatch_t *dlz_ssumatch;
68 	dlz_dlopen_addrdataset_t *dlz_addrdataset;
69 	dlz_dlopen_subrdataset_t *dlz_subrdataset;
70 	dlz_dlopen_delrdataset_t *dlz_delrdataset;
71 	dlz_dlopen_destroy_t *dlz_destroy;
72 } dlopen_data_t;
73 
74 /* Modules can choose whether they are lock-safe or not. */
75 #define MAYBE_LOCK(cd) \
76 	do { \
77 		if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
78 		    cd->in_configure == ISC_FALSE) \
79 			LOCK(&cd->lock); \
80 	} while (/*CONSTCOND*/0)
81 
82 #define MAYBE_UNLOCK(cd) \
83 	do { \
84 		if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
85 		    cd->in_configure == ISC_FALSE) \
86 			UNLOCK(&cd->lock); \
87 	} while (/*CONSTCOND*/0)
88 
89 /*
90  * Log a message at the given level.
91  */
92 static void dlopen_log(int level, const char *fmt, ...)
93 {
94 	va_list ap;
95 	va_start(ap, fmt);
96 	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
97 		       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
98 		       fmt, ap);
99 	va_end(ap);
100 }
101 
102 /*
103  * SDLZ methods
104  */
105 
106 static isc_result_t
107 dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
108 		    dns_sdlzallnodes_t *allnodes)
109 {
110 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
111 	isc_result_t result;
112 
113 
114 	UNUSED(driverarg);
115 
116 	if (cd->dlz_allnodes == NULL) {
117 		return (ISC_R_NOPERM);
118 	}
119 
120 	MAYBE_LOCK(cd);
121 	result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
122 	MAYBE_UNLOCK(cd);
123 	return (result);
124 }
125 
126 
127 static isc_result_t
128 dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
129 			const char *client)
130 {
131 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
132 	isc_result_t result;
133 
134 	UNUSED(driverarg);
135 
136 
137 	if (cd->dlz_allowzonexfr == NULL) {
138 		return (ISC_R_NOPERM);
139 	}
140 
141 	MAYBE_LOCK(cd);
142 	result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
143 	MAYBE_UNLOCK(cd);
144 	return (result);
145 }
146 
147 static isc_result_t
148 dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
149 		   dns_sdlzlookup_t *lookup)
150 {
151 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
152 	isc_result_t result;
153 
154 	UNUSED(driverarg);
155 
156 	if (cd->dlz_authority == NULL) {
157 		return (ISC_R_NOTIMPLEMENTED);
158 	}
159 
160 	MAYBE_LOCK(cd);
161 	result = cd->dlz_authority(zone, cd->dbdata, lookup);
162 	MAYBE_UNLOCK(cd);
163 	return (result);
164 }
165 
166 static isc_result_t
167 dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name,
168 		      dns_clientinfomethods_t *methods,
169 		      dns_clientinfo_t *clientinfo)
170 {
171 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
172 	isc_result_t result;
173 
174 	UNUSED(driverarg);
175 
176 	MAYBE_LOCK(cd);
177 	result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo);
178 	MAYBE_UNLOCK(cd);
179 	return (result);
180 }
181 
182 
183 static isc_result_t
184 dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
185 		  void *dbdata, dns_sdlzlookup_t *lookup,
186 		  dns_clientinfomethods_t *methods,
187 		  dns_clientinfo_t *clientinfo)
188 {
189 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
190 	isc_result_t result;
191 
192 	UNUSED(driverarg);
193 
194 	MAYBE_LOCK(cd);
195 	result = cd->dlz_lookup(zone, name, cd->dbdata, lookup,
196 				methods, clientinfo);
197 	MAYBE_UNLOCK(cd);
198 	return (result);
199 }
200 
201 /*
202  * Load a symbol from the library
203  */
204 static void *
205 dl_load_symbol(dlopen_data_t *cd, const char *symbol, isc_boolean_t mandatory) {
206 	void *ptr = GetProcAddress(cd->dl_handle, symbol);
207 	if (ptr == NULL && mandatory) {
208 		dlopen_log(ISC_LOG_ERROR,
209 			   "dlz_dlopen: library '%s' is missing "
210 			   "required symbol '%s'", cd->dl_path, symbol);
211 	}
212 	return (ptr);
213 }
214 
215 /*
216  * Called at startup for each dlopen zone in named.conf
217  */
218 static isc_result_t
219 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
220 		  void *driverarg, void **dbdata)
221 {
222 	dlopen_data_t *cd;
223 	isc_mem_t *mctx = NULL;
224 	isc_result_t result = ISC_R_FAILURE;
225 	isc_boolean_t triedload = ISC_FALSE;
226 
227 	UNUSED(driverarg);
228 
229 	if (argc < 2) {
230 		dlopen_log(ISC_LOG_ERROR,
231 			   "dlz_dlopen driver for '%s' needs a path to "
232 			   "the shared library", dlzname);
233 		return (ISC_R_FAILURE);
234 	}
235 
236 	isc_mem_create(0, 0, &mctx);
237 
238 	cd = isc_mem_get(mctx, sizeof(*cd));
239 	if (cd == NULL) {
240 		isc_mem_destroy(&mctx);
241 		return (ISC_R_NOMEMORY);
242 	}
243 	memset(cd, 0, sizeof(*cd));
244 
245 	cd->mctx = mctx;
246 
247 	cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
248 	if (cd->dl_path == NULL) {
249 		result = ISC_R_NOMEMORY;
250 		goto failed;
251 	}
252 
253 	cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
254 	if (cd->dlzname == NULL) {
255 		result = ISC_R_NOMEMORY;
256 		goto failed;
257 	}
258 
259 	triedload = ISC_TRUE;
260 
261 	/* Initialize the lock */
262 	result = isc_mutex_init(&cd->lock);
263 	if (result != ISC_R_SUCCESS)
264 		goto failed;
265 
266 	/* Open the library */
267 	cd->dl_handle = LoadLibraryA(cd->dl_path);
268 	if (cd->dl_handle == NULL) {
269 		unsigned int error = GetLastError();
270 
271 		dlopen_log(ISC_LOG_ERROR,
272 			   "dlz_dlopen failed to open library '%s' - %u",
273 			   cd->dl_path, error);
274 		result = ISC_R_FAILURE;
275 		goto cleanup_lock;
276 	}
277 
278 	/* Find the symbols */
279 	cd->dlz_version = (dlz_dlopen_version_t *)
280 		dl_load_symbol(cd, "dlz_version", ISC_TRUE);
281 	cd->dlz_create = (dlz_dlopen_create_t *)
282 		dl_load_symbol(cd, "dlz_create", ISC_TRUE);
283 	cd->dlz_lookup = (dlz_dlopen_lookup_t *)
284 		dl_load_symbol(cd, "dlz_lookup", ISC_TRUE);
285 	cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
286 		dl_load_symbol(cd, "dlz_findzonedb", ISC_TRUE);
287 
288 	if (cd->dlz_create == NULL ||
289 	    cd->dlz_version == NULL ||
290 	    cd->dlz_lookup == NULL ||
291 	    cd->dlz_findzonedb == NULL)
292 	{
293 		/* We're missing a required symbol */
294 		result = ISC_R_FAILURE;
295 		goto cleanup_lock;
296 	}
297 
298 	cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
299 		dl_load_symbol(cd, "dlz_allowzonexfr", ISC_FALSE);
300 	cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
301 		dl_load_symbol(cd, "dlz_allnodes",
302 			       ISC_TF(cd->dlz_allowzonexfr != NULL));
303 	cd->dlz_authority = (dlz_dlopen_authority_t *)
304 		dl_load_symbol(cd, "dlz_authority", ISC_FALSE);
305 	cd->dlz_newversion = (dlz_dlopen_newversion_t *)
306 		dl_load_symbol(cd, "dlz_newversion", ISC_FALSE);
307 	cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
308 		dl_load_symbol(cd, "dlz_closeversion",
309 			       ISC_TF(cd->dlz_newversion != NULL));
310 	cd->dlz_configure = (dlz_dlopen_configure_t *)
311 		dl_load_symbol(cd, "dlz_configure", ISC_FALSE);
312 	cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
313 		dl_load_symbol(cd, "dlz_ssumatch", ISC_FALSE);
314 	cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
315 		dl_load_symbol(cd, "dlz_addrdataset", ISC_FALSE);
316 	cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
317 		dl_load_symbol(cd, "dlz_subrdataset", ISC_FALSE);
318 	cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
319 		dl_load_symbol(cd, "dlz_delrdataset", ISC_FALSE);
320 
321 	/* Check the version of the API is the same */
322 	cd->version = cd->dlz_version(&cd->flags);
323 	if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) ||
324 	    cd->version > DLZ_DLOPEN_VERSION)
325 	{
326 		dlopen_log(ISC_LOG_ERROR,
327 			   "dlz_dlopen: %s: incorrect driver API version %d, "
328 			   "requires %d",
329 			   cd->dl_path, cd->version, DLZ_DLOPEN_VERSION);
330 		result = ISC_R_FAILURE;
331 		goto cleanup_lock;
332 	}
333 
334 	/*
335 	 * Call the library's create function. Note that this is an
336 	 * extended version of dlz create, with the addition of
337 	 * named function pointers for helper functions that the
338 	 * driver will need. This avoids the need for the backend to
339 	 * link the BIND9 libraries
340 	 */
341 	MAYBE_LOCK(cd);
342 	result = cd->dlz_create(dlzname, argc-1, argv+1,
343 				&cd->dbdata,
344 				"log", dlopen_log,
345 				"putrr", dns_sdlz_putrr,
346 				"putnamedrr", dns_sdlz_putnamedrr,
347 				"writeable_zone", dns_dlz_writeablezone,
348 				NULL);
349 	MAYBE_UNLOCK(cd);
350 	if (result != ISC_R_SUCCESS)
351 		goto cleanup_lock;
352 
353 	*dbdata = cd;
354 
355 	return (ISC_R_SUCCESS);
356 
357 cleanup_lock:
358 	DESTROYLOCK(&cd->lock);
359 failed:
360 	dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
361 	if (cd->dl_path)
362 		isc_mem_free(mctx, cd->dl_path);
363 	if (cd->dlzname)
364 		isc_mem_free(mctx, cd->dlzname);
365 	if (triedload)
366 		(void) isc_mutex_destroy(&cd->lock);
367 	if (cd->dl_handle)
368 		FreeLibrary(cd->dl_handle);
369 	isc_mem_put(mctx, cd, sizeof(*cd));
370 	isc_mem_destroy(&mctx);
371 	return (result);
372 }
373 
374 
375 /*
376  * Called when bind is shutting down
377  */
378 static void
379 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
380 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
381 	isc_mem_t *mctx;
382 
383 	UNUSED(driverarg);
384 
385 	if (cd->dlz_destroy) {
386 		MAYBE_LOCK(cd);
387 		cd->dlz_destroy(cd->dbdata);
388 		MAYBE_UNLOCK(cd);
389 	}
390 
391 	if (cd->dl_path)
392 		isc_mem_free(cd->mctx, cd->dl_path);
393 	if (cd->dlzname)
394 		isc_mem_free(cd->mctx, cd->dlzname);
395 
396 	if (cd->dl_handle)
397 		FreeLibrary(cd->dl_handle);
398 
399 	DESTROYLOCK(&cd->lock);
400 
401 	mctx = cd->mctx;
402 	isc_mem_put(mctx, cd, sizeof(*cd));
403 	isc_mem_destroy(&mctx);
404 }
405 
406 /*
407  * Called to start a transaction
408  */
409 static isc_result_t
410 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
411 		      void **versionp)
412 {
413 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
414 	isc_result_t result;
415 
416 	UNUSED(driverarg);
417 
418 	if (cd->dlz_newversion == NULL)
419 		return (ISC_R_NOTIMPLEMENTED);
420 
421 	MAYBE_LOCK(cd);
422 	result = cd->dlz_newversion(zone, cd->dbdata, versionp);
423 	MAYBE_UNLOCK(cd);
424 	return (result);
425 }
426 
427 /*
428  * Called to end a transaction
429  */
430 static void
431 dlopen_dlz_closeversion(const char *zone, isc_boolean_t commit,
432 			void *driverarg, void *dbdata, void **versionp)
433 {
434 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
435 
436 	UNUSED(driverarg);
437 
438 	if (cd->dlz_newversion == NULL) {
439 		*versionp = NULL;
440 		return;
441 	}
442 
443 	MAYBE_LOCK(cd);
444 	cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
445 	MAYBE_UNLOCK(cd);
446 }
447 
448 /*
449  * Called on startup to configure any writeable zones
450  */
451 static isc_result_t
452 dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
453 		     void *driverarg, void *dbdata)
454 {
455 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
456 	isc_result_t result;
457 
458 	UNUSED(driverarg);
459 
460 	if (cd->dlz_configure == NULL)
461 		return (ISC_R_SUCCESS);
462 
463 	MAYBE_LOCK(cd);
464 	cd->in_configure = ISC_TRUE;
465 	result = cd->dlz_configure(view, dlzdb, cd->dbdata);
466 	cd->in_configure = ISC_FALSE;
467 	MAYBE_UNLOCK(cd);
468 
469 	return (result);
470 }
471 
472 
473 /*
474  * Check for authority to change a name
475  */
476 static isc_boolean_t
477 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
478 		    const char *type, const char *key, isc_uint32_t keydatalen,
479 		    unsigned char *keydata, void *driverarg, void *dbdata)
480 {
481 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
482 	isc_boolean_t ret;
483 
484 	UNUSED(driverarg);
485 
486 	if (cd->dlz_ssumatch == NULL)
487 		return (ISC_FALSE);
488 
489 	MAYBE_LOCK(cd);
490 	ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
491 			       keydata, cd->dbdata);
492 	MAYBE_UNLOCK(cd);
493 
494 	return (ret);
495 }
496 
497 
498 /*
499  * Add an rdataset
500  */
501 static isc_result_t
502 dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
503 		       void *driverarg, void *dbdata, void *version)
504 {
505 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
506 	isc_result_t result;
507 
508 	UNUSED(driverarg);
509 
510 	if (cd->dlz_addrdataset == NULL)
511 		return (ISC_R_NOTIMPLEMENTED);
512 
513 	MAYBE_LOCK(cd);
514 	result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
515 	MAYBE_UNLOCK(cd);
516 
517 	return (result);
518 }
519 
520 /*
521  * Subtract an rdataset
522  */
523 static isc_result_t
524 dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
525 		       void *driverarg, void *dbdata, void *version)
526 {
527 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
528 	isc_result_t result;
529 
530 	UNUSED(driverarg);
531 
532 	if (cd->dlz_subrdataset == NULL)
533 		return (ISC_R_NOTIMPLEMENTED);
534 
535 	MAYBE_LOCK(cd);
536 	result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
537 	MAYBE_UNLOCK(cd);
538 
539 	return (result);
540 }
541 
542 /*
543   delete a rdataset
544  */
545 static isc_result_t
546 dlopen_dlz_delrdataset(const char *name, const char *type,
547 		       void *driverarg, void *dbdata, void *version)
548 {
549 	dlopen_data_t *cd = (dlopen_data_t *) dbdata;
550 	isc_result_t result;
551 
552 	UNUSED(driverarg);
553 
554 	if (cd->dlz_delrdataset == NULL)
555 		return (ISC_R_NOTIMPLEMENTED);
556 
557 	MAYBE_LOCK(cd);
558 	result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
559 	MAYBE_UNLOCK(cd);
560 
561 	return (result);
562 }
563 
564 
565 static dns_sdlzmethods_t dlz_dlopen_methods = {
566 	dlopen_dlz_create,
567 	dlopen_dlz_destroy,
568 	dlopen_dlz_findzonedb,
569 	dlopen_dlz_lookup,
570 	dlopen_dlz_authority,
571 	dlopen_dlz_allnodes,
572 	dlopen_dlz_allowzonexfr,
573 	dlopen_dlz_newversion,
574 	dlopen_dlz_closeversion,
575 	dlopen_dlz_configure,
576 	dlopen_dlz_ssumatch,
577 	dlopen_dlz_addrdataset,
578 	dlopen_dlz_subrdataset,
579 	dlopen_dlz_delrdataset
580 };
581 #endif
582 
583 /*
584  * Register driver with BIND
585  */
586 isc_result_t
587 dlz_dlopen_init(isc_mem_t *mctx) {
588 #ifndef ISC_DLZ_DLOPEN
589 	UNUSED(mctx);
590 	return (ISC_R_NOTIMPLEMENTED);
591 #else
592 	isc_result_t result;
593 
594 	dlopen_log(2, "Registering DLZ_dlopen driver");
595 
596 	result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
597 				  DNS_SDLZFLAG_RELATIVEOWNER |
598 				  DNS_SDLZFLAG_RELATIVERDATA |
599 				  DNS_SDLZFLAG_THREADSAFE,
600 				  mctx, &dlz_dlopen);
601 
602 	if (result != ISC_R_SUCCESS) {
603 		UNEXPECTED_ERROR(__FILE__, __LINE__,
604 				 "dns_sdlzregister() failed: %s",
605 				 isc_result_totext(result));
606 		result = ISC_R_UNEXPECTED;
607 	}
608 
609 	return (result);
610 #endif
611 }
612 
613 
614 /*
615  * Unregister the driver
616  */
617 void
618 dlz_dlopen_clear(void) {
619 #ifdef ISC_DLZ_DLOPEN
620 	dlopen_log(2, "Unregistering DLZ_dlopen driver");
621 	if (dlz_dlopen != NULL)
622 		dns_sdlzunregister(&dlz_dlopen);
623 #endif
624 }
625