1/* 2 * Copyright (C) 2010 Andrew Tridgell 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR 10 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 11 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 12 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 14 * 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 16 * USE OR 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 <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <stdbool.h> 28#include <stdint.h> 29#include <stdarg.h> 30 31#include "dlz_minimal.h" 32 33 34/* for this simple example, use fixed sized strings */ 35struct record { 36 char name[100]; 37 char type[10]; 38 char data[200]; 39 uint32_t ttl; 40}; 41 42#define MAX_RECORDS 100 43 44struct dlz_example_data { 45 char *zone_name; 46 47 /* an example driver doesn't need good memory management :-) */ 48 struct record current[MAX_RECORDS]; 49 struct record adds[MAX_RECORDS]; 50 struct record deletes[MAX_RECORDS]; 51 52 bool transaction_started; 53 54 /* helper functions from the dlz_dlopen driver */ 55 void (*log)(int level, const char *fmt, ...); 56 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type, 57 dns_ttl_t ttl, const char *data); 58 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name, 59 const char *type, dns_ttl_t ttl, const char *data); 60 isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name); 61}; 62 63static bool single_valued(const char *type) 64{ 65 const char *single[] = { "soa", "cname", NULL }; 66 int i; 67 for (i=0; single[i]; i++) { 68 if (strcasecmp(single[i], type) == 0) { 69 return true; 70 } 71 } 72 return false; 73} 74 75/* 76 add a record to a list 77 */ 78static isc_result_t add_name(struct dlz_example_data *state, 79 struct record *list, const char *name, const char *type, 80 uint32_t ttl, const char *data) 81{ 82 int i; 83 bool single = single_valued(type); 84 int first_empty = -1; 85 86 for (i=0; i<MAX_RECORDS; i++) { 87 if (first_empty == -1 && strlen(list[i].name) == 0) { 88 first_empty = i; 89 } 90 if (strcasecmp(list[i].name, name) != 0) 91 continue; 92 if (strcasecmp(list[i].type, type) != 0) 93 continue; 94 if (!single && strcasecmp(list[i].data, data) != 0) 95 continue; 96 break; 97 } 98 if (i == MAX_RECORDS && first_empty != -1) { 99 i = first_empty; 100 } 101 if (i == MAX_RECORDS) { 102 state->log(ISC_LOG_ERROR, "dlz_example: out of record space"); 103 return ISC_R_FAILURE; 104 } 105 strcpy(list[i].name, name); 106 strcpy(list[i].type, type); 107 strcpy(list[i].data, data); 108 list[i].ttl = ttl; 109 return ISC_R_SUCCESS; 110} 111 112/* 113 delete a record from a list 114 */ 115static isc_result_t del_name(struct dlz_example_data *state, 116 struct record *list, const char *name, const char *type, 117 uint32_t ttl, const char *data) 118{ 119 int i; 120 for (i=0; i<MAX_RECORDS; i++) { 121 if (strcasecmp(name, list[i].name) == 0 && 122 strcasecmp(type, list[i].type) == 0 && 123 strcasecmp(data, list[i].data) == 0 && 124 ttl == list[i].ttl) { 125 break; 126 } 127 } 128 if (i == MAX_RECORDS) { 129 return ISC_R_NOTFOUND; 130 } 131 memset(&list[i], 0, sizeof(struct record)); 132 return ISC_R_SUCCESS; 133} 134 135 136 137/* 138 return the version of the API 139 */ 140int dlz_version(unsigned int *flags) 141{ 142 return DLZ_DLOPEN_VERSION; 143} 144 145/* 146 remember a helper function from the bind9 dlz_dlopen driver 147 */ 148static void b9_add_helper(struct dlz_example_data *state, const char *helper_name, void *ptr) 149{ 150 if (strcmp(helper_name, "log") == 0) { 151 state->log = ptr; 152 } 153 if (strcmp(helper_name, "putrr") == 0) { 154 state->putrr = ptr; 155 } 156 if (strcmp(helper_name, "putnamedrr") == 0) { 157 state->putnamedrr = ptr; 158 } 159 if (strcmp(helper_name, "writeable_zone") == 0) { 160 state->writeable_zone = ptr; 161 } 162} 163 164 165/* 166 called to initialise the driver 167 */ 168isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], 169 void **dbdata, ...) 170{ 171 struct dlz_example_data *state; 172 const char *helper_name; 173 va_list ap; 174 char soa_data[200]; 175 176 state = calloc(1, sizeof(struct dlz_example_data)); 177 if (state == NULL) { 178 return ISC_R_NOMEMORY; 179 } 180 181 /* fill in the helper functions */ 182 va_start(ap, dbdata); 183 while ((helper_name = va_arg(ap, const char *)) != NULL) { 184 b9_add_helper(state, helper_name, va_arg(ap, void*)); 185 } 186 va_end(ap); 187 188 if (argc < 2) { 189 state->log(ISC_LOG_ERROR, "dlz_example: please specify a zone name"); 190 return ISC_R_FAILURE; 191 } 192 193 state->zone_name = strdup(argv[1]); 194 195 sprintf(soa_data, "%s hostmaster.%s 123 900 600 86400 3600", 196 state->zone_name, state->zone_name); 197 198 add_name(state, &state->current[0], state->zone_name, "soa", 3600, soa_data); 199 add_name(state, &state->current[0], state->zone_name, "ns", 3600, state->zone_name); 200 add_name(state, &state->current[0], state->zone_name, "a", 1800, "10.53.0.1"); 201 202 state->log(ISC_LOG_INFO, "dlz_example: started for zone %s", state->zone_name); 203 204 *dbdata = state; 205 return ISC_R_SUCCESS; 206} 207 208/* 209 shutdown the backend 210 */ 211void dlz_destroy(void *dbdata) 212{ 213 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 214 state->log(ISC_LOG_INFO, "dlz_example: shutting down zone %s", state->zone_name); 215 free(state->zone_name); 216 free(state); 217} 218 219 220/* 221 see if we handle a given zone 222 */ 223isc_result_t dlz_findzonedb(void *dbdata, const char *name) 224{ 225 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 226 if (strcasecmp(state->zone_name, name) == 0) { 227 return ISC_R_SUCCESS; 228 } 229 return ISC_R_NOTFOUND; 230} 231 232 233 234/* 235 lookup one record 236 */ 237isc_result_t dlz_lookup(const char *zone, const char *name, 238 void *dbdata, dns_sdlzlookup_t *lookup) 239{ 240 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 241 int i; 242 bool found = false; 243 char full_name[100]; 244 245 if (strcmp(name, "@") == 0) { 246 strcpy(full_name, state->zone_name); 247 } else { 248 sprintf(full_name, "%s.%s", name, state->zone_name); 249 } 250 for (i=0; i<MAX_RECORDS; i++) { 251 if (strcasecmp(state->current[i].name, full_name) == 0) { 252 isc_result_t result; 253 found = true; 254 result = state->putrr(lookup, state->current[i].type, 255 state->current[i].ttl, state->current[i].data); 256 if (result != ISC_R_SUCCESS) { 257 return result; 258 } 259 } 260 } 261 if (!found) { 262 return ISC_R_NOTFOUND; 263 } 264 return ISC_R_SUCCESS; 265} 266 267 268/* 269 see if a zone transfer is allowed 270 */ 271isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client) 272{ 273 /* just say yes for all our zones */ 274 return dlz_findzonedb(dbdata, name); 275} 276 277/* 278 perform a zone transfer 279 */ 280isc_result_t dlz_allnodes(const char *zone, void *dbdata, 281 dns_sdlzallnodes_t *allnodes) 282{ 283 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 284 int i; 285 286 for (i=0; i<MAX_RECORDS; i++) { 287 isc_result_t result; 288 if (strlen(state->current[i].name) == 0) { 289 continue; 290 } 291 result = state->putnamedrr(allnodes, state->current[i].name, state->current[i].type, 292 state->current[i].ttl, state->current[i].data); 293 if (result != ISC_R_SUCCESS) { 294 return result; 295 } 296 } 297 298 return ISC_R_SUCCESS; 299} 300 301 302/* 303 start a transaction 304 */ 305isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp) 306{ 307 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 308 309 if (state->transaction_started) { 310 state->log(ISC_LOG_INFO, "dlz_example: transaction already started for zone %s", zone); 311 return ISC_R_FAILURE; 312 } 313 314 state->transaction_started = true; 315 316 *versionp = (void *) &state->transaction_started; 317 318 return ISC_R_SUCCESS; 319} 320 321/* 322 end a transaction 323 */ 324void dlz_closeversion(const char *zone, isc_boolean_t commit, void *dbdata, void **versionp) 325{ 326 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 327 328 if (!state->transaction_started) { 329 state->log(ISC_LOG_INFO, "dlz_example: transaction not started for zone %s", zone); 330 *versionp = NULL; 331 return; 332 } 333 334 state->transaction_started = false; 335 336 *versionp = NULL; 337 338 if (commit) { 339 int i; 340 state->log(ISC_LOG_INFO, "dlz_example: committing transaction on zone %s", zone); 341 for (i=0; i<MAX_RECORDS; i++) { 342 if (strlen(state->adds[i].name) > 0) { 343 add_name(state, &state->current[0], 344 state->adds[i].name, 345 state->adds[i].type, 346 state->adds[i].ttl, 347 state->adds[i].data); 348 } 349 } 350 for (i=0; i<MAX_RECORDS; i++) { 351 if (strlen(state->deletes[i].name) > 0) { 352 del_name(state, &state->current[0], 353 state->deletes[i].name, 354 state->deletes[i].type, 355 state->deletes[i].ttl, 356 state->deletes[i].data); 357 } 358 } 359 } else { 360 state->log(ISC_LOG_INFO, "dlz_example: cancelling transaction on zone %s", zone); 361 } 362 memset(state->adds, 0, sizeof(state->adds)); 363 memset(state->deletes, 0, sizeof(state->deletes)); 364} 365 366 367/* 368 configure a writeable zone 369 */ 370isc_result_t dlz_configure(dns_view_t *view, void *dbdata) 371{ 372 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 373 isc_result_t result; 374 375 376 state->log(ISC_LOG_INFO, "dlz_example: starting configure"); 377 if (state->writeable_zone == NULL) { 378 state->log(ISC_LOG_INFO, "dlz_example: no writeable_zone method available"); 379 return ISC_R_FAILURE; 380 } 381 382 result = state->writeable_zone(view, state->zone_name); 383 if (result != ISC_R_SUCCESS) { 384 state->log(ISC_LOG_ERROR, "dlz_example: failed to configure zone %s", state->zone_name); 385 return result; 386 } 387 388 state->log(ISC_LOG_INFO, "dlz_example: configured writeable zone %s", state->zone_name); 389 return ISC_R_SUCCESS; 390} 391 392/* 393 authorize a zone update 394 */ 395isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, 396 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata, 397 void *dbdata) 398{ 399 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 400 if (strncmp(name, "deny.", 5) == 0) { 401 state->log(ISC_LOG_INFO, "dlz_example: denying update of name=%s by %s", 402 name, signer); 403 return false; 404 } 405 state->log(ISC_LOG_INFO, "dlz_example: allowing update of name=%s by %s", 406 name, signer); 407 return true; 408} 409 410 411static isc_result_t modrdataset(struct dlz_example_data *state, const char *name, const char *rdatastr, 412 struct record *list) 413{ 414 char *full_name, *dclass, *type, *data, *ttlstr; 415 char *buf = strdup(rdatastr); 416 isc_result_t result; 417 char *saveptr = NULL; 418 419 /* 420 the format is: 421 FULLNAME\tTTL\tDCLASS\tTYPE\tDATA 422 423 The DATA field is space separated, and is in the data format 424 for the type used by dig 425 */ 426 427 full_name = strtok_r(buf, "\t", &saveptr); 428 if (full_name == NULL) return ISC_R_FAILURE; 429 ttlstr = strtok_r(NULL, "\t", &saveptr); 430 if (ttlstr == NULL) return ISC_R_FAILURE; 431 dclass = strtok_r(NULL, "\t", &saveptr); 432 if (dclass == NULL) return ISC_R_FAILURE; 433 type = strtok_r(NULL, "\t", &saveptr); 434 if (type == NULL) return ISC_R_FAILURE; 435 data = strtok_r(NULL, "\t", &saveptr); 436 if (data == NULL) return ISC_R_FAILURE; 437 438 result = add_name(state, list, name, type, strtoul(ttlstr, NULL, 10), data); 439 free(buf); 440 return result; 441} 442 443 444isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version) 445{ 446 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 447 448 if (version != (void *) &state->transaction_started) { 449 return ISC_R_FAILURE; 450 } 451 452 state->log(ISC_LOG_INFO, "dlz_example: adding rdataset %s '%s'", name, rdatastr); 453 454 return modrdataset(state, name, rdatastr, &state->adds[0]); 455} 456 457isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version) 458{ 459 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 460 461 if (version != (void *) &state->transaction_started) { 462 return ISC_R_FAILURE; 463 } 464 465 state->log(ISC_LOG_INFO, "dlz_example: subtracting rdataset %s '%s'", name, rdatastr); 466 467 return modrdataset(state, name, rdatastr, &state->deletes[0]); 468} 469 470 471isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version) 472{ 473 struct dlz_example_data *state = (struct dlz_example_data *)dbdata; 474 475 if (version != (void *) &state->transaction_started) { 476 return ISC_R_FAILURE; 477 } 478 479 state->log(ISC_LOG_INFO, "dlz_example: deleting rdataset %s of type %s", name, type); 480 481 return ISC_R_SUCCESS; 482} 483