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