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