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