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