1/* $NetBSD: driver.c,v 1.1.1.1 2011/09/11 17:13:07 christos Exp $ */ 2 3/* 4 * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/* Id */ 20 21/* 22 * This provides a very simple example of an external loadable DLZ 23 * driver, with update support. 24 */ 25 26#include <config.h> 27 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <stdarg.h> 32 33#include <isc/log.h> 34#include <isc/print.h> 35#include <isc/result.h> 36#include <isc/types.h> 37#include <isc/util.h> 38 39#include <dns/types.h> 40#include <dns/dlz_dlopen.h> 41 42#include "driver.h" 43 44#ifdef WIN32 45#define STRTOK_R(a, b, c) strtok_s(a, b, c) 46#elif defined(_REENTRANT) 47#define STRTOK_R(a, b, c) strtok_r(a, b, c) 48#else 49#define STRTOK_R(a, b, c) strtok(a, b) 50#endif 51 52/* For this simple example, use fixed sized strings */ 53struct record { 54 char name[100]; 55 char type[10]; 56 char data[200]; 57 dns_ttl_t ttl; 58}; 59 60#define MAX_RECORDS 100 61 62typedef void log_t(int level, const char *fmt, ...); 63 64struct dlz_example_data { 65 char *zone_name; 66 67 /* An example driver doesn't need good memory management :-) */ 68 struct record current[MAX_RECORDS]; 69 struct record adds[MAX_RECORDS]; 70 struct record deletes[MAX_RECORDS]; 71 72 isc_boolean_t transaction_started; 73 74 /* Helper functions from the dlz_dlopen driver */ 75 log_t *log; 76 dns_sdlz_putrr_t *putrr; 77 dns_sdlz_putnamedrr_t *putnamedrr; 78 dns_dlz_writeablezone_t *writeable_zone; 79}; 80 81static isc_boolean_t 82single_valued(const char *type) { 83 const char *single[] = { "soa", "cname", NULL }; 84 int i; 85 86 for (i = 0; single[i]; i++) { 87 if (strcasecmp(single[i], type) == 0) { 88 return (ISC_TRUE); 89 } 90 } 91 return (ISC_FALSE); 92} 93 94/* 95 * Add a record to a list 96 */ 97static isc_result_t 98add_name(struct dlz_example_data *state, struct record *list, 99 const char *name, const char *type, dns_ttl_t ttl, const char *data) 100{ 101 int i; 102 isc_boolean_t single = single_valued(type); 103 int first_empty = -1; 104 105 for (i = 0; i < MAX_RECORDS; i++) { 106 if (first_empty == -1 && strlen(list[i].name) == 0U) { 107 first_empty = i; 108 } 109 if (strcasecmp(list[i].name, name) != 0) 110 continue; 111 if (strcasecmp(list[i].type, type) != 0) 112 continue; 113 if (!single && strcasecmp(list[i].data, data) != 0) 114 continue; 115 break; 116 } 117 if (i == MAX_RECORDS && first_empty != -1) { 118 i = first_empty; 119 } 120 if (i == MAX_RECORDS) { 121 state->log(ISC_LOG_ERROR, "dlz_example: out of record space"); 122 return (ISC_R_FAILURE); 123 } 124 strcpy(list[i].name, name); 125 strcpy(list[i].type, type); 126 strcpy(list[i].data, data); 127 list[i].ttl = ttl; 128 return (ISC_R_SUCCESS); 129} 130 131/* 132 * Delete a record from a list 133 */ 134static isc_result_t 135del_name(struct dlz_example_data *state, struct record *list, 136 const char *name, const char *type, dns_ttl_t ttl, 137 const char *data) 138{ 139 int i; 140 141 UNUSED(state); 142 143 for (i = 0; i < MAX_RECORDS; i++) { 144 if (strcasecmp(name, list[i].name) == 0 && 145 strcasecmp(type, list[i].type) == 0 && 146 strcasecmp(data, list[i].data) == 0 && 147 ttl == list[i].ttl) { 148 break; 149 } 150 } 151 if (i == MAX_RECORDS) { 152 return (ISC_R_NOTFOUND); 153 } 154 memset(&list[i], 0, sizeof(struct record)); 155 return (ISC_R_SUCCESS); 156} 157 158static isc_result_t 159fmt_address(isc_sockaddr_t *addr, char *buffer, size_t size) { 160 char addr_buf[100]; 161 const char *ret; 162 isc_uint16_t port = 0; 163 164 switch (addr->type.sa.sa_family) { 165 case AF_INET: 166 port = ntohs(addr->type.sin.sin_port); 167 ret = inet_ntop(AF_INET, &addr->type.sin.sin_addr, addr_buf, 168 sizeof(addr_buf)); 169 break; 170 case AF_INET6: 171 port = ntohs(addr->type.sin6.sin6_port); 172 ret = inet_ntop(AF_INET6, &addr->type.sin6.sin6_addr, addr_buf, 173 sizeof(addr_buf)); 174 break; 175 default: 176 return (ISC_R_FAILURE); 177 } 178 179 if (ret == NULL) 180 return (ISC_R_FAILURE); 181 182 snprintf(buffer, size, "%s#%u", addr_buf, port); 183 return (ISC_R_SUCCESS); 184} 185 186/* 187 * Return the version of the API 188 */ 189int 190dlz_version(unsigned int *flags) { 191 UNUSED(flags); 192 return (DLZ_DLOPEN_VERSION); 193} 194 195/* 196 * Remember a helper function from the bind9 dlz_dlopen driver 197 */ 198static void 199b9_add_helper(struct dlz_example_data *state, 200 const char *helper_name, void *ptr) 201{ 202 if (strcmp(helper_name, "log") == 0) 203 state->log = (log_t *)ptr; 204 if (strcmp(helper_name, "putrr") == 0) 205 state->putrr = (dns_sdlz_putrr_t *)ptr; 206 if (strcmp(helper_name, "putnamedrr") == 0) 207 state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr; 208 if (strcmp(helper_name, "writeable_zone") == 0) 209 state->writeable_zone = (dns_dlz_writeablezone_t *)ptr; 210} 211 212 213/* 214 * Called to initialize the driver 215 */ 216isc_result_t 217dlz_create(const char *dlzname, unsigned int argc, char *argv[], 218 void **dbdata, ...) 219{ 220 struct dlz_example_data *state; 221 const char *helper_name; 222 va_list ap; 223 char soa_data[200]; 224 225 UNUSED(dlzname); 226 227 state = calloc(1, sizeof(struct dlz_example_data)); 228 if (state == NULL) 229 return (ISC_R_NOMEMORY); 230 231 /* Fill in the helper functions */ 232 va_start(ap, dbdata); 233 while ((helper_name = va_arg(ap, const char *)) != NULL) { 234 b9_add_helper(state, helper_name, va_arg(ap, void*)); 235 } 236 va_end(ap); 237 238 if (argc < 2) { 239 state->log(ISC_LOG_ERROR, 240 "dlz_example: please specify a zone name"); 241 dlz_destroy(state); 242 return (ISC_R_FAILURE); 243 } 244 245 state->zone_name = strdup(argv[1]); 246 247 sprintf(soa_data, "%s hostmaster.%s 123 900 600 86400 3600", 248 state->zone_name, state->zone_name); 249 250 add_name(state, &state->current[0], state->zone_name, 251 "soa", 3600, soa_data); 252 add_name(state, &state->current[0], state->zone_name, 253 "ns", 3600, state->zone_name); 254 add_name(state, &state->current[0], state->zone_name, 255 "a", 1800, "10.53.0.1"); 256 257 state->log(ISC_LOG_INFO, 258 "dlz_example: started for zone %s", 259 state->zone_name); 260 261 *dbdata = state; 262 return (ISC_R_SUCCESS); 263} 264 265/* 266 * Shut down the backend 267 */ 268void 269dlz_destroy(void *dbdata) { 270 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 271 272 state->log(ISC_LOG_INFO, 273 "dlz_example: shutting down zone %s", 274 state->zone_name); 275 free(state->zone_name); 276 free(state); 277} 278 279 280/* 281 * See if we handle a given zone 282 */ 283isc_result_t 284dlz_findzonedb(void *dbdata, const char *name) { 285 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 286 287 if (strcasecmp(state->zone_name, name) == 0) 288 return (ISC_R_SUCCESS); 289 290 return (ISC_R_NOTFOUND); 291} 292 293/* 294 * Look up one record 295 */ 296isc_result_t 297dlz_lookup(const char *zone, const char *name, void *dbdata, 298 dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods, 299 dns_clientinfo_t *clientinfo) 300{ 301 isc_result_t result; 302 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 303 isc_boolean_t found = ISC_FALSE; 304 isc_sockaddr_t *src; 305 char full_name[100]; 306 int i; 307 308 UNUSED(zone); 309 310 if (strcmp(name, "@") == 0) 311 strcpy(full_name, state->zone_name); 312 else 313 sprintf(full_name, "%s.%s", name, state->zone_name); 314 315 if (strcmp(name, "source-addr") == 0) { 316 char buf[100]; 317 strcpy(buf, "unknown"); 318 if (methods != NULL && 319 methods->version - methods->age >= 320 DNS_CLIENTINFOMETHODS_VERSION) 321 { 322 methods->sourceip(clientinfo, &src); 323 fmt_address(src, buf, sizeof(buf)); 324 } 325 326 fprintf(stderr, "connection from: %s\n", buf); 327 328 found = ISC_TRUE; 329 result = state->putrr(lookup, "TXT", 0, buf); 330 if (result != ISC_R_SUCCESS) 331 return (result); 332 } 333 334 for (i = 0; i < MAX_RECORDS; i++) { 335 if (strcasecmp(state->current[i].name, full_name) == 0) { 336 found = ISC_TRUE; 337 result = state->putrr(lookup, state->current[i].type, 338 state->current[i].ttl, 339 state->current[i].data); 340 if (result != ISC_R_SUCCESS) 341 return (result); 342 } 343 } 344 345 if (!found) 346 return (ISC_R_NOTFOUND); 347 348 return (ISC_R_SUCCESS); 349} 350 351 352/* 353 * See if a zone transfer is allowed 354 */ 355isc_result_t 356dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { 357 UNUSED(client); 358 359 /* Just say yes for all our zones */ 360 return (dlz_findzonedb(dbdata, name)); 361} 362 363/* 364 * Perform a zone transfer 365 */ 366isc_result_t 367dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) { 368 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 369 int i; 370 371 UNUSED(zone); 372 373 for (i = 0; i < MAX_RECORDS; i++) { 374 isc_result_t result; 375 if (strlen(state->current[i].name) == 0U) { 376 continue; 377 } 378 result = state->putnamedrr(allnodes, state->current[i].name, 379 state->current[i].type, 380 state->current[i].ttl, 381 state->current[i].data); 382 if (result != ISC_R_SUCCESS) 383 return (result); 384 } 385 386 return (ISC_R_SUCCESS); 387} 388 389 390/* 391 * Start a transaction 392 */ 393isc_result_t 394dlz_newversion(const char *zone, void *dbdata, void **versionp) { 395 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 396 397 if (state->transaction_started) { 398 state->log(ISC_LOG_INFO, 399 "dlz_example: transaction already " 400 "started for zone %s", zone); 401 return (ISC_R_FAILURE); 402 } 403 404 state->transaction_started = ISC_TRUE; 405 *versionp = (void *) &state->transaction_started; 406 407 return (ISC_R_SUCCESS); 408} 409 410/* 411 * End a transaction 412 */ 413void 414dlz_closeversion(const char *zone, isc_boolean_t commit, 415 void *dbdata, void **versionp) 416{ 417 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 418 419 if (!state->transaction_started) { 420 state->log(ISC_LOG_INFO, 421 "dlz_example: transaction not started for zone %s", 422 zone); 423 *versionp = NULL; 424 return; 425 } 426 427 state->transaction_started = ISC_FALSE; 428 429 *versionp = NULL; 430 431 if (commit) { 432 int i; 433 state->log(ISC_LOG_INFO, 434 "dlz_example: committing transaction on zone %s", 435 zone); 436 for (i = 0; i < MAX_RECORDS; i++) { 437 if (strlen(state->adds[i].name) > 0U) { 438 add_name(state, &state->current[0], 439 state->adds[i].name, 440 state->adds[i].type, 441 state->adds[i].ttl, 442 state->adds[i].data); 443 } 444 } 445 for (i = 0; i < MAX_RECORDS; i++) { 446 if (strlen(state->deletes[i].name) > 0U) { 447 del_name(state, &state->current[0], 448 state->deletes[i].name, 449 state->deletes[i].type, 450 state->deletes[i].ttl, 451 state->deletes[i].data); 452 } 453 } 454 } else { 455 state->log(ISC_LOG_INFO, 456 "dlz_example: cancelling transaction on zone %s", 457 zone); 458 } 459 memset(state->adds, 0, sizeof(state->adds)); 460 memset(state->deletes, 0, sizeof(state->deletes)); 461} 462 463 464/* 465 * Configure a writeable zone 466 */ 467isc_result_t 468dlz_configure(dns_view_t *view, void *dbdata) { 469 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 470 isc_result_t result; 471 472 473 state->log(ISC_LOG_INFO, "dlz_example: starting configure"); 474 if (state->writeable_zone == NULL) { 475 state->log(ISC_LOG_INFO, 476 "dlz_example: no writeable_zone method available"); 477 return (ISC_R_FAILURE); 478 } 479 480 result = state->writeable_zone(view, state->zone_name); 481 if (result != ISC_R_SUCCESS) { 482 state->log(ISC_LOG_ERROR, 483 "dlz_example: failed to configure zone %s", 484 state->zone_name); 485 return (result); 486 } 487 488 state->log(ISC_LOG_INFO, 489 "dlz_example: configured writeable zone %s", 490 state->zone_name); 491 return (ISC_R_SUCCESS); 492} 493 494/* 495 * Authorize a zone update 496 */ 497isc_boolean_t 498dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, 499 const char *type, const char *key, isc_uint32_t keydatalen, 500 unsigned char *keydata, void *dbdata) 501{ 502 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 503 504 UNUSED(tcpaddr); 505 UNUSED(type); 506 UNUSED(key); 507 UNUSED(keydatalen); 508 UNUSED(keydata); 509 510 if (strncmp(name, "deny.", 5) == 0) { 511 state->log(ISC_LOG_INFO, 512 "dlz_example: denying update of name=%s by %s", 513 name, signer); 514 return (ISC_FALSE); 515 } 516 state->log(ISC_LOG_INFO, 517 "dlz_example: allowing update of name=%s by %s", 518 name, signer); 519 return (ISC_TRUE); 520} 521 522 523static isc_result_t 524modrdataset(struct dlz_example_data *state, const char *name, 525 const char *rdatastr, struct record *list) 526{ 527 char *full_name, *dclass, *type, *data, *ttlstr, *buf; 528 isc_result_t result; 529#if defined(WIN32) || defined(_REENTRANT) 530 char *saveptr = NULL; 531#endif 532 533 buf = strdup(rdatastr); 534 if (buf == NULL) 535 return (ISC_R_FAILURE); 536 537 /* 538 * The format is: 539 * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA 540 * 541 * The DATA field is space separated, and is in the data format 542 * for the type used by dig 543 */ 544 545 full_name = STRTOK_R(buf, "\t", &saveptr); 546 if (full_name == NULL) 547 goto error; 548 549 ttlstr = STRTOK_R(NULL, "\t", &saveptr); 550 if (ttlstr == NULL) 551 goto error; 552 553 dclass = STRTOK_R(NULL, "\t", &saveptr); 554 if (dclass == NULL) 555 goto error; 556 557 type = STRTOK_R(NULL, "\t", &saveptr); 558 if (type == NULL) 559 goto error; 560 561 data = STRTOK_R(NULL, "\t", &saveptr); 562 if (data == NULL) 563 goto error; 564 565 result = add_name(state, list, name, type, 566 strtoul(ttlstr, NULL, 10), data); 567 free(buf); 568 return (result); 569 570 error: 571 free(buf); 572 return (ISC_R_FAILURE); 573} 574 575 576isc_result_t 577dlz_addrdataset(const char *name, const char *rdatastr, 578 void *dbdata, void *version) 579{ 580 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 581 582 if (version != (void *) &state->transaction_started) 583 return (ISC_R_FAILURE); 584 585 state->log(ISC_LOG_INFO, 586 "dlz_example: adding rdataset %s '%s'", 587 name, rdatastr); 588 589 return (modrdataset(state, name, rdatastr, &state->adds[0])); 590} 591 592isc_result_t 593dlz_subrdataset(const char *name, const char *rdatastr, 594 void *dbdata, void *version) 595{ 596 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 597 598 if (version != (void *) &state->transaction_started) 599 return (ISC_R_FAILURE); 600 601 state->log(ISC_LOG_INFO, 602 "dlz_example: subtracting rdataset %s '%s'", 603 name, rdatastr); 604 605 return (modrdataset(state, name, rdatastr, &state->deletes[0])); 606} 607 608 609isc_result_t 610dlz_delrdataset(const char *name, const char *type, 611 void *dbdata, void *version) 612{ 613 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 614 615 if (version != (void *) &state->transaction_started) 616 return (ISC_R_FAILURE); 617 618 state->log(ISC_LOG_INFO, 619 "dlz_example: deleting rdataset %s of type %s", 620 name, type); 621 622 return (ISC_R_SUCCESS); 623} 624