1 /* $NetBSD: dlz_postgres_driver.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 #ifdef DLZ_POSTGRES 56 57 #include <config.h> 58 #include <stdio.h> 59 #include <string.h> 60 #include <stdlib.h> 61 62 #include <dns/log.h> 63 #include <dns/sdlz.h> 64 #include <dns/result.h> 65 66 #include <isc/mem.h> 67 #include <isc/platform.h> 68 #include <isc/print.h> 69 #include <isc/result.h> 70 #include <isc/string.h> 71 #include <isc/util.h> 72 73 #include <named/globals.h> 74 75 #include <dlz/sdlz_helper.h> 76 #include <dlz/dlz_postgres_driver.h> 77 78 /* temporarily include time. */ 79 #include <time.h> 80 81 #include <libpq-fe.h> 82 83 static dns_sdlzimplementation_t *dlz_postgres = NULL; 84 85 #define dbc_search_limit 30 86 #define ALLNODES 1 87 #define ALLOWXFR 2 88 #define AUTHORITY 3 89 #define FINDZONE 4 90 #define LOOKUP 5 91 92 /* 93 * Private methods 94 */ 95 96 /* --------------- 97 * Escaping arbitrary strings to get valid SQL strings/identifiers. 98 * 99 * Replaces "\\" with "\\\\" and "'" with "''". 100 * length is the length of the buffer pointed to by 101 * from. The buffer at to must be at least 2*length + 1 characters 102 * long. A terminating NUL character is written. 103 * 104 * NOTICE!!! 105 * This function was borrowed directly from PostgreSQL's libpq. 106 * The function was originally called PQescapeString and renamed 107 * to postgres_makesafe to avoid a naming collision. 108 * PQescapeString is a new function made available in Postgres 7.2. 109 * For some reason the function is not properly exported on Win32 110 * builds making the function unavailable on Windows. Also, since 111 * this function is new it would require building this driver with 112 * the libpq 7.2. By borrowing this function the Windows problem 113 * is solved, and the dependence on libpq 7.2 is removed. Libpq is 114 * still required of course, but an older version should work now too. 115 * 116 * The copyright statements from the original file containing this 117 * function are included below: 118 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group 119 * Portions Copyright (c) 1994, Regents of the University of California 120 * --------------- 121 */ 122 123 static size_t 124 postgres_makesafe(char *to, const char *from, size_t length) 125 { 126 const char *source = from; 127 char *target = to; 128 unsigned int remaining = length; 129 130 while (remaining > 0) 131 { 132 switch (*source) 133 { 134 case '\\': 135 *target = '\\'; 136 target++; 137 *target = '\\'; 138 /* target and remaining are updated below. */ 139 break; 140 141 case '\'': 142 *target = '\''; 143 target++; 144 *target = '\''; 145 /* target and remaining are updated below. */ 146 break; 147 148 default: 149 *target = *source; 150 /* target and remaining are updated below. */ 151 } 152 source++; 153 target++; 154 remaining--; 155 } 156 157 /* Write the terminating NUL character. */ 158 *target = '\0'; 159 160 return target - to; 161 } 162 163 #ifdef ISC_PLATFORM_USETHREADS 164 165 /*% 166 * Properly cleans up a list of database instances. 167 * This function is only used when the driver is compiled for 168 * multithreaded operation. 169 */ 170 static void 171 postgres_destroy_dblist(db_list_t *dblist) 172 { 173 174 dbinstance_t *ndbi = NULL; 175 dbinstance_t *dbi = NULL; 176 177 /* get the first DBI in the list */ 178 ndbi = ISC_LIST_HEAD(*dblist); 179 180 /* loop through the list */ 181 while (ndbi != NULL) { 182 dbi = ndbi; 183 /* get the next DBI in the list */ 184 ndbi = ISC_LIST_NEXT(dbi, link); 185 /* release DB connection */ 186 if (dbi->dbconn != NULL) 187 PQfinish((PGconn *) dbi->dbconn); 188 /* release all memory that comprised a DBI */ 189 destroy_sqldbinstance(dbi); 190 } 191 /* release memory for the list structure */ 192 isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t)); 193 } 194 195 /*% 196 * Loops through the list of DB instances, attempting to lock 197 * on the mutex. If successful, the DBI is reserved for use 198 * and the thread can perform queries against the database. 199 * If the lock fails, the next one in the list is tried. 200 * looping continues until a lock is obtained, or until 201 * the list has been searched dbc_search_limit times. 202 * This function is only used when the driver is compiled for 203 * multithreaded operation. 204 */ 205 206 static dbinstance_t * 207 postgres_find_avail_conn(db_list_t *dblist) 208 { 209 dbinstance_t *dbi = NULL; 210 dbinstance_t *head; 211 int count = 0; 212 213 /* get top of list */ 214 head = dbi = ISC_LIST_HEAD(*dblist); 215 216 /* loop through list */ 217 while (count < dbc_search_limit) { 218 /* try to lock on the mutex */ 219 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) 220 return dbi; /* success, return the DBI for use. */ 221 222 /* not successful, keep trying */ 223 dbi = ISC_LIST_NEXT(dbi, link); 224 225 /* check to see if we have gone to the top of the list. */ 226 if (dbi == NULL) { 227 count++; 228 dbi = head; 229 } 230 } 231 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 232 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 233 "Postgres driver unable to find available connection " 234 "after searching %d times", 235 count); 236 return NULL; 237 } 238 239 #endif /* ISC_PLATFORM_USETHREADS */ 240 241 /*% 242 * Allocates memory for a new string, and then constructs the new 243 * string by "escaping" the input string. The new string is 244 * safe to be used in queries. This is necessary because we cannot 245 * be sure of what types of strings are passed to us, and we don't 246 * want special characters in the string causing problems. 247 */ 248 249 static char * 250 postgres_escape_string(const char *instr) { 251 252 char *outstr; 253 unsigned int len; 254 255 if (instr == NULL) 256 return NULL; 257 258 len = strlen(instr); 259 260 outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); 261 if (outstr == NULL) 262 return NULL; 263 264 postgres_makesafe(outstr, instr, len); 265 /* PQescapeString(outstr, instr, len); */ 266 267 return outstr; 268 } 269 270 /*% 271 * This function is the real core of the driver. Zone, record 272 * and client strings are passed in (or NULL is passed if the 273 * string is not available). The type of query we want to run 274 * is indicated by the query flag, and the dbdata object is passed 275 * passed in to. dbdata really holds either: 276 * 1) a list of database instances (in multithreaded mode) OR 277 * 2) a single database instance (in single threaded mode) 278 * The function will construct the query and obtain an available 279 * database instance (DBI). It will then run the query and hopefully 280 * obtain a result set. Postgres is nice, in that once the result 281 * set is returned, we can make the db connection available for another 282 * thread to use, while this thread continues on. So, the DBI is made 283 * available ASAP by unlocking the instance_lock after we have cleaned 284 * it up properly. 285 */ 286 static isc_result_t 287 postgres_get_resultset(const char *zone, const char *record, 288 const char *client, unsigned int query, 289 void *dbdata, PGresult **rs) 290 { 291 isc_result_t result; 292 dbinstance_t *dbi = NULL; 293 char *querystring = NULL; 294 unsigned int i = 0; 295 unsigned int j = 0; 296 297 #if 0 298 /* temporarily get a unique thread # */ 299 unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0)); 300 #endif 301 302 REQUIRE(*rs == NULL); 303 304 #if 0 305 /* temporary logging message */ 306 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 307 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 308 "%d Getting DBI", dlz_thread_num); 309 #endif 310 311 /* get db instance / connection */ 312 #ifdef ISC_PLATFORM_USETHREADS 313 314 /* find an available DBI from the list */ 315 dbi = postgres_find_avail_conn((db_list_t *) dbdata); 316 317 #else /* ISC_PLATFORM_USETHREADS */ 318 319 /* 320 * only 1 DBI - no need to lock instance lock either 321 * only 1 thread in the whole process, no possible contention. 322 */ 323 dbi = (dbinstance_t *) dbdata; 324 325 #endif /* ISC_PLATFORM_USETHREADS */ 326 327 #if 0 328 /* temporary logging message */ 329 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 330 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 331 "%d Got DBI - checking query", dlz_thread_num); 332 #endif 333 334 /* if DBI is null, can't do anything else */ 335 if (dbi == NULL) { 336 result = ISC_R_FAILURE; 337 goto cleanup; 338 } 339 340 /* what type of query are we going to run? */ 341 switch(query) { 342 case ALLNODES: 343 /* 344 * if the query was not passed in from the config file 345 * then we can't run it. return not_implemented, so 346 * it's like the code for that operation was never 347 * built into the driver.... AHHH flexibility!!! 348 */ 349 if (dbi->allnodes_q == NULL) { 350 result = ISC_R_NOTIMPLEMENTED; 351 goto cleanup; 352 } 353 break; 354 case ALLOWXFR: 355 /* same as comments as ALLNODES */ 356 if (dbi->allowxfr_q == NULL) { 357 result = ISC_R_NOTIMPLEMENTED; 358 goto cleanup; 359 } 360 break; 361 case AUTHORITY: 362 /* same as comments as ALLNODES */ 363 if (dbi->authority_q == NULL) { 364 result = ISC_R_NOTIMPLEMENTED; 365 goto cleanup; 366 } 367 break; 368 case FINDZONE: 369 /* this is required. It's the whole point of DLZ! */ 370 if (dbi->findzone_q == NULL) { 371 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 372 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 373 "No query specified for findzone. " 374 "Findzone requires a query"); 375 result = ISC_R_FAILURE; 376 goto cleanup; 377 } 378 break; 379 case LOOKUP: 380 /* this is required. It's also a major point of DLZ! */ 381 if (dbi->lookup_q == NULL) { 382 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 383 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 384 "No query specified for lookup. " 385 "Lookup requires a query"); 386 result = ISC_R_FAILURE; 387 goto cleanup; 388 } 389 break; 390 default: 391 /* 392 * this should never happen. If it does, the code is 393 * screwed up! 394 */ 395 UNEXPECTED_ERROR(__FILE__, __LINE__, 396 "Incorrect query flag passed to " 397 "postgres_get_resultset"); 398 result = ISC_R_UNEXPECTED; 399 goto cleanup; 400 } 401 402 #if 0 403 /* temporary logging message */ 404 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 405 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 406 "%d checked query", dlz_thread_num); 407 #endif 408 409 /* 410 * was a zone string passed? If so, make it safe for use in 411 * queries. 412 */ 413 if (zone != NULL) { 414 dbi->zone = postgres_escape_string(zone); 415 if (dbi->zone == NULL) { 416 result = ISC_R_NOMEMORY; 417 goto cleanup; 418 } 419 } else { /* no string passed, set the string pointer to NULL */ 420 dbi->zone = NULL; 421 } 422 423 #if 0 424 /* temporary logging message */ 425 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 426 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 427 "%d did zone", dlz_thread_num); 428 #endif 429 430 /* 431 * was a record string passed? If so, make it safe for use in 432 * queries. 433 */ 434 if (record != NULL) { 435 dbi->record = postgres_escape_string(record); 436 if (dbi->record == NULL) { 437 result = ISC_R_NOMEMORY; 438 goto cleanup; 439 } 440 } else { /* no string passed, set the string pointer to NULL */ 441 dbi->record = NULL; 442 } 443 444 445 #if 0 446 /* temporary logging message */ 447 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 448 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 449 "%d did record", dlz_thread_num); 450 #endif 451 452 /* 453 * was a client string passed? If so, make it safe for use in 454 * queries. 455 */ 456 if (client != NULL) { 457 dbi->client = postgres_escape_string(client); 458 if (dbi->client == NULL) { 459 result = ISC_R_NOMEMORY; 460 goto cleanup; 461 } 462 } else { /* no string passed, set the string pointer to NULL */ 463 dbi->client = NULL; 464 } 465 466 #if 0 467 /* temporary logging message */ 468 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 469 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 470 "%d did client", dlz_thread_num); 471 #endif 472 473 /* 474 * what type of query are we going to run? 475 * this time we build the actual query to run. 476 */ 477 switch(query) { 478 case ALLNODES: 479 querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); 480 break; 481 case ALLOWXFR: 482 querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); 483 break; 484 case AUTHORITY: 485 querystring = build_querystring(ns_g_mctx, dbi->authority_q); 486 break; 487 case FINDZONE: 488 querystring = build_querystring(ns_g_mctx, dbi->findzone_q); 489 break; 490 case LOOKUP: 491 querystring = build_querystring(ns_g_mctx, dbi->lookup_q); 492 break; 493 default: 494 /* 495 * this should never happen. If it does, the code is 496 * screwed up! 497 */ 498 UNEXPECTED_ERROR(__FILE__, __LINE__, 499 "Incorrect query flag passed to " 500 "postgres_get_resultset"); 501 result = ISC_R_UNEXPECTED; 502 goto cleanup; 503 } 504 505 #if 0 506 /* temporary logging message */ 507 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 508 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 509 "%d built query", dlz_thread_num); 510 #endif 511 512 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ 513 if (querystring == NULL) { 514 result = ISC_R_NOMEMORY; 515 goto cleanup; 516 } 517 518 #if 0 519 /* temporary logging message */ 520 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 521 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 522 "%d query is '%s'", dlz_thread_num, querystring); 523 #endif 524 525 /* 526 * output the full query string during debug so we can see 527 * what lame error the query has. 528 */ 529 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 530 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 531 "\nQuery String: %s\n", querystring); 532 533 /* attempt query up to 3 times. */ 534 for (j=0; j < 3; j++) { 535 #if 0 536 /* temporary logging message */ 537 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 538 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 539 "%d executing query for %d time", 540 dlz_thread_num, j); 541 #endif 542 /* try to get result set */ 543 *rs = PQexec((PGconn *)dbi->dbconn, querystring ); 544 result = ISC_R_SUCCESS; 545 /* 546 * if result set is null, reset DB connection, max 3 547 * attempts. 548 */ 549 for (i=0; *rs == NULL && i < 3; i++) { 550 #if 0 551 /* temporary logging message */ 552 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 553 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 554 "%d resetting connection", 555 dlz_thread_num); 556 #endif 557 result = ISC_R_FAILURE; 558 PQreset((PGconn *) dbi->dbconn); 559 /* connection ok, break inner loop */ 560 if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK) 561 break; 562 } 563 /* result set ok, break outter loop */ 564 if (PQresultStatus(*rs) == PGRES_TUPLES_OK) { 565 #if 0 566 /* temporary logging message */ 567 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 568 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 569 "%d rs ok", dlz_thread_num); 570 #endif 571 break; 572 } else { 573 /* we got a result set object, but it's not right. */ 574 #if 0 575 /* temporary logging message */ 576 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 577 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 578 "%d clearing rs", dlz_thread_num); 579 #endif 580 PQclear(*rs); /* get rid of it */ 581 /* in case this was the last attempt */ 582 *rs = NULL; 583 result = ISC_R_FAILURE; 584 } 585 } 586 587 cleanup: 588 /* it's always good to cleanup after yourself */ 589 590 #if 0 591 /* temporary logging message */ 592 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 593 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 594 "%d cleaning up", dlz_thread_num); 595 #endif 596 597 /* if we couldn't even allocate DBI, just return NULL */ 598 if (dbi == NULL) 599 return ISC_R_FAILURE; 600 601 /* free dbi->zone string */ 602 if (dbi->zone != NULL) 603 isc_mem_free(ns_g_mctx, dbi->zone); 604 605 /* free dbi->record string */ 606 if (dbi->record != NULL) 607 isc_mem_free(ns_g_mctx, dbi->record); 608 609 /* free dbi->client string */ 610 if (dbi->client != NULL) 611 isc_mem_free(ns_g_mctx, dbi->client); 612 613 #ifdef ISC_PLATFORM_USETHREADS 614 615 #if 0 616 /* temporary logging message */ 617 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 618 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 619 "%d unlocking mutex", dlz_thread_num); 620 #endif 621 622 /* release the lock so another thread can use this dbi */ 623 isc_mutex_unlock(&dbi->instance_lock); 624 625 #endif /* ISC_PLATFORM_USETHREADS */ 626 627 /* release query string */ 628 if (querystring != NULL) 629 isc_mem_free(ns_g_mctx, querystring ); 630 631 #if 0 632 /* temporary logging message */ 633 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 634 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 635 "%d returning", dlz_thread_num); 636 #endif 637 638 /* return result */ 639 return result; 640 } 641 642 /*% 643 * The processing of result sets for lookup and authority are 644 * exactly the same. So that functionality has been moved 645 * into this function to minimize code. 646 */ 647 648 static isc_result_t 649 postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs) 650 { 651 isc_result_t result; 652 unsigned int i; 653 unsigned int rows; 654 unsigned int fields; 655 unsigned int j; 656 unsigned int len; 657 char *tmpString; 658 char *endp; 659 int ttl; 660 661 rows = PQntuples(rs); /* how many rows in result set */ 662 fields = PQnfields(rs); /* how many columns in result set */ 663 for (i=0; i < rows; i++) { 664 switch(fields) { 665 case 1: 666 /* 667 * one column in rs, it's the data field. use 668 * default type of A record, and default TTL 669 * of 86400 670 */ 671 result = dns_sdlz_putrr(lookup, "a", 86400, 672 PQgetvalue(rs, i, 0)); 673 break; 674 case 2: 675 /* two columns, data field, and data type. 676 * use default TTL of 86400. 677 */ 678 result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0), 679 86400, PQgetvalue(rs, i, 1)); 680 break; 681 case 3: 682 /* three columns, all data no defaults. 683 * convert text to int, make sure it worked 684 * right. 685 */ 686 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); 687 if (*endp != '\0' || ttl < 0) { 688 isc_log_write(dns_lctx, 689 DNS_LOGCATEGORY_DATABASE, 690 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 691 "Postgres driver ttl must be " 692 "a positive number"); 693 } 694 result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1), 695 ttl, PQgetvalue(rs, i, 2)); 696 break; 697 default: 698 /* 699 * more than 3 fields, concatenate the last 700 * ones together. figure out how long to make 701 * string 702 */ 703 for (j=2, len=0; j < fields; j++) { 704 len += strlen(PQgetvalue(rs, i, j)) + 1; 705 } 706 /* 707 * allocate string memory, allow for NULL to 708 * term string 709 */ 710 tmpString = isc_mem_allocate(ns_g_mctx, len + 1); 711 if (tmpString == NULL) { 712 /* major bummer, need more ram */ 713 isc_log_write(dns_lctx, 714 DNS_LOGCATEGORY_DATABASE, 715 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 716 "Postgres driver unable to " 717 "allocate memory for " 718 "temporary string"); 719 PQclear(rs); 720 return (ISC_R_FAILURE); /* Yeah, I'd say! */ 721 } 722 /* copy field to tmpString */ 723 strcpy(tmpString, PQgetvalue(rs, i, 2)); 724 /* 725 * concat the rest of fields together, space 726 * between each one. 727 */ 728 for (j=3; j < fields; j++) { 729 strcat(tmpString, " "); 730 strcat(tmpString, PQgetvalue(rs, i, j)); 731 } 732 /* convert text to int, make sure it worked right */ 733 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); 734 if (*endp != '\0' || ttl < 0) { 735 isc_log_write(dns_lctx, 736 DNS_LOGCATEGORY_DATABASE, 737 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 738 "Postgres driver ttl must be " 739 "a postive number"); 740 } 741 /* ok, now tell Bind about it. */ 742 result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1), 743 ttl, tmpString); 744 /* done, get rid of this thing. */ 745 isc_mem_free(ns_g_mctx, tmpString); 746 } 747 /* I sure hope we were successful */ 748 if (result != ISC_R_SUCCESS) { 749 /* nope, get rid of the Result set, and log a msg */ 750 PQclear(rs); 751 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 752 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 753 "dns_sdlz_putrr returned error. " 754 "Error code was: %s", 755 isc_result_totext(result)); 756 return (ISC_R_FAILURE); 757 } 758 } 759 760 /* free result set memory */ 761 PQclear(rs); 762 763 /* if we did return results, we are successful */ 764 if (rows > 0) 765 return (ISC_R_SUCCESS); 766 767 /* empty result set, no data found */ 768 return (ISC_R_NOTFOUND); 769 } 770 771 /* 772 * SDLZ interface methods 773 */ 774 775 /*% determine if the zone is supported by (in) the database */ 776 777 static isc_result_t 778 postgres_findzone(void *driverarg, void *dbdata, const char *name, 779 dns_clientinfomethods_t *methods, 780 dns_clientinfo_t *clientinfo) 781 { 782 isc_result_t result; 783 PGresult *rs = NULL; 784 unsigned int rows; 785 786 UNUSED(driverarg); 787 UNUSED(methods); 788 UNUSED(clientinfo); 789 790 /* run the query and get the result set from the database. */ 791 result = postgres_get_resultset(name, NULL, NULL, 792 FINDZONE, dbdata, &rs); 793 /* if we didn't get a result set, log an err msg. */ 794 if (result != ISC_R_SUCCESS) { 795 if (rs != NULL) 796 PQclear(rs); 797 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 798 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 799 "Postgres driver unable to return " 800 "result set for findzone query"); 801 return (ISC_R_FAILURE); 802 } 803 /* count how many rows in result set */ 804 rows = PQntuples(rs); 805 /* get rid of result set, we are done with it. */ 806 PQclear(rs); 807 808 /* if we returned any rows, zone is supported. */ 809 if (rows > 0) 810 return (ISC_R_SUCCESS); 811 812 /* no rows returned, zone is not supported. */ 813 return (ISC_R_NOTFOUND); 814 } 815 816 /*% Determine if the client is allowed to perform a zone transfer */ 817 static isc_result_t 818 postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name, 819 const char *client) 820 { 821 isc_result_t result; 822 PGresult *rs = NULL; 823 unsigned int rows; 824 UNUSED(driverarg); 825 826 /* first check if the zone is supported by the database. */ 827 result = postgres_findzone(driverarg, dbdata, name, NULL, NULL); 828 if (result != ISC_R_SUCCESS) 829 return (ISC_R_NOTFOUND); 830 831 /* 832 * if we get to this point we know the zone is supported by 833 * the database the only questions now are is the zone 834 * transfer is allowed for this client and did the config file 835 * have an allow zone xfr query. 836 * 837 * Run our query, and get a result set from the database. 838 */ 839 result = postgres_get_resultset(name, NULL, client, 840 ALLOWXFR, dbdata, &rs); 841 /* if we get "not implemented", send it along. */ 842 if (result == ISC_R_NOTIMPLEMENTED) 843 return result; 844 /* if we didn't get a result set, log an err msg. */ 845 if (result != ISC_R_SUCCESS) { 846 if (rs != NULL) 847 PQclear(rs); 848 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 849 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 850 "Postgres driver unable to return " 851 "result set for allow xfr query"); 852 return (ISC_R_FAILURE); 853 } 854 /* count how many rows in result set */ 855 rows = PQntuples(rs); 856 /* get rid of result set, we are done with it. */ 857 PQclear(rs); 858 859 /* if we returned any rows, zone xfr is allowed. */ 860 if (rows > 0) 861 return (ISC_R_SUCCESS); 862 863 /* no rows returned, zone xfr not allowed */ 864 return (ISC_R_NOPERM); 865 } 866 867 /*% 868 * If the client is allowed to perform a zone transfer, the next order of 869 * business is to get all the nodes in the zone, so bind can respond to the 870 * query. 871 */ 872 static isc_result_t 873 postgres_allnodes(const char *zone, void *driverarg, void *dbdata, 874 dns_sdlzallnodes_t *allnodes) 875 { 876 isc_result_t result; 877 PGresult *rs = NULL; 878 unsigned int i; 879 unsigned int rows; 880 unsigned int fields; 881 unsigned int j; 882 unsigned int len; 883 char *tmpString; 884 char *endp; 885 int ttl; 886 887 UNUSED(driverarg); 888 889 /* run the query and get the result set from the database. */ 890 result = postgres_get_resultset(zone, NULL, NULL, 891 ALLNODES, dbdata, &rs); 892 /* if we get "not implemented", send it along */ 893 if (result == ISC_R_NOTIMPLEMENTED) 894 return result; 895 /* if we didn't get a result set, log an err msg. */ 896 if (result != ISC_R_SUCCESS) { 897 if (rs != NULL) 898 PQclear(rs); 899 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 900 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 901 "Postgres driver unable to return " 902 "result set for all nodes query"); 903 return (ISC_R_FAILURE); 904 } 905 906 rows = PQntuples(rs); /* how many rows in result set */ 907 fields = PQnfields(rs); /* how many columns in result set */ 908 for (i=0; i < rows; i++) { 909 if (fields < 4) { /* gotta have at least 4 columns */ 910 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 911 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 912 "Postgres driver too few fields " 913 "returned by all nodes query"); 914 } 915 /* convert text to int, make sure it worked right */ 916 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10); 917 if (*endp != '\0' || ttl < 0) { 918 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 919 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 920 "Postgres driver ttl must be " 921 "a postive number"); 922 } 923 if (fields == 4) { 924 /* tell Bind about it. */ 925 result = dns_sdlz_putnamedrr(allnodes, 926 PQgetvalue(rs, i, 2), 927 PQgetvalue(rs, i, 1), 928 ttl, 929 PQgetvalue(rs, i, 3)); 930 } else { 931 /* 932 * more than 4 fields, concatonat the last 933 * ones together. figure out how long to make 934 * string 935 */ 936 for (j=3, len=0; j < fields; j++) { 937 len += strlen(PQgetvalue(rs, i, j)) + 1; 938 } 939 /* allocate memory, allow for NULL to term string */ 940 tmpString = isc_mem_allocate(ns_g_mctx, len + 1); 941 if (tmpString == NULL) { /* we need more ram. */ 942 isc_log_write(dns_lctx, 943 DNS_LOGCATEGORY_DATABASE, 944 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 945 "Postgres driver unable to " 946 "allocate memory for " 947 "temporary string"); 948 PQclear(rs); 949 return (ISC_R_FAILURE); 950 } 951 /* copy this field to tmpString */ 952 strcpy(tmpString, PQgetvalue(rs, i, 3)); 953 /* concatonate the rest, with spaces between */ 954 for (j=4; j < fields; j++) { 955 strcat(tmpString, " "); 956 strcat(tmpString, PQgetvalue(rs, i, j)); 957 } 958 /* tell Bind about it. */ 959 result = dns_sdlz_putnamedrr(allnodes, 960 PQgetvalue(rs, i, 2), 961 PQgetvalue(rs, i, 1), 962 ttl, tmpString); 963 isc_mem_free(ns_g_mctx, tmpString); 964 } 965 /* if we weren't successful, log err msg */ 966 if (result != ISC_R_SUCCESS) { 967 PQclear(rs); 968 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 969 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 970 "dns_sdlz_putnamedrr returned error. " 971 "Error code was: %s", 972 isc_result_totext(result)); 973 return (ISC_R_FAILURE); 974 } 975 } 976 977 /* free result set memory */ 978 PQclear(rs); 979 980 /* if we did return results, we are successful */ 981 if (rows > 0) 982 return (ISC_R_SUCCESS); 983 984 /* empty result set, no data found */ 985 return (ISC_R_NOTFOUND); 986 } 987 988 /*% 989 * if the lookup function does not return SOA or NS records for the zone, 990 * use this function to get that information for Bind. 991 */ 992 993 static isc_result_t 994 postgres_authority(const char *zone, void *driverarg, void *dbdata, 995 dns_sdlzlookup_t *lookup) 996 { 997 isc_result_t result; 998 PGresult *rs = NULL; 999 1000 UNUSED(driverarg); 1001 1002 /* run the query and get the result set from the database. */ 1003 result = postgres_get_resultset(zone, NULL, NULL, 1004 AUTHORITY, dbdata, &rs); 1005 /* if we get "not implemented", send it along */ 1006 if (result == ISC_R_NOTIMPLEMENTED) 1007 return result; 1008 /* if we didn't get a result set, log an err msg. */ 1009 if (result != ISC_R_SUCCESS) { 1010 if (rs != NULL) 1011 PQclear(rs); 1012 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1013 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1014 "Postgres driver unable to return " 1015 "result set for authority query"); 1016 return (ISC_R_FAILURE); 1017 } 1018 /* 1019 * lookup and authority result sets are processed in the same 1020 * manner postgres_process_rs does the job for both 1021 * functions. 1022 */ 1023 return postgres_process_rs(lookup, rs); 1024 } 1025 1026 /*% if zone is supported, lookup up a (or multiple) record(s) in it */ 1027 static isc_result_t 1028 postgres_lookup(const char *zone, const char *name, void *driverarg, 1029 void *dbdata, dns_sdlzlookup_t *lookup, 1030 dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo) 1031 { 1032 isc_result_t result; 1033 PGresult *rs = NULL; 1034 1035 UNUSED(driverarg); 1036 UNUSED(methods); 1037 UNUSED(clientinfo); 1038 1039 /* run the query and get the result set from the database. */ 1040 result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs); 1041 /* if we didn't get a result set, log an err msg. */ 1042 if (result != ISC_R_SUCCESS) { 1043 if (rs != NULL) 1044 PQclear(rs); 1045 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1046 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1047 "Postgres driver unable to " 1048 "return result set for lookup query"); 1049 return (ISC_R_FAILURE); 1050 } 1051 /* 1052 * lookup and authority result sets are processed in the same 1053 * manner postgres_process_rs does the job for both functions. 1054 */ 1055 return postgres_process_rs(lookup, rs); 1056 } 1057 1058 /*% 1059 * create an instance of the driver. Remember, only 1 copy of the driver's 1060 * code is ever loaded, the driver has to remember which context it's 1061 * operating in. This is done via use of the dbdata argument which is 1062 * passed into all query functions. 1063 */ 1064 static isc_result_t 1065 postgres_create(const char *dlzname, unsigned int argc, char *argv[], 1066 void *driverarg, void **dbdata) 1067 { 1068 isc_result_t result; 1069 dbinstance_t *dbi = NULL; 1070 unsigned int j; 1071 1072 #ifdef ISC_PLATFORM_USETHREADS 1073 /* if multi-threaded, we need a few extra variables. */ 1074 int dbcount; 1075 db_list_t *dblist = NULL; 1076 int i; 1077 char *endp; 1078 1079 #endif /* ISC_PLATFORM_USETHREADS */ 1080 1081 UNUSED(driverarg); 1082 UNUSED(dlzname); 1083 1084 /* seed random # generator */ 1085 srand( (unsigned)time( NULL ) ); 1086 1087 1088 #ifdef ISC_PLATFORM_USETHREADS 1089 /* if debugging, let user know we are multithreaded. */ 1090 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1091 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 1092 "Postgres driver running multithreaded"); 1093 #else /* ISC_PLATFORM_USETHREADS */ 1094 /* if debugging, let user know we are single threaded. */ 1095 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1096 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 1097 "Postgres driver running single threaded"); 1098 #endif /* ISC_PLATFORM_USETHREADS */ 1099 1100 /* verify we have at least 5 arg's passed to the driver */ 1101 if (argc < 5) { 1102 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1103 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1104 "Postgres driver requires at least " 1105 "4 command line args."); 1106 return (ISC_R_FAILURE); 1107 } 1108 1109 /* no more than 8 arg's should be passed to the driver */ 1110 if (argc > 8) { 1111 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1112 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1113 "Postgres driver cannot accept more than " 1114 "7 command line args."); 1115 return (ISC_R_FAILURE); 1116 } 1117 1118 /* multithreaded build can have multiple DB connections */ 1119 #ifdef ISC_PLATFORM_USETHREADS 1120 1121 /* check how many db connections we should create */ 1122 dbcount = strtol(argv[1], &endp, 10); 1123 if (*endp != '\0' || dbcount < 0) { 1124 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1125 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1126 "Postgres driver database connection count " 1127 "must be positive."); 1128 return (ISC_R_FAILURE); 1129 } 1130 1131 /* allocate memory for database connection list */ 1132 dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); 1133 if (dblist == NULL) 1134 return (ISC_R_NOMEMORY); 1135 1136 /* initialize DB connection list */ 1137 ISC_LIST_INIT(*dblist); 1138 1139 /* 1140 * create the appropriate number of database instances (DBI) 1141 * append each new DBI to the end of the list 1142 */ 1143 for (i=0; i < dbcount; i++) { 1144 1145 #endif /* ISC_PLATFORM_USETHREADS */ 1146 1147 /* how many queries were passed in from config file? */ 1148 switch(argc) { 1149 case 5: 1150 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, 1151 NULL, argv[3], argv[4], 1152 NULL, &dbi); 1153 break; 1154 case 6: 1155 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, 1156 argv[5], argv[3], argv[4], 1157 NULL, &dbi); 1158 break; 1159 case 7: 1160 result = build_sqldbinstance(ns_g_mctx, argv[6], NULL, 1161 argv[5], argv[3], argv[4], 1162 NULL, &dbi); 1163 break; 1164 case 8: 1165 result = build_sqldbinstance(ns_g_mctx, argv[6], 1166 argv[7], argv[5], argv[3], 1167 argv[4], NULL, &dbi); 1168 break; 1169 default: 1170 /* not really needed, should shut up compiler. */ 1171 result = ISC_R_FAILURE; 1172 } 1173 1174 1175 if (result == ISC_R_SUCCESS) { 1176 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1177 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1178 "Postgres driver created database " 1179 "instance object."); 1180 } else { /* unsuccessful?, log err msg and cleanup. */ 1181 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1182 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1183 "Postgres driver could not create " 1184 "database instance object."); 1185 goto cleanup; 1186 } 1187 1188 #ifdef ISC_PLATFORM_USETHREADS 1189 1190 /* when multithreaded, build a list of DBI's */ 1191 ISC_LINK_INIT(dbi, link); 1192 ISC_LIST_APPEND(*dblist, dbi, link); 1193 1194 #endif 1195 1196 /* create and set db connection */ 1197 dbi->dbconn = PQconnectdb(argv[2]); 1198 /* 1199 * if db connection cannot be created, log err msg and 1200 * cleanup. 1201 */ 1202 if (dbi->dbconn == NULL) { 1203 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1204 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1205 "Postgres driver could not allocate " 1206 "memory for database connection"); 1207 goto cleanup; 1208 } 1209 1210 /* if we cannot connect the first time, try 3 more times. */ 1211 for (j = 0; 1212 PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK && 1213 j < 3; 1214 j++) 1215 PQreset((PGconn *) dbi->dbconn); 1216 1217 1218 #ifdef ISC_PLATFORM_USETHREADS 1219 1220 /* 1221 * if multi threaded, let user know which connection 1222 * failed. user could be attempting to create 10 db 1223 * connections and for some reason the db backend only 1224 * allows 9 1225 */ 1226 if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) { 1227 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1228 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1229 "Postgres driver failed to create " 1230 "database connection number %u " 1231 "after 4 attempts", 1232 i + 1); 1233 goto cleanup; 1234 } 1235 1236 /* set DBI = null for next loop through. */ 1237 dbi = NULL; 1238 } /* end for loop */ 1239 1240 /* set dbdata to the list we created. */ 1241 *dbdata = dblist; 1242 1243 #else /* ISC_PLATFORM_USETHREADS */ 1244 /* if single threaded, just let user know we couldn't connect. */ 1245 if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) { 1246 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1247 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1248 "Postgres driver failed to create database " 1249 "connection after 4 attempts"); 1250 goto cleanup; 1251 } 1252 1253 /* 1254 * single threaded build can only use 1 db connection, return 1255 * it via dbdata 1256 */ 1257 *dbdata = dbi; 1258 1259 #endif /* ISC_PLATFORM_USETHREADS */ 1260 1261 /* hey, we got through all of that ok, return success. */ 1262 return(ISC_R_SUCCESS); 1263 1264 cleanup: 1265 1266 #ifdef ISC_PLATFORM_USETHREADS 1267 /* 1268 * if multithreaded, we could fail because only 1 connection 1269 * couldn't be made. We should cleanup the other successful 1270 * connections properly. 1271 */ 1272 postgres_destroy_dblist(dblist); 1273 1274 #else /* ISC_PLATFORM_USETHREADS */ 1275 if (dbi != NULL) 1276 destroy_sqldbinstance(dbi); 1277 1278 #endif /* ISC_PLATFORM_USETHREADS */ 1279 return(ISC_R_FAILURE); 1280 } 1281 1282 /*% 1283 * destroy an instance of the driver. Remember, only 1 copy of the driver's 1284 * code is ever loaded, the driver has to remember which context it's 1285 * operating in. This is done via use of the dbdata argument. 1286 * so we really only need to clean it up since we are not using driverarg. 1287 */ 1288 static void 1289 postgres_destroy(void *driverarg, void *dbdata) 1290 { 1291 1292 #ifdef ISC_PLATFORM_USETHREADS 1293 1294 UNUSED(driverarg); 1295 /* cleanup the list of DBI's */ 1296 postgres_destroy_dblist((db_list_t *) dbdata); 1297 1298 #else /* ISC_PLATFORM_USETHREADS */ 1299 1300 dbinstance_t *dbi; 1301 1302 UNUSED(driverarg); 1303 1304 dbi = (dbinstance_t *) dbdata; 1305 1306 /* release DB connection */ 1307 if (dbi->dbconn != NULL) 1308 PQfinish((PGconn *) dbi->dbconn); 1309 1310 /* destroy single DB instance */ 1311 destroy_sqldbinstance(dbi); 1312 1313 #endif /* ISC_PLATFORM_USETHREADS */ 1314 } 1315 1316 /* pointers to all our runtime methods. */ 1317 /* this is used during driver registration */ 1318 /* i.e. in dlz_postgres_init below. */ 1319 static dns_sdlzmethods_t dlz_postgres_methods = { 1320 postgres_create, 1321 postgres_destroy, 1322 postgres_findzone, 1323 postgres_lookup, 1324 postgres_authority, 1325 postgres_allnodes, 1326 postgres_allowzonexfr, 1327 NULL, 1328 NULL, 1329 NULL, 1330 NULL, 1331 NULL, 1332 NULL, 1333 NULL, 1334 }; 1335 1336 /*% 1337 * Wrapper around dns_sdlzregister(). 1338 */ 1339 isc_result_t 1340 dlz_postgres_init(void) { 1341 isc_result_t result; 1342 1343 /* 1344 * Write debugging message to log 1345 */ 1346 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1347 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1348 "Registering DLZ postgres driver."); 1349 1350 /* 1351 * Driver is always threadsafe. When multithreaded all 1352 * functions use multithreaded code. When not multithreaded, 1353 * all functions can only be entered once, but only 1 thread 1354 * of operation is available in Bind. So everything is still 1355 * threadsafe. 1356 */ 1357 result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL, 1358 DNS_SDLZFLAG_RELATIVEOWNER | 1359 DNS_SDLZFLAG_RELATIVERDATA | 1360 DNS_SDLZFLAG_THREADSAFE, 1361 ns_g_mctx, &dlz_postgres); 1362 /* if we can't register the driver, there are big problems. */ 1363 if (result != ISC_R_SUCCESS) { 1364 UNEXPECTED_ERROR(__FILE__, __LINE__, 1365 "dns_sdlzregister() failed: %s", 1366 isc_result_totext(result)); 1367 result = ISC_R_UNEXPECTED; 1368 } 1369 1370 1371 return result; 1372 } 1373 1374 /*% 1375 * Wrapper around dns_sdlzunregister(). 1376 */ 1377 void 1378 dlz_postgres_clear(void) { 1379 1380 /* 1381 * Write debugging message to log 1382 */ 1383 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1384 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1385 "Unregistering DLZ postgres driver."); 1386 1387 /* unregister the driver. */ 1388 if (dlz_postgres != NULL) 1389 dns_sdlzunregister(&dlz_postgres); 1390 } 1391 1392 #endif 1393