1 /*
2  * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: 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 /*
20  * This provides a very simple example of an external loadable DLZ
21  * driver, with update support.
22  */
23 
24 #include <inttypes.h>
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "../modules/include/dlz_minimal.h"
32 
33 #define CHECK(x)                             \
34 	do {                                 \
35 		result = (x);                \
36 		if (result != ISC_R_SUCCESS) \
37 			goto failure;        \
38 	} while (0)
39 
40 /* For this simple example, use fixed sized strings */
41 struct record {
42 	char name[100];
43 	char type[10];
44 	char data[200];
45 	dns_ttl_t ttl;
46 };
47 
48 #define MAX_RECORDS 100
49 
50 struct dlz_example_data {
51 	char *zone_name;
52 
53 	/* An example driver doesn't need good memory management :-) */
54 	struct record current[MAX_RECORDS];
55 	struct record adds[MAX_RECORDS];
56 	struct record deletes[MAX_RECORDS];
57 
58 	bool transaction_started;
59 
60 	/* Helper functions from the dlz_dlopen driver */
61 	log_t *log;
62 	dns_sdlz_putrr_t *putrr;
63 	dns_sdlz_putnamedrr_t *putnamedrr;
64 	dns_dlz_writeablezone_t *writeable_zone;
65 };
66 
67 static bool
single_valued(const char * type)68 single_valued(const char *type) {
69 	const char *single[] = { "soa", "cname", NULL };
70 	int i;
71 
72 	for (i = 0; single[i]; i++) {
73 		if (strcasecmp(single[i], type) == 0) {
74 			return (true);
75 		}
76 	}
77 	return (false);
78 }
79 
80 /*
81  * Add a record to a list
82  */
83 static isc_result_t
add_name(struct dlz_example_data * state,struct record * list,const char * name,const char * type,dns_ttl_t ttl,const char * data)84 add_name(struct dlz_example_data *state, struct record *list, const char *name,
85 	 const char *type, dns_ttl_t ttl, const char *data) {
86 	int i;
87 	bool single = single_valued(type);
88 	int first_empty = -1;
89 
90 	for (i = 0; i < MAX_RECORDS; i++) {
91 		if (first_empty == -1 && strlen(list[i].name) == 0U) {
92 			first_empty = i;
93 		}
94 		if (strcasecmp(list[i].name, name) != 0) {
95 			continue;
96 		}
97 		if (strcasecmp(list[i].type, type) != 0) {
98 			continue;
99 		}
100 		if (!single && strcasecmp(list[i].data, data) != 0) {
101 			continue;
102 		}
103 		break;
104 	}
105 	if (i == MAX_RECORDS && first_empty != -1) {
106 		i = first_empty;
107 	}
108 	if (i == MAX_RECORDS) {
109 		if (state->log != NULL) {
110 			state->log(ISC_LOG_ERROR, "dlz_example: out of record "
111 						  "space");
112 		}
113 		return (ISC_R_FAILURE);
114 	}
115 
116 	if (strlen(name) >= sizeof(list[i].name) ||
117 	    strlen(type) >= sizeof(list[i].type) ||
118 	    strlen(data) >= sizeof(list[i].data))
119 	{
120 		return (ISC_R_NOSPACE);
121 	}
122 
123 	strncpy(list[i].name, name, sizeof(list[i].name));
124 	list[i].name[sizeof(list[i].name) - 1] = '\0';
125 
126 	strncpy(list[i].type, type, sizeof(list[i].type));
127 	list[i].type[sizeof(list[i].type) - 1] = '\0';
128 
129 	strncpy(list[i].data, data, sizeof(list[i].data));
130 	list[i].data[sizeof(list[i].data) - 1] = '\0';
131 
132 	list[i].ttl = ttl;
133 
134 	return (ISC_R_SUCCESS);
135 }
136 
137 /*
138  * Delete a record from a list
139  */
140 static isc_result_t
del_name(struct dlz_example_data * state,struct record * list,const char * name,const char * type,dns_ttl_t ttl,const char * data)141 del_name(struct dlz_example_data *state, struct record *list, const char *name,
142 	 const char *type, dns_ttl_t ttl, const char *data) {
143 	int i;
144 
145 	UNUSED(state);
146 
147 	for (i = 0; i < MAX_RECORDS; i++) {
148 		if (strcasecmp(name, list[i].name) == 0 &&
149 		    strcasecmp(type, list[i].type) == 0 &&
150 		    strcasecmp(data, list[i].data) == 0 && ttl == list[i].ttl)
151 		{
152 			break;
153 		}
154 	}
155 	if (i == MAX_RECORDS) {
156 		return (ISC_R_NOTFOUND);
157 	}
158 	memset(&list[i], 0, sizeof(struct record));
159 	return (ISC_R_SUCCESS);
160 }
161 
162 static isc_result_t
fmt_address(isc_sockaddr_t * addr,char * buffer,size_t size)163 fmt_address(isc_sockaddr_t *addr, char *buffer, size_t size) {
164 	char addr_buf[100];
165 	const char *ret;
166 	uint16_t port = 0;
167 
168 	switch (addr->type.sa.sa_family) {
169 	case AF_INET:
170 		port = ntohs(addr->type.sin.sin_port);
171 		ret = inet_ntop(AF_INET, &addr->type.sin.sin_addr, addr_buf,
172 				sizeof(addr_buf));
173 		break;
174 	case AF_INET6:
175 		port = ntohs(addr->type.sin6.sin6_port);
176 		ret = inet_ntop(AF_INET6, &addr->type.sin6.sin6_addr, addr_buf,
177 				sizeof(addr_buf));
178 		break;
179 	default:
180 		return (ISC_R_FAILURE);
181 	}
182 
183 	if (ret == NULL) {
184 		return (ISC_R_FAILURE);
185 	}
186 
187 	snprintf(buffer, size, "%s#%u", addr_buf, port);
188 	return (ISC_R_SUCCESS);
189 }
190 
191 /*
192  * Return the version of the API
193  */
194 int
dlz_version(unsigned int * flags)195 dlz_version(unsigned int *flags) {
196 	UNUSED(flags);
197 	return (DLZ_DLOPEN_VERSION);
198 }
199 
200 /*
201  * Remember a helper function from the bind9 dlz_dlopen driver
202  */
203 static void
b9_add_helper(struct dlz_example_data * state,const char * helper_name,void * ptr)204 b9_add_helper(struct dlz_example_data *state, const char *helper_name,
205 	      void *ptr) {
206 	if (strcmp(helper_name, "log") == 0) {
207 		state->log = (log_t *)ptr;
208 	}
209 	if (strcmp(helper_name, "putrr") == 0) {
210 		state->putrr = (dns_sdlz_putrr_t *)ptr;
211 	}
212 	if (strcmp(helper_name, "putnamedrr") == 0) {
213 		state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
214 	}
215 	if (strcmp(helper_name, "writeable_zone") == 0) {
216 		state->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
217 	}
218 }
219 
220 /*
221  * Called to initialize the driver
222  */
223 isc_result_t
dlz_create(const char * dlzname,unsigned int argc,char * argv[],void ** dbdata,...)224 dlz_create(const char *dlzname, unsigned int argc, char *argv[], void **dbdata,
225 	   ...) {
226 	struct dlz_example_data *state;
227 	const char *helper_name;
228 	va_list ap;
229 	char soa_data[200];
230 	const char *extra;
231 	isc_result_t result;
232 	int n;
233 
234 	UNUSED(dlzname);
235 
236 	state = calloc(1, sizeof(struct dlz_example_data));
237 	if (state == NULL) {
238 		return (ISC_R_NOMEMORY);
239 	}
240 
241 	/* Fill in the helper functions */
242 	va_start(ap, dbdata);
243 	while ((helper_name = va_arg(ap, const char *)) != NULL) {
244 		b9_add_helper(state, helper_name, va_arg(ap, void *));
245 	}
246 	va_end(ap);
247 
248 	if (argc < 2 || argv[1][0] == '\0') {
249 		if (state->log != NULL) {
250 			state->log(ISC_LOG_ERROR, "dlz_example: please specify "
251 						  "a zone name");
252 		}
253 		dlz_destroy(state);
254 		return (ISC_R_FAILURE);
255 	}
256 
257 	/* Ensure zone name is absolute */
258 	state->zone_name = malloc(strlen(argv[1]) + 2);
259 	if (state->zone_name == NULL) {
260 		free(state);
261 		return (ISC_R_NOMEMORY);
262 	}
263 	if (argv[1][strlen(argv[1]) - 1] == '.') {
264 		strcpy(state->zone_name, argv[1]);
265 	} else {
266 		sprintf(state->zone_name, "%s.", argv[1]);
267 	}
268 
269 	if (strcmp(state->zone_name, ".") == 0) {
270 		extra = ".root";
271 	} else {
272 		extra = ".";
273 	}
274 
275 	n = sprintf(soa_data, "%s hostmaster%s%s 123 900 600 86400 3600",
276 		    state->zone_name, extra, state->zone_name);
277 
278 	if (n < 0) {
279 		CHECK(ISC_R_FAILURE);
280 	}
281 	if ((unsigned)n >= sizeof(soa_data)) {
282 		CHECK(ISC_R_NOSPACE);
283 	}
284 
285 	add_name(state, &state->current[0], state->zone_name, "soa", 3600,
286 		 soa_data);
287 	add_name(state, &state->current[0], state->zone_name, "ns", 3600,
288 		 state->zone_name);
289 	add_name(state, &state->current[0], state->zone_name, "a", 1800,
290 		 "10.53.0.1");
291 
292 	if (state->log != NULL) {
293 		state->log(ISC_LOG_INFO, "dlz_example: started for zone %s",
294 			   state->zone_name);
295 	}
296 
297 	*dbdata = state;
298 	return (ISC_R_SUCCESS);
299 
300 failure:
301 	free(state);
302 	return (result);
303 }
304 
305 /*
306  * Shut down the backend
307  */
308 void
dlz_destroy(void * dbdata)309 dlz_destroy(void *dbdata) {
310 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
311 
312 	if (state->log != NULL) {
313 		state->log(ISC_LOG_INFO, "dlz_example: shutting down zone %s",
314 			   state->zone_name);
315 	}
316 	free(state->zone_name);
317 	free(state);
318 }
319 
320 /*
321  * See if we handle a given zone
322  */
323 isc_result_t
dlz_findzonedb(void * dbdata,const char * name,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)324 dlz_findzonedb(void *dbdata, const char *name, dns_clientinfomethods_t *methods,
325 	       dns_clientinfo_t *clientinfo) {
326 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
327 	isc_sockaddr_t *src;
328 	char addrbuf[100];
329 	char absolute[1024];
330 
331 	strcpy(addrbuf, "unknown");
332 	if (methods != NULL && methods->sourceip != NULL &&
333 	    methods->version - methods->age <= DNS_CLIENTINFOMETHODS_VERSION &&
334 	    DNS_CLIENTINFOMETHODS_VERSION <= methods->version)
335 	{
336 		methods->sourceip(clientinfo, &src);
337 		fmt_address(src, addrbuf, sizeof(addrbuf));
338 	}
339 	state->log(ISC_LOG_INFO, "dlz_example: findzonedb connection from: %s",
340 		   addrbuf);
341 
342 	state->log(ISC_LOG_INFO,
343 		   "dlz_example: dlz_findzonedb called with name '%s' "
344 		   "in zone DB '%s'",
345 		   name, state->zone_name);
346 
347 	/*
348 	 * Returning ISC_R_NOTFOUND will cause the query logic to
349 	 * check the database for parent names, looking for zone cuts.
350 	 *
351 	 * Returning ISC_R_NOMORE prevents the query logic from doing
352 	 * this; it will move onto the next database after a single query.
353 	 */
354 	if (strcasecmp(name, "test.example.com") == 0) {
355 		return (ISC_R_NOMORE);
356 	}
357 
358 	/*
359 	 * For example.net, only return ISC_R_NOMORE when queried
360 	 * from 10.53.0.1.
361 	 */
362 	if (strcasecmp(name, "test.example.net") == 0 &&
363 	    strncmp(addrbuf, "10.53.0.1", 9) == 0)
364 	{
365 		return (ISC_R_NOMORE);
366 	}
367 
368 	if (strcasecmp(state->zone_name, name) == 0) {
369 		return (ISC_R_SUCCESS);
370 	}
371 
372 	snprintf(absolute, sizeof(absolute), "%s.", name);
373 	if (strcasecmp(state->zone_name, absolute) == 0) {
374 		return (ISC_R_SUCCESS);
375 	}
376 
377 	return (ISC_R_NOTFOUND);
378 }
379 
380 /*
381  * Look up one record in the sample database.
382  *
383  * If the queryname is "source-addr", send back a TXT record containing
384  * the address of the client; this demonstrates the use of 'methods'
385  * and 'clientinfo'.
386  *
387  * If the queryname is "too-long", send back a TXT record that's too long
388  * to process; this should result in a SERVFAIL when queried.
389  */
390 isc_result_t
dlz_lookup(const char * zone,const char * name,void * dbdata,dns_sdlzlookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)391 dlz_lookup(const char *zone, const char *name, void *dbdata,
392 	   dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods,
393 	   dns_clientinfo_t *clientinfo) {
394 	isc_result_t result;
395 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
396 	bool found = false;
397 	void *dbversion = NULL;
398 	isc_sockaddr_t *src;
399 	char full_name[256];
400 	char buf[512];
401 	int i;
402 
403 	UNUSED(zone);
404 
405 	if (state->putrr == NULL) {
406 		return (ISC_R_NOTIMPLEMENTED);
407 	}
408 
409 	if (strcmp(name, "@") == 0) {
410 		strncpy(full_name, state->zone_name, 255);
411 		full_name[255] = '\0';
412 	} else {
413 		snprintf(full_name, 255, "%s.%s", name, state->zone_name);
414 	}
415 
416 	/*
417 	 * If we need to know the database version (as set in
418 	 * the 'newversion' dlz function) we can pick it up from the
419 	 * clientinfo.
420 	 *
421 	 * This allows a lookup to query the correct version of the DNS
422 	 * data, if the DLZ can differentiate between versions.
423 	 *
424 	 * For example, if a new database transaction is created by
425 	 * 'newversion', the lookup should query within the same
426 	 * transaction scope if it can.
427 	 *
428 	 * If the DLZ only operates on 'live' data, then version
429 	 * wouldn't necessarily be needed.
430 	 */
431 	if (clientinfo != NULL && clientinfo->version >= 2) {
432 		dbversion = clientinfo->dbversion;
433 		if (dbversion != NULL && *(bool *)dbversion) {
434 			state->log(ISC_LOG_INFO, "dlz_example: lookup against "
435 						 "live "
436 						 "transaction\n");
437 		}
438 	}
439 
440 	if (strcmp(name, "source-addr") == 0) {
441 		char ecsbuf[DNS_ECS_FORMATSIZE] = "not supported";
442 		strncpy(buf, "unknown", sizeof(buf));
443 		if (methods != NULL && methods->sourceip != NULL &&
444 		    (methods->version - methods->age <=
445 		     DNS_CLIENTINFOMETHODS_VERSION) &&
446 		    DNS_CLIENTINFOMETHODS_VERSION <= methods->version)
447 		{
448 			methods->sourceip(clientinfo, &src);
449 			fmt_address(src, buf, sizeof(buf));
450 		}
451 		if (clientinfo != NULL && clientinfo->version >= 3) {
452 			if (clientinfo->ecs.addr.family != AF_UNSPEC) {
453 				dns_ecs_format(&clientinfo->ecs, ecsbuf,
454 					       sizeof(ecsbuf));
455 			} else {
456 				snprintf(ecsbuf, sizeof(ecsbuf), "%s",
457 					 "not present");
458 			}
459 		}
460 		i = strlen(buf);
461 		snprintf(buf + i, sizeof(buf) - i - 1, " ECS %s", ecsbuf);
462 
463 		state->log(ISC_LOG_INFO,
464 			   "dlz_example: lookup connection from: %s", buf);
465 
466 		found = true;
467 		result = state->putrr(lookup, "TXT", 0, buf);
468 		/* We could also generate a CNAME RR:
469 		snprintf(buf, sizeof(buf), "%s.redirect.example.", ecsbuf);
470 		result = state->putrr(lookup, "CNAME", 0, buf); */
471 		if (result != ISC_R_SUCCESS) {
472 			return (result);
473 		}
474 	}
475 
476 	if (strcmp(name, "too-long") == 0) {
477 		for (i = 0; i < 511; i++)
478 			buf[i] = 'x';
479 		buf[i] = '\0';
480 		found = true;
481 		result = state->putrr(lookup, "TXT", 0, buf);
482 		if (result != ISC_R_SUCCESS) {
483 			return (result);
484 		}
485 	}
486 
487 	for (i = 0; i < MAX_RECORDS; i++) {
488 		if (strcasecmp(state->current[i].name, full_name) == 0) {
489 			found = true;
490 			result = state->putrr(lookup, state->current[i].type,
491 					      state->current[i].ttl,
492 					      state->current[i].data);
493 			if (result != ISC_R_SUCCESS) {
494 				return (result);
495 			}
496 		}
497 	}
498 
499 	if (!found) {
500 		return (ISC_R_NOTFOUND);
501 	}
502 
503 	return (ISC_R_SUCCESS);
504 }
505 
506 /*
507  * See if a zone transfer is allowed
508  */
509 isc_result_t
dlz_allowzonexfr(void * dbdata,const char * name,const char * client)510 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
511 	UNUSED(client);
512 
513 	/* Just say yes for all our zones */
514 	return (dlz_findzonedb(dbdata, name, NULL, NULL));
515 }
516 
517 /*
518  * Perform a zone transfer
519  */
520 isc_result_t
dlz_allnodes(const char * zone,void * dbdata,dns_sdlzallnodes_t * allnodes)521 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
522 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
523 	int i;
524 
525 	UNUSED(zone);
526 
527 	if (state->putnamedrr == NULL) {
528 		return (ISC_R_NOTIMPLEMENTED);
529 	}
530 
531 	for (i = 0; i < MAX_RECORDS; i++) {
532 		isc_result_t result;
533 		if (strlen(state->current[i].name) == 0U) {
534 			continue;
535 		}
536 		result = state->putnamedrr(allnodes, state->current[i].name,
537 					   state->current[i].type,
538 					   state->current[i].ttl,
539 					   state->current[i].data);
540 		if (result != ISC_R_SUCCESS) {
541 			return (result);
542 		}
543 	}
544 
545 	return (ISC_R_SUCCESS);
546 }
547 
548 /*
549  * Start a transaction
550  */
551 isc_result_t
dlz_newversion(const char * zone,void * dbdata,void ** versionp)552 dlz_newversion(const char *zone, void *dbdata, void **versionp) {
553 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
554 
555 	if (state->transaction_started) {
556 		if (state->log != NULL) {
557 			state->log(ISC_LOG_INFO,
558 				   "dlz_example: transaction already "
559 				   "started for zone %s",
560 				   zone);
561 		}
562 		return (ISC_R_FAILURE);
563 	}
564 
565 	state->transaction_started = true;
566 	*versionp = (void *)&state->transaction_started;
567 
568 	return (ISC_R_SUCCESS);
569 }
570 
571 /*
572  * End a transaction
573  */
574 void
dlz_closeversion(const char * zone,bool commit,void * dbdata,void ** versionp)575 dlz_closeversion(const char *zone, bool commit, void *dbdata, void **versionp) {
576 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
577 
578 	if (!state->transaction_started) {
579 		if (state->log != NULL) {
580 			state->log(ISC_LOG_INFO,
581 				   "dlz_example: transaction not "
582 				   "started for zone %s",
583 				   zone);
584 		}
585 		*versionp = NULL;
586 		return;
587 	}
588 
589 	state->transaction_started = false;
590 
591 	*versionp = NULL;
592 
593 	if (commit) {
594 		int i;
595 		if (state->log != NULL) {
596 			state->log(ISC_LOG_INFO,
597 				   "dlz_example: committing "
598 				   "transaction on zone %s",
599 				   zone);
600 		}
601 		for (i = 0; i < MAX_RECORDS; i++) {
602 			if (strlen(state->deletes[i].name) > 0U) {
603 				(void)del_name(state, &state->current[0],
604 					       state->deletes[i].name,
605 					       state->deletes[i].type,
606 					       state->deletes[i].ttl,
607 					       state->deletes[i].data);
608 			}
609 		}
610 		for (i = 0; i < MAX_RECORDS; i++) {
611 			if (strlen(state->adds[i].name) > 0U) {
612 				(void)add_name(state, &state->current[0],
613 					       state->adds[i].name,
614 					       state->adds[i].type,
615 					       state->adds[i].ttl,
616 					       state->adds[i].data);
617 			}
618 		}
619 	} else {
620 		if (state->log != NULL) {
621 			state->log(ISC_LOG_INFO,
622 				   "dlz_example: cancelling "
623 				   "transaction on zone %s",
624 				   zone);
625 		}
626 	}
627 	memset(state->adds, 0, sizeof(state->adds));
628 	memset(state->deletes, 0, sizeof(state->deletes));
629 }
630 
631 /*
632  * Configure a writeable zone
633  */
634 isc_result_t
dlz_configure(dns_view_t * view,dns_dlzdb_t * dlzdb,void * dbdata)635 dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *dbdata) {
636 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
637 	isc_result_t result;
638 
639 	if (state->log != NULL) {
640 		state->log(ISC_LOG_INFO, "dlz_example: starting configure");
641 	}
642 
643 	if (state->writeable_zone == NULL) {
644 		if (state->log != NULL) {
645 			state->log(ISC_LOG_INFO, "dlz_example: no "
646 						 "writeable_zone method "
647 						 "available");
648 		}
649 		return (ISC_R_FAILURE);
650 	}
651 
652 	result = state->writeable_zone(view, dlzdb, state->zone_name);
653 	if (result != ISC_R_SUCCESS) {
654 		if (state->log != NULL) {
655 			state->log(ISC_LOG_ERROR,
656 				   "dlz_example: failed to "
657 				   "configure zone %s",
658 				   state->zone_name);
659 		}
660 		return (result);
661 	}
662 
663 	if (state->log != NULL) {
664 		state->log(ISC_LOG_INFO,
665 			   "dlz_example: configured writeable "
666 			   "zone %s",
667 			   state->zone_name);
668 	}
669 	return (ISC_R_SUCCESS);
670 }
671 
672 /*
673  * Authorize a zone update
674  */
675 bool
dlz_ssumatch(const char * signer,const char * name,const char * tcpaddr,const char * type,const char * key,uint32_t keydatalen,unsigned char * keydata,void * dbdata)676 dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
677 	     const char *type, const char *key, uint32_t keydatalen,
678 	     unsigned char *keydata, void *dbdata) {
679 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
680 
681 	UNUSED(tcpaddr);
682 	UNUSED(type);
683 	UNUSED(key);
684 	UNUSED(keydatalen);
685 	UNUSED(keydata);
686 
687 	if (strncmp(name, "deny.", 5) == 0) {
688 		if (state->log != NULL) {
689 			state->log(ISC_LOG_INFO,
690 				   "dlz_example: denying update "
691 				   "of name=%s by %s",
692 				   name, signer);
693 		}
694 		return (false);
695 	}
696 	if (state->log != NULL) {
697 		state->log(ISC_LOG_INFO,
698 			   "dlz_example: allowing update of "
699 			   "name=%s by %s",
700 			   name, signer);
701 	}
702 	return (true);
703 }
704 
705 static isc_result_t
modrdataset(struct dlz_example_data * state,const char * name,const char * rdatastr,struct record * list)706 modrdataset(struct dlz_example_data *state, const char *name,
707 	    const char *rdatastr, struct record *list) {
708 	char *full_name, *dclass, *type, *data, *ttlstr, *buf;
709 	char absolute[1024];
710 	isc_result_t result;
711 #if defined(WIN32) || defined(_REENTRANT)
712 	char *saveptr = NULL;
713 #endif /* if defined(WIN32) || defined(_REENTRANT) */
714 
715 	buf = strdup(rdatastr);
716 	if (buf == NULL) {
717 		return (ISC_R_FAILURE);
718 	}
719 
720 	/*
721 	 * The format is:
722 	 * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA
723 	 *
724 	 * The DATA field is space separated, and is in the data format
725 	 * for the type used by dig
726 	 */
727 
728 	full_name = strtok_r(buf, "\t", &saveptr);
729 	if (full_name == NULL) {
730 		goto error;
731 	}
732 
733 	ttlstr = strtok_r(NULL, "\t", &saveptr);
734 	if (ttlstr == NULL) {
735 		goto error;
736 	}
737 
738 	dclass = strtok_r(NULL, "\t", &saveptr);
739 	if (dclass == NULL) {
740 		goto error;
741 	}
742 
743 	type = strtok_r(NULL, "\t", &saveptr);
744 	if (type == NULL) {
745 		goto error;
746 	}
747 
748 	data = strtok_r(NULL, "\t", &saveptr);
749 	if (data == NULL) {
750 		goto error;
751 	}
752 
753 	if (name[strlen(name) - 1] != '.') {
754 		snprintf(absolute, sizeof(absolute), "%s.", name);
755 		name = absolute;
756 	}
757 
758 	result = add_name(state, list, name, type, strtoul(ttlstr, NULL, 10),
759 			  data);
760 	free(buf);
761 	return (result);
762 
763 error:
764 	free(buf);
765 	return (ISC_R_FAILURE);
766 }
767 
768 isc_result_t
dlz_addrdataset(const char * name,const char * rdatastr,void * dbdata,void * version)769 dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata,
770 		void *version) {
771 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
772 
773 	if (version != (void *)&state->transaction_started) {
774 		return (ISC_R_FAILURE);
775 	}
776 
777 	if (state->log != NULL) {
778 		state->log(ISC_LOG_INFO, "dlz_example: adding rdataset %s '%s'",
779 			   name, rdatastr);
780 	}
781 
782 	return (modrdataset(state, name, rdatastr, &state->adds[0]));
783 }
784 
785 isc_result_t
dlz_subrdataset(const char * name,const char * rdatastr,void * dbdata,void * version)786 dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata,
787 		void *version) {
788 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
789 
790 	if (version != (void *)&state->transaction_started) {
791 		return (ISC_R_FAILURE);
792 	}
793 
794 	if (state->log != NULL) {
795 		state->log(ISC_LOG_INFO,
796 			   "dlz_example: subtracting rdataset "
797 			   "%s '%s'",
798 			   name, rdatastr);
799 	}
800 
801 	return (modrdataset(state, name, rdatastr, &state->deletes[0]));
802 }
803 
804 isc_result_t
dlz_delrdataset(const char * name,const char * type,void * dbdata,void * version)805 dlz_delrdataset(const char *name, const char *type, void *dbdata,
806 		void *version) {
807 	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
808 
809 	if (version != (void *)&state->transaction_started) {
810 		return (ISC_R_FAILURE);
811 	}
812 
813 	if (state->log != NULL) {
814 		state->log(ISC_LOG_INFO,
815 			   "dlz_example: deleting rdataset %s "
816 			   "of type %s",
817 			   name, type);
818 	}
819 
820 	return (ISC_R_SUCCESS);
821 }
822