1 /* $NetBSD: sdlz_helper.c,v 1.5 2014/12/10 04:37:55 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the 8 * above copyright notice and this permission notice appear in all 9 * copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET 12 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 14 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 16 * 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 18 * USE OR PERFORMANCE OF THIS SOFTWARE. 19 * 20 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was 21 * conceived and contributed by Rob Butler. 22 * 23 * Permission to use, copy, modify, and distribute this software for any 24 * purpose with or without fee is hereby granted, provided that the 25 * above copyright notice and this permission notice appear in all 26 * copies. 27 * 28 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER 29 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 31 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 34 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 35 * USE OR PERFORMANCE OF THIS SOFTWARE. 36 */ 37 38 /* 39 * Copyright (C) 1999-2001 Internet Software Consortium. 40 * 41 * Permission to use, copy, modify, and distribute this software for any 42 * purpose with or without fee is hereby granted, provided that the above 43 * copyright notice and this permission notice appear in all copies. 44 * 45 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 46 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 48 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 49 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 50 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 51 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 52 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 53 */ 54 55 #include <config.h> 56 57 #include <dns/log.h> 58 #include <dns/result.h> 59 60 #include <isc/mem.h> 61 #include <isc/result.h> 62 #include <isc/string.h> 63 #include <isc/util.h> 64 65 #include <dlz/sdlz_helper.h> 66 67 /* 68 * sdlz helper methods 69 */ 70 71 /*% 72 * properly destroys a querylist by de-allocating the 73 * memory for each query segment, and then the list itself 74 */ 75 76 static void 77 destroy_querylist(isc_mem_t *mctx, query_list_t **querylist) 78 { 79 query_segment_t *tseg = NULL; 80 query_segment_t *nseg = NULL; 81 82 REQUIRE(mctx != NULL); 83 84 /* if query list is null, nothing to do */ 85 if (*querylist == NULL) 86 return; 87 88 /* start at the top of the list */ 89 nseg = ISC_LIST_HEAD(**querylist); 90 while (nseg != NULL) { /* loop, until end of list */ 91 tseg = nseg; 92 /* 93 * free the query segment's text string but only if it 94 * was really a query segment, and not a pointer to 95 * %zone%, or %record%, or %client% 96 */ 97 if (tseg->sql != NULL && tseg->direct == isc_boolean_true) 98 isc_mem_free(mctx, tseg->sql); 99 /* get the next query segment, before we destroy this one. */ 100 nseg = ISC_LIST_NEXT(nseg, link); 101 /* deallocate this query segment. */ 102 isc_mem_put(mctx, tseg, sizeof(query_segment_t)); 103 } 104 /* deallocate the query segment list */ 105 isc_mem_put(mctx, *querylist, sizeof(query_list_t)); 106 } 107 108 /*% constructs a query list by parsing a string into query segments */ 109 static isc_result_t 110 build_querylist(isc_mem_t *mctx, const char *query_str, char **zone, 111 char **record, char **client, query_list_t **querylist, 112 unsigned int flags) 113 { 114 isc_result_t result; 115 isc_boolean_t foundzone = isc_boolean_false; 116 isc_boolean_t foundrecord = isc_boolean_false; 117 isc_boolean_t foundclient = isc_boolean_false; 118 char *temp_str = NULL; 119 char *right_str = NULL; 120 query_list_t *tql; 121 query_segment_t *tseg = NULL; 122 123 REQUIRE(querylist != NULL && *querylist == NULL); 124 REQUIRE(mctx != NULL); 125 126 /* if query string is null, or zero length */ 127 if (query_str == NULL || strlen(query_str) < 1) { 128 if ((flags & SDLZH_REQUIRE_QUERY) == 0) 129 /* we don't need it were ok. */ 130 return (ISC_R_SUCCESS); 131 else 132 /* we did need it, PROBLEM!!! */ 133 return (ISC_R_FAILURE); 134 } 135 136 /* allocate memory for query list */ 137 tql = isc_mem_get(mctx, sizeof(query_list_t)); 138 /* couldn't allocate memory. Problem!! */ 139 if (tql == NULL) 140 return (ISC_R_NOMEMORY); 141 142 /* initialize the query segment list */ 143 ISC_LIST_INIT(*tql); 144 145 /* make a copy of query_str so we can chop it up */ 146 temp_str = right_str = isc_mem_strdup(mctx, query_str); 147 /* couldn't make a copy, problem!! */ 148 if (right_str == NULL) { 149 result = ISC_R_NOMEMORY; 150 goto cleanup; 151 } 152 153 /* loop through the string and chop it up */ 154 while (right_str != NULL) { 155 /* allocate memory for tseg */ 156 tseg = isc_mem_get(mctx, sizeof(query_segment_t)); 157 if (tseg == NULL) { /* no memory, clean everything up. */ 158 result = ISC_R_NOMEMORY; 159 goto cleanup; 160 } 161 tseg->sql = NULL; 162 tseg->direct = isc_boolean_false; 163 /* initialize the query segment link */ 164 ISC_LINK_INIT(tseg, link); 165 /* append the query segment to the list */ 166 ISC_LIST_APPEND(*tql, tseg, link); 167 168 /* 169 * split string at the first "$". set query segment to 170 * left portion 171 */ 172 tseg->sql = isc_mem_strdup(mctx, 173 isc_string_separate(&right_str, 174 "$")); 175 if (tseg->sql == NULL) { 176 /* no memory, clean everything up. */ 177 result = ISC_R_NOMEMORY; 178 goto cleanup; 179 } 180 /* tseg->sql points directly to a string. */ 181 tseg->direct = isc_boolean_true; 182 tseg->strlen = strlen(tseg->sql); 183 184 /* check if we encountered "$zone$" token */ 185 if (strcasecmp(tseg->sql, "zone") == 0) { 186 /* 187 * we don't really need, or want the "zone" 188 * text, so get rid of it. 189 */ 190 isc_mem_free(mctx, tseg->sql); 191 /* set tseg->sql to in-direct zone string */ 192 tseg->sql = (char**) zone; 193 tseg->strlen = 0; 194 /* tseg->sql points in-directly to a string */ 195 tseg->direct = isc_boolean_false; 196 foundzone = isc_boolean_true; 197 /* check if we encountered "$record$" token */ 198 } else if (strcasecmp(tseg->sql, "record") == 0) { 199 /* 200 * we don't really need, or want the "record" 201 * text, so get rid of it. 202 */ 203 isc_mem_free(mctx, tseg->sql); 204 /* set tseg->sql to in-direct record string */ 205 tseg->sql = (char**) record; 206 tseg->strlen = 0; 207 /* tseg->sql points in-directly poinsts to a string */ 208 tseg->direct = isc_boolean_false; 209 foundrecord = isc_boolean_true; 210 /* check if we encountered "$client$" token */ 211 } else if (strcasecmp(tseg->sql, "client") == 0) { 212 /* 213 * we don't really need, or want the "client" 214 * text, so get rid of it. 215 */ 216 isc_mem_free(mctx, tseg->sql); 217 /* set tseg->sql to in-direct record string */ 218 tseg->sql = (char**) client; 219 tseg->strlen = 0; 220 /* tseg->sql points in-directly poinsts to a string */ 221 tseg->direct = isc_boolean_false; 222 foundclient = isc_boolean_true; 223 } 224 } 225 226 /* we don't need temp_str any more */ 227 isc_mem_free(mctx, temp_str); 228 /* 229 * add checks later to verify zone and record are found if 230 * necessary. 231 */ 232 233 /* if this query requires %client%, make sure we found it */ 234 if (((flags & SDLZH_REQUIRE_CLIENT) != 0) && (!foundclient) ) { 235 /* Write error message to log */ 236 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 237 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 238 "Required token $client$ not found."); 239 result = ISC_R_FAILURE; 240 goto flag_fail; 241 } 242 243 /* if this query requires %record%, make sure we found it */ 244 if (((flags & SDLZH_REQUIRE_RECORD) != 0) && (!foundrecord) ) { 245 /* Write error message to log */ 246 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 247 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 248 "Required token $record$ not found."); 249 result = ISC_R_FAILURE; 250 goto flag_fail; 251 } 252 253 /* if this query requires %zone%, make sure we found it */ 254 if (((flags & SDLZH_REQUIRE_ZONE) != 0) && (!foundzone) ) { 255 /* Write error message to log */ 256 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 257 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 258 "Required token $zone$ not found."); 259 result = ISC_R_FAILURE; 260 goto flag_fail; 261 } 262 263 /* pass back the query list */ 264 *querylist = (query_list_t *) tql; 265 266 /* return success */ 267 return (ISC_R_SUCCESS); 268 269 cleanup: 270 /* get rid of temp_str */ 271 if (temp_str != NULL) 272 isc_mem_free(mctx, temp_str); 273 274 flag_fail: 275 /* get rid of what was build of the query list */ 276 if (tql != NULL) 277 destroy_querylist(mctx, &tql); 278 return result; 279 } 280 281 /*% 282 * build a query string from query segments, and dynamic segments 283 * dynamic segments replace where the tokens %zone%, %record%, %client% 284 * used to be in our queries from named.conf 285 */ 286 char * 287 sdlzh_build_querystring(isc_mem_t *mctx, query_list_t *querylist) 288 { 289 query_segment_t *tseg = NULL; 290 unsigned int length = 0; 291 char *qs = NULL; 292 293 REQUIRE(mctx != NULL); 294 REQUIRE(querylist != NULL); 295 296 /* start at the top of the list */ 297 tseg = ISC_LIST_HEAD(*querylist); 298 while (tseg != NULL) { 299 /* 300 * if this is a query segment, use the 301 * precalculated string length 302 */ 303 if (tseg->direct == isc_boolean_true) 304 length += tseg->strlen; 305 else /* calculate string length for dynamic segments. */ 306 length += strlen(* (char**) tseg->sql); 307 /* get the next segment */ 308 tseg = ISC_LIST_NEXT(tseg, link); 309 } 310 311 /* allocate memory for the string */ 312 qs = isc_mem_allocate(mctx, length + 1); 313 /* couldn't allocate memory, We need more ram! */ 314 if (qs == NULL) 315 return NULL; 316 317 *qs = 0; 318 /* start at the top of the list again */ 319 tseg = ISC_LIST_HEAD(*querylist); 320 while (tseg != NULL) { 321 if (tseg->direct == isc_boolean_true) 322 /* query segments */ 323 strcat(qs, tseg->sql); 324 else 325 /* dynamic segments */ 326 strcat(qs, * (char**) tseg->sql); 327 /* get the next segment */ 328 tseg = ISC_LIST_NEXT(tseg, link); 329 } 330 331 return qs; 332 } 333 334 /*% constructs a sql dbinstance (DBI) */ 335 isc_result_t 336 sdlzh_build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, 337 const char *allowxfr_str, const char *authority_str, 338 const char *findzone_str, const char *lookup_str, 339 const char *countzone_str, dbinstance_t **dbi) 340 { 341 342 isc_result_t result; 343 dbinstance_t *db = NULL; 344 345 REQUIRE(dbi != NULL && *dbi == NULL); 346 REQUIRE(mctx != NULL); 347 348 /* allocate and zero memory for driver structure */ 349 db = isc_mem_get(mctx, sizeof(dbinstance_t)); 350 if (db == NULL) { 351 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 352 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 353 "Could not allocate memory for " 354 "database instance object."); 355 return (ISC_R_NOMEMORY); 356 } 357 memset(db, 0, sizeof(dbinstance_t)); 358 db->dbconn = NULL; 359 db->client = NULL; 360 db->record = NULL; 361 db->zone = NULL; 362 db->mctx = NULL; 363 db->query_buf = NULL; 364 db->allnodes_q = NULL; 365 db->allowxfr_q = NULL; 366 db->authority_q = NULL; 367 db->findzone_q = NULL; 368 db->countzone_q = NULL; 369 db->lookup_q = NULL; 370 371 /* attach to the memory context */ 372 isc_mem_attach(mctx, &db->mctx); 373 374 /* initialize the reference count mutex */ 375 result = isc_mutex_init(&db->instance_lock); 376 if (result != ISC_R_SUCCESS) { 377 UNEXPECTED_ERROR(__FILE__, __LINE__, 378 "isc_mutex_init() failed: %s", 379 isc_result_totext(result)); 380 goto cleanup; 381 } 382 383 /* build the all nodes query list */ 384 result = build_querylist(mctx, allnodes_str, &db->zone, 385 &db->record, &db->client, 386 &db->allnodes_q, SDLZH_REQUIRE_ZONE); 387 /* if unsuccessful, log err msg and cleanup */ 388 if (result != ISC_R_SUCCESS) { 389 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 390 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 391 "Could not build all nodes query list"); 392 goto cleanup; 393 } 394 395 /* build the allow zone transfer query list */ 396 result = build_querylist(mctx, allowxfr_str, &db->zone, 397 &db->record, &db->client, 398 &db->allowxfr_q, 399 SDLZH_REQUIRE_ZONE | SDLZH_REQUIRE_CLIENT); 400 /* if unsuccessful, log err msg and cleanup */ 401 if (result != ISC_R_SUCCESS) { 402 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 403 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 404 "Could not build allow xfr query list"); 405 goto cleanup; 406 } 407 408 /* build the authority query, query list */ 409 result = build_querylist(mctx, authority_str, &db->zone, 410 &db->record, &db->client, 411 &db->authority_q, SDLZH_REQUIRE_ZONE); 412 /* if unsuccessful, log err msg and cleanup */ 413 if (result != ISC_R_SUCCESS) { 414 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 415 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 416 "Could not build authority query list"); 417 goto cleanup; 418 } 419 420 /* build findzone query, query list */ 421 result = build_querylist(mctx, findzone_str, &db->zone, 422 &db->record, &db->client, 423 &db->findzone_q, SDLZH_REQUIRE_ZONE); 424 /* if unsuccessful, log err msg and cleanup */ 425 if (result != ISC_R_SUCCESS) { 426 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 427 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 428 "Could not build find zone query list"); 429 goto cleanup; 430 } 431 432 /* build countzone query, query list */ 433 result = build_querylist(mctx, countzone_str, &db->zone, 434 &db->record, &db->client, 435 &db->countzone_q, SDLZH_REQUIRE_ZONE); 436 /* if unsuccessful, log err msg and cleanup */ 437 if (result != ISC_R_SUCCESS) { 438 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 439 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 440 "Could not build count zone query list"); 441 goto cleanup; 442 } 443 444 /* build lookup query, query list */ 445 result = build_querylist(mctx, lookup_str, &db->zone, 446 &db->record, &db->client, 447 &db->lookup_q, SDLZH_REQUIRE_RECORD); 448 /* if unsuccessful, log err msg and cleanup */ 449 if (result != ISC_R_SUCCESS) { 450 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 451 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 452 "Could not build lookup query list"); 453 goto cleanup; 454 } 455 456 /* pass back the db instance */ 457 *dbi = (dbinstance_t *) db; 458 459 /* return success */ 460 return (ISC_R_SUCCESS); 461 462 cleanup: 463 /* destroy whatever was build of the db instance */ 464 destroy_sqldbinstance(db); 465 /* return failure */ 466 return (ISC_R_FAILURE); 467 } 468 469 void 470 sdlzh_destroy_sqldbinstance(dbinstance_t *dbi) 471 { 472 isc_mem_t *mctx; 473 474 /* save mctx for later */ 475 mctx = dbi->mctx; 476 477 /* destroy any query lists we created */ 478 destroy_querylist(mctx, &dbi->allnodes_q); 479 destroy_querylist(mctx, &dbi->allowxfr_q); 480 destroy_querylist(mctx, &dbi->authority_q); 481 destroy_querylist(mctx, &dbi->findzone_q); 482 destroy_querylist(mctx, &dbi->countzone_q); 483 destroy_querylist(mctx, &dbi->lookup_q); 484 485 /* get rid of the mutex */ 486 (void) isc_mutex_destroy(&dbi->instance_lock); 487 488 /* return, and detach the memory */ 489 isc_mem_put(mctx, dbi, sizeof(dbinstance_t)); 490 isc_mem_detach(&mctx); 491 } 492 493 char * 494 sdlzh_get_parameter_value(isc_mem_t *mctx, const char *input, const char* key) 495 { 496 int keylen; 497 char *keystart; 498 char value[255]; 499 int i; 500 501 if (key == NULL || input == NULL || strlen(input) < 1) 502 return NULL; 503 504 keylen = strlen(key); 505 506 if (keylen < 1) 507 return NULL; 508 509 keystart = strstr(input, key); 510 511 if (keystart == NULL) 512 return NULL; 513 514 REQUIRE(mctx != NULL); 515 516 for (i = 0; i < 255; i++) { 517 value[i] = keystart[keylen + i]; 518 if (value[i] == ' ' || value[i] == '\0') { 519 value[i] = '\0'; 520 break; 521 } 522 } 523 524 return isc_mem_strdup(mctx, value); 525 } 526