1/* $NetBSD: dlz_example.c,v 1.2.8.1 2012/06/05 21:15:40 bouyer Exp $ */ 2 3/* 4 * Copyright (C) 2011 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 <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <stdarg.h> 30#include <stdint.h> 31 32#include "dlz_minimal.h" 33 34#ifdef WIN32 35#define STRTOK_R(a, b, c) strtok_s(a, b, c) 36#elif defined(_REENTRANT) 37#define STRTOK_R(a, b, c) strtok_r(a, b, c) 38#else 39#define STRTOK_R(a, b, c) strtok(a, b) 40#endif 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 isc_boolean_t 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 isc_boolean_t 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 (ISC_TRUE); 77 } 78 } 79 return (ISC_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, 87 const char *name, const char *type, dns_ttl_t ttl, const char *data) 88{ 89 int i; 90 isc_boolean_t single = single_valued(type); 91 int first_empty = -1; 92 93 for (i = 0; i < MAX_RECORDS; i++) { 94 if (first_empty == -1 && strlen(list[i].name) == 0U) { 95 first_empty = i; 96 } 97 if (strcasecmp(list[i].name, name) != 0) 98 continue; 99 if (strcasecmp(list[i].type, type) != 0) 100 continue; 101 if (!single && strcasecmp(list[i].data, data) != 0) 102 continue; 103 break; 104 } 105 if (i == MAX_RECORDS && first_empty != -1) { 106 i = first_empty; 107 } 108 if (i == MAX_RECORDS) { 109 state->log(ISC_LOG_ERROR, "dlz_example: out of record space"); 110 return (ISC_R_FAILURE); 111 } 112 strcpy(list[i].name, name); 113 strcpy(list[i].type, type); 114 strcpy(list[i].data, data); 115 list[i].ttl = ttl; 116 return (ISC_R_SUCCESS); 117} 118 119/* 120 * Delete a record from a list 121 */ 122static isc_result_t 123del_name(struct dlz_example_data *state, struct record *list, 124 const char *name, const char *type, dns_ttl_t ttl, 125 const char *data) 126{ 127 int i; 128 129 UNUSED(state); 130 131 for (i = 0; i < MAX_RECORDS; i++) { 132 if (strcasecmp(name, list[i].name) == 0 && 133 strcasecmp(type, list[i].type) == 0 && 134 strcasecmp(data, list[i].data) == 0 && 135 ttl == list[i].ttl) { 136 break; 137 } 138 } 139 if (i == MAX_RECORDS) { 140 return (ISC_R_NOTFOUND); 141 } 142 memset(&list[i], 0, sizeof(struct record)); 143 return (ISC_R_SUCCESS); 144} 145 146static isc_result_t 147fmt_address(isc_sockaddr_t *addr, char *buffer, size_t size) { 148 char addr_buf[100]; 149 const char *ret; 150 uint16_t port = 0; 151 152 switch (addr->type.sa.sa_family) { 153 case AF_INET: 154 port = ntohs(addr->type.sin.sin_port); 155 ret = inet_ntop(AF_INET, &addr->type.sin.sin_addr, addr_buf, 156 sizeof(addr_buf)); 157 break; 158 case AF_INET6: 159 port = ntohs(addr->type.sin6.sin6_port); 160 ret = inet_ntop(AF_INET6, &addr->type.sin6.sin6_addr, addr_buf, 161 sizeof(addr_buf)); 162 break; 163 default: 164 return (ISC_R_FAILURE); 165 } 166 167 if (ret == NULL) 168 return (ISC_R_FAILURE); 169 170 snprintf(buffer, size, "%s#%u", addr_buf, port); 171 return (ISC_R_SUCCESS); 172} 173 174/* 175 * Return the version of the API 176 */ 177int 178dlz_version(unsigned int *flags) { 179 UNUSED(flags); 180 return (DLZ_DLOPEN_VERSION); 181} 182 183/* 184 * Remember a helper function from the bind9 dlz_dlopen driver 185 */ 186static void 187b9_add_helper(struct dlz_example_data *state, 188 const char *helper_name, void *ptr) 189{ 190 if (strcmp(helper_name, "log") == 0) 191 state->log = (log_t *)ptr; 192 if (strcmp(helper_name, "putrr") == 0) 193 state->putrr = (dns_sdlz_putrr_t *)ptr; 194 if (strcmp(helper_name, "putnamedrr") == 0) 195 state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr; 196 if (strcmp(helper_name, "writeable_zone") == 0) 197 state->writeable_zone = (dns_dlz_writeablezone_t *)ptr; 198} 199 200 201/* 202 * Called to initialize the driver 203 */ 204isc_result_t 205dlz_create(const char *dlzname, unsigned int argc, char *argv[], 206 void **dbdata, ...) 207{ 208 struct dlz_example_data *state; 209 const char *helper_name; 210 va_list ap; 211 char soa_data[200]; 212 213 UNUSED(dlzname); 214 215 state = calloc(1, sizeof(struct dlz_example_data)); 216 if (state == NULL) 217 return (ISC_R_NOMEMORY); 218 219 /* Fill in the helper functions */ 220 va_start(ap, dbdata); 221 while ((helper_name = va_arg(ap, const char *)) != NULL) { 222 b9_add_helper(state, helper_name, va_arg(ap, void*)); 223 } 224 va_end(ap); 225 226 if (argc < 2) { 227 state->log(ISC_LOG_ERROR, 228 "dlz_example: please specify a zone name"); 229 return (ISC_R_FAILURE); 230 } 231 232 state->zone_name = strdup(argv[1]); 233 234 sprintf(soa_data, "%s hostmaster.%s 123 900 600 86400 3600", 235 state->zone_name, state->zone_name); 236 237 add_name(state, &state->current[0], state->zone_name, 238 "soa", 3600, soa_data); 239 add_name(state, &state->current[0], state->zone_name, 240 "ns", 3600, state->zone_name); 241 add_name(state, &state->current[0], state->zone_name, 242 "a", 1800, "10.53.0.1"); 243 244 state->log(ISC_LOG_INFO, 245 "dlz_example: started for zone %s", 246 state->zone_name); 247 248 *dbdata = state; 249 return (ISC_R_SUCCESS); 250} 251 252/* 253 * Shut down the backend 254 */ 255void 256dlz_destroy(void *dbdata) { 257 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 258 259 state->log(ISC_LOG_INFO, 260 "dlz_example: shutting down zone %s", 261 state->zone_name); 262 free(state->zone_name); 263 free(state); 264} 265 266 267/* 268 * See if we handle a given zone 269 */ 270isc_result_t 271dlz_findzonedb(void *dbdata, const char *name) { 272 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 273 274 if (strcasecmp(state->zone_name, name) == 0) 275 return (ISC_R_SUCCESS); 276 277 return (ISC_R_NOTFOUND); 278} 279 280/* 281 * Look up one record in the sample database. 282 * 283 * If the queryname is "source-addr", we add a TXT record containing 284 * the address of the client; this demonstrates the use of 'methods' 285 * and 'clientinfo'. 286 */ 287isc_result_t 288dlz_lookup(const char *zone, const char *name, void *dbdata, 289 dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods, 290 dns_clientinfo_t *clientinfo) 291{ 292 isc_result_t result; 293 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 294 isc_boolean_t found = ISC_FALSE; 295 isc_sockaddr_t *src; 296 char full_name[100]; 297 int i; 298 299 UNUSED(zone); 300 301 if (strcmp(name, "@") == 0) 302 strcpy(full_name, state->zone_name); 303 else 304 sprintf(full_name, "%s.%s", name, state->zone_name); 305 306 if (strcmp(name, "source-addr") == 0) { 307 char buf[100]; 308 strcpy(buf, "unknown"); 309 if (methods != NULL && 310 methods->version - methods->age >= 311 DNS_CLIENTINFOMETHODS_VERSION) 312 { 313 methods->sourceip(clientinfo, &src); 314 fmt_address(src, buf, sizeof(buf)); 315 } 316 317 fprintf(stderr, "connection from: %s\n", buf); 318 319 found = ISC_TRUE; 320 result = state->putrr(lookup, "TXT", 0, buf); 321 if (result != ISC_R_SUCCESS) 322 return (result); 323 } 324 325 for (i = 0; i < MAX_RECORDS; i++) { 326 if (strcasecmp(state->current[i].name, full_name) == 0) { 327 found = ISC_TRUE; 328 result = state->putrr(lookup, state->current[i].type, 329 state->current[i].ttl, 330 state->current[i].data); 331 if (result != ISC_R_SUCCESS) 332 return (result); 333 } 334 } 335 336 if (!found) 337 return (ISC_R_NOTFOUND); 338 339 return (ISC_R_SUCCESS); 340} 341 342 343/* 344 * See if a zone transfer is allowed 345 */ 346isc_result_t 347dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { 348 UNUSED(client); 349 350 /* Just say yes for all our zones */ 351 return (dlz_findzonedb(dbdata, name)); 352} 353 354/* 355 * Perform a zone transfer 356 */ 357isc_result_t 358dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) { 359 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 360 int i; 361 362 UNUSED(zone); 363 364 for (i = 0; i < MAX_RECORDS; i++) { 365 isc_result_t result; 366 if (strlen(state->current[i].name) == 0U) { 367 continue; 368 } 369 result = state->putnamedrr(allnodes, state->current[i].name, 370 state->current[i].type, 371 state->current[i].ttl, 372 state->current[i].data); 373 if (result != ISC_R_SUCCESS) 374 return (result); 375 } 376 377 return (ISC_R_SUCCESS); 378} 379 380 381/* 382 * Start a transaction 383 */ 384isc_result_t 385dlz_newversion(const char *zone, void *dbdata, void **versionp) { 386 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 387 388 if (state->transaction_started) { 389 state->log(ISC_LOG_INFO, 390 "dlz_example: transaction already " 391 "started for zone %s", zone); 392 return (ISC_R_FAILURE); 393 } 394 395 state->transaction_started = ISC_TRUE; 396 *versionp = (void *) &state->transaction_started; 397 398 return (ISC_R_SUCCESS); 399} 400 401/* 402 * End a transaction 403 */ 404void 405dlz_closeversion(const char *zone, isc_boolean_t commit, 406 void *dbdata, void **versionp) 407{ 408 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 409 410 if (!state->transaction_started) { 411 state->log(ISC_LOG_INFO, 412 "dlz_example: transaction not started for zone %s", 413 zone); 414 *versionp = NULL; 415 return; 416 } 417 418 state->transaction_started = ISC_FALSE; 419 420 *versionp = NULL; 421 422 if (commit) { 423 int i; 424 state->log(ISC_LOG_INFO, 425 "dlz_example: committing transaction on zone %s", 426 zone); 427 for (i = 0; i < MAX_RECORDS; i++) { 428 if (strlen(state->adds[i].name) > 0U) { 429 add_name(state, &state->current[0], 430 state->adds[i].name, 431 state->adds[i].type, 432 state->adds[i].ttl, 433 state->adds[i].data); 434 } 435 } 436 for (i = 0; i < MAX_RECORDS; i++) { 437 if (strlen(state->deletes[i].name) > 0U) { 438 del_name(state, &state->current[0], 439 state->deletes[i].name, 440 state->deletes[i].type, 441 state->deletes[i].ttl, 442 state->deletes[i].data); 443 } 444 } 445 } else { 446 state->log(ISC_LOG_INFO, 447 "dlz_example: cancelling transaction on zone %s", 448 zone); 449 } 450 memset(state->adds, 0, sizeof(state->adds)); 451 memset(state->deletes, 0, sizeof(state->deletes)); 452} 453 454 455/* 456 * Configure a writeable zone 457 */ 458isc_result_t 459dlz_configure(dns_view_t *view, void *dbdata) { 460 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 461 isc_result_t result; 462 463 464 state->log(ISC_LOG_INFO, "dlz_example: starting configure"); 465 if (state->writeable_zone == NULL) { 466 state->log(ISC_LOG_INFO, 467 "dlz_example: no writeable_zone method available"); 468 return (ISC_R_FAILURE); 469 } 470 471 result = state->writeable_zone(view, state->zone_name); 472 if (result != ISC_R_SUCCESS) { 473 state->log(ISC_LOG_ERROR, 474 "dlz_example: failed to configure zone %s", 475 state->zone_name); 476 return (result); 477 } 478 479 state->log(ISC_LOG_INFO, 480 "dlz_example: configured writeable zone %s", 481 state->zone_name); 482 return (ISC_R_SUCCESS); 483} 484 485/* 486 * Authorize a zone update 487 */ 488isc_boolean_t 489dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, 490 const char *type, const char *key, uint32_t keydatalen, 491 unsigned char *keydata, void *dbdata) 492{ 493 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 494 495 UNUSED(tcpaddr); 496 UNUSED(type); 497 UNUSED(key); 498 UNUSED(keydatalen); 499 UNUSED(keydata); 500 501 if (strncmp(name, "deny.", 5) == 0) { 502 state->log(ISC_LOG_INFO, 503 "dlz_example: denying update of name=%s by %s", 504 name, signer); 505 return (ISC_FALSE); 506 } 507 state->log(ISC_LOG_INFO, 508 "dlz_example: allowing update of name=%s by %s", 509 name, signer); 510 return (ISC_TRUE); 511} 512 513 514static isc_result_t 515modrdataset(struct dlz_example_data *state, const char *name, 516 const char *rdatastr, struct record *list) 517{ 518 char *full_name, *dclass, *type, *data, *ttlstr; 519 char *buf = strdup(rdatastr); 520 isc_result_t result; 521#if defined(WIN32) || defined(_REENTRANT) 522 char *saveptr = NULL; 523#endif 524 525 /* 526 * The format is: 527 * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA 528 * 529 * The DATA field is space separated, and is in the data format 530 * for the type used by dig 531 */ 532 533 full_name = STRTOK_R(buf, "\t", &saveptr); 534 if (full_name == NULL) 535 return (ISC_R_FAILURE); 536 537 ttlstr = STRTOK_R(NULL, "\t", &saveptr); 538 if (ttlstr == NULL) 539 return (ISC_R_FAILURE); 540 541 dclass = STRTOK_R(NULL, "\t", &saveptr); 542 if (dclass == NULL) 543 return (ISC_R_FAILURE); 544 545 type = STRTOK_R(NULL, "\t", &saveptr); 546 if (type == NULL) 547 return (ISC_R_FAILURE); 548 549 data = STRTOK_R(NULL, "\t", &saveptr); 550 if (data == NULL) 551 return (ISC_R_FAILURE); 552 553 result = add_name(state, list, name, type, 554 strtoul(ttlstr, NULL, 10), data); 555 free(buf); 556 return (result); 557} 558 559 560isc_result_t 561dlz_addrdataset(const char *name, const char *rdatastr, 562 void *dbdata, void *version) 563{ 564 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 565 566 if (version != (void *) &state->transaction_started) 567 return (ISC_R_FAILURE); 568 569 state->log(ISC_LOG_INFO, 570 "dlz_example: adding rdataset %s '%s'", 571 name, rdatastr); 572 573 return (modrdataset(state, name, rdatastr, &state->adds[0])); 574} 575 576isc_result_t 577dlz_subrdataset(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: subtracting rdataset %s '%s'", 587 name, rdatastr); 588 589 return (modrdataset(state, name, rdatastr, &state->deletes[0])); 590} 591 592 593isc_result_t 594dlz_delrdataset(const char *name, const char *type, 595 void *dbdata, void *version) 596{ 597 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 598 599 if (version != (void *) &state->transaction_started) 600 return (ISC_R_FAILURE); 601 602 state->log(ISC_LOG_INFO, 603 "dlz_example: deleting rdataset %s of type %s", 604 name, type); 605 606 return (ISC_R_SUCCESS); 607} 608