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