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_ODBC 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_odbc_driver.h> 75 76#include <sql.h> 77#include <sqlext.h> 78#include <sqltypes.h> 79 80static dns_sdlzimplementation_t *dlz_odbc = NULL; 81 82#define dbc_search_limit 30 83#define ALLNODES 1 84#define ALLOWXFR 2 85#define AUTHORITY 3 86#define FINDZONE 4 87#define LOOKUP 5 88 89#define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0) 90 91/* 92 * Private Structures 93 */ 94 95/* 96 * structure to hold ODBC connection & statement 97 */ 98 99typedef struct{ 100 SQLHDBC dbc; 101 SQLHSTMT stmnt; 102} odbc_db_t; 103 104/* 105 * Structure to hold everthing needed by this "instance" of the odbc driver 106 * remember, the driver code is only loaded once, but may have many separate 107 * instances 108 */ 109 110typedef struct { 111 112#ifdef ISC_PLATFORM_USETHREADS 113 114 db_list_t *db; /* handle to a list of DB */ 115 116#else 117 118 dbinstance_t *db; /* handle to db */ 119 120#endif 121 122 SQLHENV sql_env; /* handle to SQL environment */ 123 SQLCHAR *dsn; 124 SQLCHAR *user; 125 SQLCHAR *pass; 126} odbc_instance_t; 127 128/* forward reference */ 129 130static size_t 131odbc_makesafe(char *to, const char *from, size_t length); 132 133/* 134 * Private methods 135 */ 136 137static SQLSMALLINT 138safeLen(void *a) { 139 if (a == NULL) 140 return 0; 141 return strlen((char *) a); 142} 143 144/*% propertly cleans up an odbc_instance_t */ 145 146static void 147destroy_odbc_instance(odbc_instance_t *odbc_inst) { 148 149#ifdef ISC_PLATFORM_USETHREADS 150 151 dbinstance_t *ndbi = NULL; 152 dbinstance_t *dbi = NULL; 153 154 /* get the first DBI in the list */ 155 ndbi = ISC_LIST_HEAD(*odbc_inst->db); 156 157 /* loop through the list */ 158 while (ndbi != NULL) { 159 dbi = ndbi; 160 /* get the next DBI in the list */ 161 ndbi = ISC_LIST_NEXT(dbi, link); 162 163 /* if we have a connection / statement object in memory */ 164 if (dbi->dbconn != NULL) { 165 /* free statement handle */ 166 if (((odbc_db_t *) (dbi->dbconn))->stmnt != NULL) { 167 SQLFreeHandle(SQL_HANDLE_STMT, 168 ((odbc_db_t *) 169 (dbi->dbconn))->stmnt); 170 ((odbc_db_t *) (dbi->dbconn))->stmnt = NULL; 171 } 172 173 /* disconnect from database & free connection handle */ 174 if (((odbc_db_t *) (dbi->dbconn))->dbc != NULL) { 175 SQLDisconnect(((odbc_db_t *) 176 dbi->dbconn)->dbc); 177 SQLFreeHandle(SQL_HANDLE_DBC, 178 ((odbc_db_t *) 179 (dbi->dbconn))->dbc); 180 ((odbc_db_t *) (dbi->dbconn))->dbc = NULL; 181 } 182 183 /* free memory that held connection & statement. */ 184 isc_mem_free(ns_g_mctx, dbi->dbconn); 185 } 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, odbc_inst->db, sizeof(db_list_t)); 191 192#else /* ISC_PLATFORM_USETHREADS */ 193 194 /* free statement handle */ 195 if (((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt != NULL) { 196 SQLFreeHandle(SQL_HANDLE_STMT, 197 ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt); 198 ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt = NULL; 199 } 200 201 /* disconnect from database, free connection handle */ 202 if (((odbc_db_t *) (odbc_inst->db->dbconn))->dbc != NULL) { 203 SQLDisconnect(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc); 204 SQLFreeHandle(SQL_HANDLE_DBC, 205 ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc); 206 ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc = NULL; 207 } 208 /* free mem for the odbc_db_t structure held in db */ 209 if (((odbc_db_t *) odbc_inst->db->dbconn) != NULL) { 210 isc_mem_free(ns_g_mctx, odbc_inst->db->dbconn); 211 odbc_inst->db->dbconn = NULL; 212 } 213 214 if (odbc_inst->db != NULL) 215 destroy_sqldbinstance(odbc_inst->db); 216 217#endif /* ISC_PLATFORM_USETHREADS */ 218 219 220 /* free sql environment */ 221 if (odbc_inst->sql_env != NULL) 222 SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env); 223 224 /* free ODBC instance strings */ 225 if (odbc_inst->dsn != NULL) 226 isc_mem_free(ns_g_mctx, odbc_inst->dsn); 227 if (odbc_inst->pass != NULL) 228 isc_mem_free(ns_g_mctx, odbc_inst->pass); 229 if (odbc_inst->user != NULL) 230 isc_mem_free(ns_g_mctx, odbc_inst->user); 231 232 /* free memory for odbc_inst */ 233 if (odbc_inst != NULL) 234 isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t)); 235 236} 237 238/*% Connects to database, and creates ODBC statements */ 239 240static isc_result_t 241odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc) { 242 243 odbc_db_t *ndb = *dbc; 244 SQLRETURN sqlRes; 245 isc_result_t result = ISC_R_SUCCESS; 246 247 if (ndb != NULL) { 248 /* 249 * if db != null, we have to do some cleanup 250 * if statement handle != null free it 251 */ 252 if (ndb->stmnt != NULL) { 253 SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); 254 ndb->stmnt = NULL; 255 } 256 257 /* if connection handle != null free it */ 258 if (ndb->dbc != NULL) { 259 SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); 260 ndb->dbc = NULL; 261 } 262 } else { 263 ndb = isc_mem_allocate(ns_g_mctx, sizeof(odbc_db_t)); 264 if (ndb == NULL) { 265 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 266 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 267 "Odbc driver unable to allocate memory"); 268 return ISC_R_NOMEMORY; 269 } 270 memset(ndb, 0, sizeof(odbc_db_t)); 271 } 272 273 sqlRes = SQLAllocHandle(SQL_HANDLE_DBC, dbi->sql_env, &(ndb->dbc)); 274 if (!sqlOK(sqlRes)) { 275 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 276 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 277 "Odbc driver unable to allocate memory"); 278 result = ISC_R_NOMEMORY; 279 goto cleanup; 280 } 281 282 sqlRes = SQLConnect(ndb->dbc, dbi->dsn, safeLen(dbi->dsn), dbi->user, 283 safeLen(dbi->user), dbi->pass, safeLen(dbi->pass)); 284 if (!sqlOK(sqlRes)) { 285 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 286 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 287 "Odbc driver unable to connect"); 288 result = ISC_R_FAILURE; 289 goto cleanup; 290 } 291 292 sqlRes = SQLAllocHandle(SQL_HANDLE_STMT, ndb->dbc, &(ndb->stmnt)); 293 if (!sqlOK(sqlRes)) { 294 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 295 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 296 "Odbc driver unable to allocate memory"); 297 result = ISC_R_NOMEMORY; 298 goto cleanup; 299 } 300 301 *dbc = ndb; 302 303 return ISC_R_SUCCESS; 304 305 cleanup: 306 307 if (ndb != NULL) { 308 309 /* if statement handle != null free it */ 310 if (ndb->stmnt != NULL) { 311 SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); 312 ndb->stmnt = NULL; 313 } 314 315 /* if connection handle != null free it */ 316 if (ndb->dbc != NULL) { 317 SQLDisconnect(ndb->dbc); 318 SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); 319 ndb->dbc = NULL; 320 } 321 /* free memory holding ndb */ 322 isc_mem_free(ns_g_mctx, ndb); 323 } 324 325 return result; 326} 327 328/*% 329 * Loops through the list of DB instances, attempting to lock 330 * on the mutex. If successful, the DBI is reserved for use 331 * and the thread can perform queries against the database. 332 * If the lock fails, the next one in the list is tried. 333 * looping continues until a lock is obtained, or until 334 * the list has been searched dbc_search_limit times. 335 * This function is only used when the driver is compiled for 336 * multithreaded operation. 337 */ 338 339#ifdef ISC_PLATFORM_USETHREADS 340 341static dbinstance_t * 342odbc_find_avail_conn(db_list_t *dblist) 343{ 344 dbinstance_t *dbi = NULL; 345 dbinstance_t *head; 346 int count = 0; 347 348 /* get top of list */ 349 head = dbi = ISC_LIST_HEAD(*dblist); 350 351 /* loop through list */ 352 while (count < dbc_search_limit) { 353 /* try to lock on the mutex */ 354 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS) 355 return dbi; /* success, return the DBI for use. */ 356 357 /* not successful, keep trying */ 358 dbi = ISC_LIST_NEXT(dbi, link); 359 360 /* check to see if we have gone to the top of the list. */ 361 if (dbi == NULL) { 362 count++; 363 dbi = head; 364 } 365 } 366 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 367 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 368 "Odbc driver unable to find available " 369 "connection after searching %d times", 370 count); 371 return NULL; 372} 373 374#endif /* ISC_PLATFORM_USETHREADS */ 375 376/*% Allocates memory for a new string, and then constructs the new 377 * string by "escaping" the input string. The new string is 378 * safe to be used in queries. This is necessary because we cannot 379 * be sure of what types of strings are passed to us, and we don't 380 * want special characters in the string causing problems. 381 */ 382 383static char * 384odbc_escape_string(const char *instr) { 385 386 char *outstr; 387 unsigned int len; 388 389 if (instr == NULL) 390 return NULL; 391 392 len = strlen(instr); 393 394 outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1); 395 if (outstr == NULL) 396 return NULL; 397 398 odbc_makesafe(outstr, instr, len); 399 400 return outstr; 401} 402 403/* --------------- 404 * Escaping arbitrary strings to get valid SQL strings/identifiers. 405 * 406 * Replaces "\\" with "\\\\" and "'" with "''". 407 * length is the length of the buffer pointed to by 408 * from. The buffer at to must be at least 2*length + 1 characters 409 * long. A terminating NUL character is written. 410 * 411 * NOTICE!!! 412 * This function was borrowed directly from PostgreSQL's libpq. 413 * 414 * The copyright statements from the original file containing this 415 * function are included below: 416 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group 417 * Portions Copyright (c) 1994, Regents of the University of California 418 * --------------- 419 */ 420 421static size_t 422odbc_makesafe(char *to, const char *from, size_t length) 423{ 424 const char *source = from; 425 char *target = to; 426 unsigned int remaining = length; 427 428 while (remaining > 0) 429 { 430 switch (*source) 431 { 432 case '\\': 433 *target = '\\'; 434 target++; 435 *target = '\\'; 436 /* target and remaining are updated below. */ 437 break; 438 439 case '\'': 440 *target = '\''; 441 target++; 442 *target = '\''; 443 /* target and remaining are updated below. */ 444 break; 445 446 default: 447 *target = *source; 448 /* target and remaining are updated below. */ 449 } 450 source++; 451 target++; 452 remaining--; 453 } 454 455 /* Write the terminating NUL character. */ 456 *target = '\0'; 457 458 return target - to; 459} 460 461/*% 462 * This function is the real core of the driver. Zone, record 463 * and client strings are passed in (or NULL is passed if the 464 * string is not available). The type of query we want to run 465 * is indicated by the query flag, and the dbdata object is passed 466 * passed in to. dbdata really holds either: 467 * 1) a list of database instances (in multithreaded mode) OR 468 * 2) a single database instance (in single threaded mode) 469 * The function will construct the query and obtain an available 470 * database instance (DBI). It will then run the query and hopefully 471 * obtain a result set. The data base instance that is used is returned 472 * to the caller so they can get the data from the result set from it. 473 * If successfull, it will be the responsibility of the caller to close 474 * the cursor, and unlock the mutex of the DBI when they are done with it. 475 * If not successfull, this function will perform all the cleanup. 476 */ 477 478 479static isc_result_t 480odbc_get_resultset(const char *zone, const char *record, 481 const char *client, unsigned int query, 482 void *dbdata, dbinstance_t **r_dbi) 483{ 484 485 isc_result_t result; 486 dbinstance_t *dbi = NULL; 487 char *querystring = NULL; 488 unsigned int j = 0; 489 SQLRETURN sqlRes; 490 491 REQUIRE(*r_dbi == NULL); 492 493 /* get db instance / connection */ 494#ifdef ISC_PLATFORM_USETHREADS 495 496 /* find an available DBI from the list */ 497 dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db); 498 499#else /* ISC_PLATFORM_USETHREADS */ 500 501 /* 502 * only 1 DBI - no need to lock instance lock either 503 * only 1 thread in the whole process, no possible contention. 504 */ 505 dbi = (dbinstance_t *) ((odbc_instance_t *) dbdata)->db; 506 507#endif /* ISC_PLATFORM_USETHREADS */ 508 509 /* if DBI is null, can't do anything else */ 510 if (dbi == NULL) { 511 result = ISC_R_FAILURE; 512 goto cleanup; 513 } 514 515 /* what type of query are we going to run? */ 516 switch(query) { 517 case ALLNODES: 518 /* 519 * if the query was not passed in from the config file 520 * then we can't run it. return not_implemented, so 521 * it's like the code for that operation was never 522 * built into the driver.... AHHH flexibility!!! 523 */ 524 if (dbi->allnodes_q == NULL) { 525 result = ISC_R_NOTIMPLEMENTED; 526 goto cleanup; 527 } 528 break; 529 case ALLOWXFR: 530 /* same as comments as ALLNODES */ 531 if (dbi->allowxfr_q == NULL) { 532 result = ISC_R_NOTIMPLEMENTED; 533 goto cleanup; 534 } 535 break; 536 case AUTHORITY: 537 /* same as comments as ALLNODES */ 538 if (dbi->authority_q == NULL) { 539 result = ISC_R_NOTIMPLEMENTED; 540 goto cleanup; 541 } 542 break; 543 case FINDZONE: 544 /* this is required. It's the whole point of DLZ! */ 545 if (dbi->findzone_q == NULL) { 546 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 547 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 548 "No query specified for findzone. " 549 "Findzone requires a query"); 550 result = ISC_R_FAILURE; 551 goto cleanup; 552 } 553 break; 554 case LOOKUP: 555 /* this is required. It's also a major point of DLZ! */ 556 if (dbi->lookup_q == NULL) { 557 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 558 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 559 "No query specified for lookup. " 560 "Lookup requires a query"); 561 result = ISC_R_FAILURE; 562 goto cleanup; 563 } 564 break; 565 default: 566 /* 567 * this should never happen. If it does, the code is 568 * screwed up! 569 */ 570 UNEXPECTED_ERROR(__FILE__, __LINE__, 571 "Incorrect query flag passed to " 572 "odbc_get_resultset"); 573 result = ISC_R_UNEXPECTED; 574 goto cleanup; 575 } 576 577 578 /* 579 * was a zone string passed? If so, make it safe for use in 580 * queries. 581 */ 582 if (zone != NULL) { 583 dbi->zone = odbc_escape_string(zone); 584 if (dbi->zone == NULL) { 585 result = ISC_R_NOMEMORY; 586 goto cleanup; 587 } 588 } else { /* no string passed, set the string pointer to NULL */ 589 dbi->zone = NULL; 590 } 591 592 /* 593 * was a record string passed? If so, make it safe for use in 594 * queries. 595 */ 596 if (record != NULL) { 597 dbi->record = odbc_escape_string(record); 598 if (dbi->record == NULL) { 599 result = ISC_R_NOMEMORY; 600 goto cleanup; 601 } 602 } else { /* no string passed, set the string pointer to NULL */ 603 dbi->record = NULL; 604 } 605 606 /* 607 * was a client string passed? If so, make it safe for use in 608 * queries. 609 */ 610 if (client != NULL) { 611 dbi->client = odbc_escape_string(client); 612 if (dbi->client == NULL) { 613 result = ISC_R_NOMEMORY; 614 goto cleanup; 615 } 616 } else { /* no string passed, set the string pointer to NULL */ 617 dbi->client = NULL; 618 } 619 620 /* 621 * what type of query are we going to run? 622 * this time we build the actual query to run. 623 */ 624 switch(query) { 625 case ALLNODES: 626 querystring = build_querystring(ns_g_mctx, dbi->allnodes_q); 627 break; 628 case ALLOWXFR: 629 querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q); 630 break; 631 case AUTHORITY: 632 querystring = build_querystring(ns_g_mctx, dbi->authority_q); 633 break; 634 case FINDZONE: 635 querystring = build_querystring(ns_g_mctx, dbi->findzone_q); 636 break; 637 case LOOKUP: 638 querystring = build_querystring(ns_g_mctx, dbi->lookup_q); 639 break; 640 default: 641 /* 642 * this should never happen. If it does, the code is 643 * screwed up! 644 */ 645 UNEXPECTED_ERROR(__FILE__, __LINE__, 646 "Incorrect query flag passed to " 647 "odbc_get_resultset"); 648 result = ISC_R_UNEXPECTED; 649 goto cleanup; 650 } 651 652 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */ 653 if (querystring == NULL) { 654 result = ISC_R_NOMEMORY; 655 goto cleanup; 656 } 657 658 /* output the full query string during debug so we can see */ 659 /* what lame error the query has. */ 660 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 661 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 662 "\nQuery String: %s\n", querystring); 663 664 /* attempt query up to 3 times. */ 665 for (j=0; j < 3; j++) { 666 /* try to get result set */ 667 sqlRes = SQLExecDirect(((odbc_db_t *) dbi->dbconn)->stmnt, 668 (SQLCHAR *) querystring, 669 (SQLINTEGER) strlen(querystring)); 670 671 /* if error, reset DB connection */ 672 if (!sqlOK(sqlRes)) { 673 /* close cursor */ 674 SQLCloseCursor(((odbc_db_t *) dbi->dbconn)->stmnt); 675 /* attempt to reconnect */ 676 result = odbc_connect((odbc_instance_t *) dbdata, 677 (odbc_db_t **) &(dbi->dbconn)); 678 /* check if we reconnected */ 679 if (result != ISC_R_SUCCESS) 680 break; 681 /* incase this is the last time through the loop */ 682 result = ISC_R_FAILURE; 683 } else { 684 result = ISC_R_SUCCESS; 685 /* return dbi */ 686 *r_dbi = dbi; 687 /* result set ok, break loop */ 688 break; 689 } 690 } /* end for loop */ 691 692 cleanup: /* it's always good to cleanup after yourself */ 693 694 /* if we couldn't even allocate DBI, just return NULL */ 695 if (dbi == NULL) 696 return ISC_R_FAILURE; 697 698 /* free dbi->zone string */ 699 if (dbi->zone != NULL) 700 isc_mem_free(ns_g_mctx, dbi->zone); 701 702 /* free dbi->record string */ 703 if (dbi->record != NULL) 704 isc_mem_free(ns_g_mctx, dbi->record); 705 706 /* free dbi->client string */ 707 if (dbi->client != NULL) 708 isc_mem_free(ns_g_mctx, dbi->client); 709 710#ifdef ISC_PLATFORM_USETHREADS 711 712 /* if we are done using this dbi, release the lock */ 713 if (result != ISC_R_SUCCESS) 714 isc_mutex_unlock(&dbi->instance_lock); 715 716#endif /* ISC_PLATFORM_USETHREADS */ 717 718 /* release query string */ 719 if (querystring != NULL) 720 isc_mem_free(ns_g_mctx, querystring ); 721 722 /* return result */ 723 return result; 724 725} 726 727/*% 728 * Gets a single field from the ODBC statement. The memory for the 729 * returned data is dynamically allocated. If this method is successful 730 * it is the reponsibility of the caller to free the memory using 731 * isc_mem_free(ns_g_mctx, *ptr); 732 */ 733 734static isc_result_t 735odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data) { 736 737 SQLINTEGER size; 738 739 REQUIRE(data != NULL && *data == NULL); 740 741 if (sqlOK(SQLColAttribute(stmnt, field, SQL_DESC_DISPLAY_SIZE, 742 NULL, 0, NULL, &size)) && size > 0) { 743 *data = isc_mem_allocate(ns_g_mctx, size + 1); 744 if (data != NULL) { 745 if (sqlOK(SQLGetData(stmnt, field, SQL_C_CHAR, 746 *data, size + 1,&size))) 747 return ISC_R_SUCCESS; 748 isc_mem_free(ns_g_mctx, *data); 749 } 750 } 751 return ISC_R_FAILURE; 752} 753 754/*% 755 * Gets multiple fields from the ODBC statement. The memory for the 756 * returned data is dynamically allocated. If this method is successful 757 * it is the reponsibility of the caller to free the memory using 758 * isc_mem_free(ns_g_mctx, *ptr); 759 */ 760 761static isc_result_t 762odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField, 763 SQLSMALLINT endField, char **retData) { 764 765 isc_result_t result; 766 SQLINTEGER size; 767 int totSize = 0; 768 SQLSMALLINT i; 769 int j = 0; 770 char *data; 771 772 REQUIRE(retData != NULL && *retData == NULL); 773 REQUIRE(startField > 0 && startField <= endField); 774 775 /* determine how large the data is */ 776 for (i=startField; i <= endField; i++) 777 if (sqlOK(SQLColAttribute(stmnt, i, SQL_DESC_DISPLAY_SIZE, 778 NULL, 0, NULL, &size)) && size > 0) { 779 /* always allow for a " " (space) character */ 780 totSize += (size + 1); 781 /* after the data item */ 782 } 783 784 if (totSize < 1) 785 return ISC_R_FAILURE; 786 787 /* allow for a "\n" at the end of the string/ */ 788 data = isc_mem_allocate(ns_g_mctx, ++totSize); 789 if (data == NULL) 790 return ISC_R_NOMEMORY; 791 792 result = ISC_R_FAILURE; 793 794 /* get the data and concat all fields into a large string */ 795 for (i=startField; i <= endField; i++) { 796 if (sqlOK(SQLGetData(stmnt, i, SQL_C_CHAR, &(data[j]), 797 totSize - j, &size))) { 798 if (size > 0) { 799 j += size; 800 data[j++] = ' '; 801 data[j] = '\0'; 802 result = ISC_R_SUCCESS; 803 } 804 } else { 805 isc_mem_free(ns_g_mctx, data); 806 return ISC_R_FAILURE; 807 } 808 } 809 810 if (result != ISC_R_SUCCESS) { 811 isc_mem_free(ns_g_mctx, data); 812 return result; 813 } 814 815 *retData = data; 816 return ISC_R_SUCCESS; 817 818} 819 820/*% 821 * The processing of result sets for lookup and authority are 822 * exactly the same. So that functionality has been moved 823 * into this function to minimize code. 824 */ 825 826static isc_result_t 827odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *dbi) 828{ 829 830 831 isc_result_t result; 832 SQLSMALLINT fields; 833 SQLHSTMT *stmnt; 834 char *ttl_s; 835 char *type; 836 char *data; 837 char *endp; 838 int ttl; 839 840 REQUIRE(dbi != NULL); 841 842 stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt; 843 844 /* get number of columns */ 845 if (!sqlOK(SQLNumResultCols(stmnt, &fields))) { 846 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 847 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 848 "Odbc driver unable to process result set"); 849 result = ISC_R_FAILURE; 850 goto process_rs_cleanup; 851 } 852 853 /* get things ready for processing */ 854 result = ISC_R_FAILURE; 855 856 while (sqlOK(SQLFetch(stmnt))) { 857 858 /* set to null for next pass through */ 859 data = type = ttl_s = NULL; 860 861 switch(fields) { 862 case 1: 863 /* 864 * one column in rs, it's the data field. use 865 * default type of A record, and default TTL 866 * of 86400. attempt to get data, & tell bind 867 * about it. 868 */ 869 if ((result = odbc_getField(stmnt, 1, 870 &data)) == ISC_R_SUCCESS) { 871 result = dns_sdlz_putrr(lookup, "a", 872 86400, data); 873 } 874 break; 875 case 2: 876 /* 877 * two columns, data field, and data type. 878 * use default TTL of 86400. attempt to get 879 * DNS type & data, then tell bind about it. 880 */ 881 if ((result = odbc_getField(stmnt, 1, 882 &type)) == ISC_R_SUCCESS && 883 (result = odbc_getField(stmnt, 2, 884 &data)) == ISC_R_SUCCESS) { 885 result = dns_sdlz_putrr(lookup, type, 886 86400, data); 887 } 888 break; 889 default: 890 /* 891 * 3 fields or more, concatenate the last ones 892 * together. attempt to get DNS ttl, type, 893 * data then tell Bind about them. 894 */ 895 if ((result = odbc_getField(stmnt, 1, &ttl_s)) 896 == ISC_R_SUCCESS && 897 (result = odbc_getField(stmnt, 2, &type)) 898 == ISC_R_SUCCESS && 899 (result = odbc_getManyFields(stmnt, 3, 900 fields, &data)) 901 == ISC_R_SUCCESS) { 902 /* try to convert ttl string to int */ 903 ttl = strtol(ttl_s, &endp, 10); 904 /* failure converting ttl. */ 905 if (*endp != '\0' || ttl < 0) { 906 isc_log_write(dns_lctx, 907 DNS_LOGCATEGORY_DATABASE, 908 DNS_LOGMODULE_DLZ, 909 ISC_LOG_ERROR, 910 "Odbc driver ttl must " 911 "be a postive number"); 912 result = ISC_R_FAILURE; 913 } else { 914 /* 915 * successful converting TTL, 916 * tell Bind everything 917 */ 918 result = dns_sdlz_putrr(lookup, type, 919 ttl, data); 920 } 921 } /* closes bid if () */ 922 } /* closes switch(fields) */ 923 924 /* clean up mem */ 925 if (ttl_s != NULL) 926 isc_mem_free(ns_g_mctx, ttl_s); 927 if (type != NULL) 928 isc_mem_free(ns_g_mctx, type); 929 if (data != NULL) 930 isc_mem_free(ns_g_mctx, data); 931 932 /* I sure hope we were successful */ 933 if (result != ISC_R_SUCCESS) { 934 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 935 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 936 "dns_sdlz_putrr returned error. " 937 "Error code was: %s", 938 isc_result_totext(result)); 939 result = ISC_R_FAILURE; 940 goto process_rs_cleanup; 941 } 942 } /* closes while loop */ 943 944 process_rs_cleanup: 945 946 /* close cursor */ 947 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); 948 949#ifdef ISC_PLATFORM_USETHREADS 950 951 /* free lock on dbi so someone else can use it. */ 952 isc_mutex_unlock(&dbi->instance_lock); 953 954#endif 955 956 return result; 957} 958 959/* 960 * SDLZ interface methods 961 */ 962 963/*% determine if the zone is supported by (in) the database */ 964 965static isc_result_t 966odbc_findzone(void *driverarg, void *dbdata, const char *name) 967{ 968 969 isc_result_t result; 970 dbinstance_t *dbi = NULL; 971 972 UNUSED(driverarg); 973 974 /* run the query and get the result set from the database. */ 975 /* if result != ISC_R_SUCCESS cursor and mutex already cleaned up. */ 976 /* so we don't have to do it here. */ 977 result = odbc_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &dbi); 978 979 /* Check that we got a result set with data */ 980 if (result == ISC_R_SUCCESS && 981 !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) { 982 result = ISC_R_NOTFOUND; 983 } 984 985 if (dbi != NULL) { 986 /* get rid of result set, we are done with it. */ 987 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); 988 989#ifdef ISC_PLATFORM_USETHREADS 990 991 /* free lock on dbi so someone else can use it. */ 992 isc_mutex_unlock(&dbi->instance_lock); 993#endif 994 } 995 996 return result; 997} 998 999/*% Determine if the client is allowed to perform a zone transfer */ 1000static isc_result_t 1001odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name, 1002 const char *client) 1003{ 1004 isc_result_t result; 1005 dbinstance_t *dbi = NULL; 1006 1007 UNUSED(driverarg); 1008 1009 /* first check if the zone is supported by the database. */ 1010 result = odbc_findzone(driverarg, dbdata, name); 1011 if (result != ISC_R_SUCCESS) 1012 return (ISC_R_NOTFOUND); 1013 1014 /* 1015 * if we get to this point we know the zone is supported by 1016 * the database. the only questions now are is the zone 1017 * transfer is allowed for this client and did the config file 1018 * have an allow zone xfr query 1019 * 1020 * Run our query, and get a result set from the database. if 1021 * result != ISC_R_SUCCESS cursor and mutex already cleaned 1022 * up, so we don't have to do it here. 1023 */ 1024 result = odbc_get_resultset(name, NULL, client, ALLOWXFR, 1025 dbdata, &dbi); 1026 1027 /* if we get "not implemented", send it along. */ 1028 if (result == ISC_R_NOTIMPLEMENTED) 1029 return result; 1030 1031 /* Check that we got a result set with data */ 1032 if (result == ISC_R_SUCCESS && 1033 !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) { 1034 result = ISC_R_NOPERM; 1035 } 1036 1037 if (dbi != NULL) { 1038 /* get rid of result set, we are done with it. */ 1039 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); 1040 1041#ifdef ISC_PLATFORM_USETHREADS 1042 1043 /* free lock on dbi so someone else can use it. */ 1044 isc_mutex_unlock(&dbi->instance_lock); 1045#endif 1046 1047 } 1048 1049 return result; 1050} 1051 1052/*% 1053 * If the client is allowed to perform a zone transfer, the next order of 1054 * business is to get all the nodes in the zone, so bind can respond to the 1055 * query. 1056 */ 1057 1058static isc_result_t 1059odbc_allnodes(const char *zone, void *driverarg, void *dbdata, 1060 dns_sdlzallnodes_t *allnodes) 1061{ 1062 1063 isc_result_t result; 1064 dbinstance_t *dbi = NULL; 1065 SQLHSTMT *stmnt; 1066 SQLSMALLINT fields; 1067 char *data; 1068 char *type; 1069 char *ttl_s; 1070 int ttl; 1071 char *host; 1072 char *endp; 1073 1074 UNUSED(driverarg); 1075 1076 /* run the query and get the result set from the database. */ 1077 result = odbc_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &dbi); 1078 1079 /* if we get "not implemented", send it along */ 1080 if (result == ISC_R_NOTIMPLEMENTED) 1081 return result; 1082 1083 /* if we didn't get a result set, log an err msg. */ 1084 if (result != ISC_R_SUCCESS) { 1085 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1086 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1087 "Odbc driver unable to return " 1088 "result set for all nodes query"); 1089 return (ISC_R_FAILURE); 1090 } 1091 1092 stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt; 1093 1094 /* get number of columns */ 1095 if (!sqlOK(SQLNumResultCols(stmnt, &fields))) { 1096 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1097 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1098 "Odbc driver unable to process result set"); 1099 result = ISC_R_FAILURE; 1100 goto allnodes_cleanup; 1101 } 1102 1103 if (fields < 4) { /* gotta have at least 4 columns */ 1104 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1105 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1106 "Odbc driver too few fields returned by " 1107 "all nodes query"); 1108 result = ISC_R_FAILURE; 1109 goto allnodes_cleanup; 1110 } 1111 1112 /* get things ready for processing */ 1113 result = ISC_R_FAILURE; 1114 1115 while (sqlOK(SQLFetch(stmnt))) { 1116 1117 /* set to null for next pass through */ 1118 data = host = type = ttl_s = NULL; 1119 1120 /* 1121 * attempt to get DNS ttl, type, host, data then tell 1122 * Bind about them 1123 */ 1124 if ((result = odbc_getField(stmnt, 1, 1125 &ttl_s)) == ISC_R_SUCCESS && 1126 (result = odbc_getField(stmnt, 2, 1127 &type)) == ISC_R_SUCCESS && 1128 (result = odbc_getField(stmnt, 3, 1129 &host)) == ISC_R_SUCCESS && 1130 (result = odbc_getManyFields(stmnt, 4, fields, 1131 &data)) == ISC_R_SUCCESS) { 1132 /* convert ttl string to int */ 1133 ttl = strtol(ttl_s, &endp, 10); 1134 /* failure converting ttl. */ 1135 if (*endp != '\0' || ttl < 0) { 1136 isc_log_write(dns_lctx, 1137 DNS_LOGCATEGORY_DATABASE, 1138 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1139 "Odbc driver ttl must be " 1140 "a postive number"); 1141 result = ISC_R_FAILURE; 1142 } else { 1143 /* successful converting TTL, tell Bind */ 1144 result = dns_sdlz_putnamedrr(allnodes, host, 1145 type, ttl, data); 1146 } 1147 } /* closes big if () */ 1148 1149 /* clean up mem */ 1150 if (ttl_s != NULL) 1151 isc_mem_free(ns_g_mctx, ttl_s); 1152 if (type != NULL) 1153 isc_mem_free(ns_g_mctx, type); 1154 if (host != NULL) 1155 isc_mem_free(ns_g_mctx, host); 1156 if (data != NULL) 1157 isc_mem_free(ns_g_mctx, data); 1158 1159 /* if we weren't successful, log err msg */ 1160 if (result != ISC_R_SUCCESS) { 1161 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1162 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1163 "dns_sdlz_putnamedrr returned error. " 1164 "Error code was: %s", 1165 isc_result_totext(result)); 1166 result = ISC_R_FAILURE; 1167 goto allnodes_cleanup; 1168 } 1169 } /* closes while loop */ 1170 1171 allnodes_cleanup: 1172 1173 /* close cursor */ 1174 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt); 1175 1176#ifdef ISC_PLATFORM_USETHREADS 1177 1178 /* free lock on dbi so someone else can use it. */ 1179 isc_mutex_unlock(&dbi->instance_lock); 1180 1181#endif 1182 1183 return result; 1184} 1185 1186/*% 1187 * if the lookup function does not return SOA or NS records for the zone, 1188 * use this function to get that information for Bind. 1189 */ 1190 1191static isc_result_t 1192odbc_authority(const char *zone, void *driverarg, void *dbdata, 1193 dns_sdlzlookup_t *lookup) 1194{ 1195 isc_result_t result; 1196 dbinstance_t *dbi = NULL; 1197 1198 UNUSED(driverarg); 1199 1200 /* run the query and get the result set from the database. */ 1201 result = odbc_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &dbi); 1202 /* if we get "not implemented", send it along */ 1203 if (result == ISC_R_NOTIMPLEMENTED) 1204 return result; 1205 /* if we didn't get a result set, log an err msg. */ 1206 if (result != ISC_R_SUCCESS) { 1207 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1208 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1209 "Odbc driver unable to return " 1210 "result set for authority query"); 1211 return (ISC_R_FAILURE); 1212 } 1213 /* lookup and authority result sets are processed in the same manner */ 1214 /* odbc_process_rs does the job for both functions. */ 1215 return odbc_process_rs(lookup, dbi); 1216} 1217 1218/*% if zone is supported, lookup up a (or multiple) record(s) in it */ 1219 1220static isc_result_t 1221odbc_lookup(const char *zone, const char *name, void *driverarg, 1222 void *dbdata, dns_sdlzlookup_t *lookup) 1223{ 1224 isc_result_t result; 1225 dbinstance_t *dbi = NULL; 1226 1227 UNUSED(driverarg); 1228 1229 /* run the query and get the result set from the database. */ 1230 result = odbc_get_resultset(zone, name, NULL, LOOKUP, dbdata, &dbi); 1231 /* if we didn't get a result set, log an err msg. */ 1232 if (result != ISC_R_SUCCESS) { 1233 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1234 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1235 "Odbc driver unable to return " 1236 "result set for lookup query"); 1237 return (ISC_R_FAILURE); 1238 } 1239 /* lookup and authority result sets are processed in the same manner */ 1240 /* odbc_process_rs does the job for both functions. */ 1241 return odbc_process_rs(lookup, dbi); 1242} 1243 1244/*% 1245 * create an instance of the driver. Remember, only 1 copy of the driver's 1246 * code is ever loaded, the driver has to remember which context it's 1247 * operating in. This is done via use of the dbdata argument which is 1248 * passed into all query functions. 1249 */ 1250static isc_result_t 1251odbc_create(const char *dlzname, unsigned int argc, char *argv[], 1252 void *driverarg, void **dbdata) 1253{ 1254 isc_result_t result; 1255 odbc_instance_t *odbc_inst = NULL; 1256 dbinstance_t *db = NULL; 1257 SQLRETURN sqlRes; 1258 1259#ifdef ISC_PLATFORM_USETHREADS 1260 /* if multi-threaded, we need a few extra variables. */ 1261 int dbcount; 1262 int i; 1263 char *endp; 1264 1265#endif /* ISC_PLATFORM_USETHREADS */ 1266 1267 UNUSED(dlzname); 1268 UNUSED(driverarg); 1269 1270#ifdef ISC_PLATFORM_USETHREADS 1271 /* if debugging, let user know we are multithreaded. */ 1272 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1273 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 1274 "Odbc driver running multithreaded"); 1275#else /* ISC_PLATFORM_USETHREADS */ 1276 /* if debugging, let user know we are single threaded. */ 1277 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1278 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1), 1279 "Odbc driver running single threaded"); 1280#endif /* ISC_PLATFORM_USETHREADS */ 1281 1282 /* verify we have at least 5 arg's passed to the driver */ 1283 if (argc < 5) { 1284 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1285 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1286 "Odbc driver requires at least " 1287 "4 command line args."); 1288 return (ISC_R_FAILURE); 1289 } 1290 1291 /* no more than 8 arg's should be passed to the driver */ 1292 if (argc > 8) { 1293 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1294 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1295 "Odbc driver cannot accept more than " 1296 "7 command line args."); 1297 return (ISC_R_FAILURE); 1298 } 1299 1300 /* multithreaded build can have multiple DB connections */ 1301#ifdef ISC_PLATFORM_USETHREADS 1302 1303 /* check how many db connections we should create */ 1304 dbcount = strtol(argv[1], &endp, 10); 1305 if (*endp != '\0' || dbcount < 0) { 1306 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1307 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1308 "Odbc driver database connection count " 1309 "must be positive."); 1310 return (ISC_R_FAILURE); 1311 } 1312 1313#endif /* ISC_PLATFORM_USETHREADS */ 1314 1315 /* allocate memory for odbc instance */ 1316 odbc_inst = isc_mem_get(ns_g_mctx, sizeof(odbc_instance_t)); 1317 if (odbc_inst == NULL) 1318 return (ISC_R_NOMEMORY); 1319 memset(odbc_inst, 0, sizeof(odbc_instance_t)); 1320 1321 /* parse connection string and get paramters. */ 1322 1323 /* get odbc database dsn - required */ 1324 odbc_inst->dsn = (SQLCHAR *) getParameterValue(argv[2], 1325 "dsn="); 1326 if (odbc_inst->dsn == NULL) { 1327 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1328 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1329 "odbc driver requires a dns parameter."); 1330 result = ISC_R_FAILURE; 1331 goto cleanup; 1332 } 1333 /* get odbc database username */ 1334 /* if no username was passed, set odbc_inst.user = NULL; */ 1335 odbc_inst->user = (SQLCHAR *) getParameterValue(argv[2], 1336 "user="); 1337 1338 /* get odbc database password */ 1339 /* if no password was passed, set odbc_inst.pass = NULL; */ 1340 odbc_inst->pass = (SQLCHAR *) getParameterValue(argv[2], "pass="); 1341 1342 /* create odbc environment & set environment to ODBC V3 */ 1343 if (odbc_inst->sql_env == NULL) { 1344 /* create environment handle */ 1345 sqlRes = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, 1346 &(odbc_inst->sql_env)); 1347 if (!sqlOK(sqlRes)) { 1348 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1349 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 1350 "Odbc driver unable to allocate memory"); 1351 result = ISC_R_NOMEMORY; 1352 goto cleanup; 1353 } 1354 /*set ODBC version = 3 */ 1355 sqlRes = SQLSetEnvAttr(odbc_inst->sql_env, 1356 SQL_ATTR_ODBC_VERSION, 1357 (void *) SQL_OV_ODBC3, 0); 1358 if (!sqlOK(sqlRes)) { 1359 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1360 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 1361 "Unable to configure ODBC environment"); 1362 result = ISC_R_NOMEMORY; 1363 goto cleanup; 1364 } 1365 } 1366 1367#ifdef ISC_PLATFORM_USETHREADS 1368 1369 /* allocate memory for database connection list */ 1370 odbc_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t)); 1371 if (odbc_inst->db == NULL) { 1372 result = ISC_R_NOMEMORY; 1373 goto cleanup; 1374 } 1375 1376 1377 /* initialize DB connection list */ 1378 ISC_LIST_INIT(*odbc_inst->db); 1379 1380 /* create the appropriate number of database instances (DBI) */ 1381 /* append each new DBI to the end of the list */ 1382 for (i=0; i < dbcount; i++) { 1383 1384#endif /* ISC_PLATFORM_USETHREADS */ 1385 1386 /* how many queries were passed in from config file? */ 1387 switch(argc) { 1388 case 5: 1389 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, 1390 NULL, argv[3], argv[4], 1391 NULL, &db); 1392 break; 1393 case 6: 1394 result = build_sqldbinstance(ns_g_mctx, NULL, NULL, 1395 argv[5], argv[3], argv[4], 1396 NULL, &db); 1397 break; 1398 case 7: 1399 result = build_sqldbinstance(ns_g_mctx, argv[6], NULL, 1400 argv[5], argv[3], argv[4], 1401 NULL, &db); 1402 break; 1403 case 8: 1404 result = build_sqldbinstance(ns_g_mctx, argv[6], 1405 argv[7], argv[5], argv[3], 1406 argv[4], NULL, &db); 1407 break; 1408 default: 1409 /* not really needed, should shut up compiler. */ 1410 result = ISC_R_FAILURE; 1411 } 1412 1413 /* unsuccessful?, log err msg and cleanup. */ 1414 if (result != ISC_R_SUCCESS) { 1415 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1416 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1417 "Odbc driver could not create " 1418 "database instance object."); 1419 goto cleanup; 1420 } 1421 1422#ifdef ISC_PLATFORM_USETHREADS 1423 1424 /* when multithreaded, build a list of DBI's */ 1425 ISC_LINK_INIT(db, link); 1426 ISC_LIST_APPEND(*odbc_inst->db, db, link); 1427 1428#endif 1429 1430 result = odbc_connect(odbc_inst, (odbc_db_t **) &(db->dbconn)); 1431 1432 if (result != ISC_R_SUCCESS) { 1433 1434#ifdef ISC_PLATFORM_USETHREADS 1435 1436 /* 1437 * if multi threaded, let user know which 1438 * connection failed. user could be 1439 * attempting to create 10 db connections and 1440 * for some reason the db backend only allows 1441 * 9. 1442 */ 1443 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1444 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1445 "Odbc driver failed to create database " 1446 "connection number %u after 3 attempts", 1447 i+1); 1448#else 1449 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1450 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 1451 "Odbc driver failed to create database " 1452 "connection after 3 attempts"); 1453#endif 1454 goto cleanup; 1455 } 1456 1457#ifdef ISC_PLATFORM_USETHREADS 1458 1459 /* set DB = null for next loop through. */ 1460 db = NULL; 1461 1462 } /* end for loop */ 1463 1464#else 1465 /* tell odbc_inst about the db connection we just created. */ 1466 odbc_inst->db = db; 1467 1468#endif 1469 1470 /* set dbdata to the odbc_instance we created. */ 1471 *dbdata = odbc_inst; 1472 1473 /* hey, we got through all of that ok, return success. */ 1474 return(ISC_R_SUCCESS); 1475 1476 cleanup: 1477 1478 destroy_odbc_instance(odbc_inst); 1479 1480 return result; 1481} 1482 1483/*% 1484 * destroy an instance of the driver. Remember, only 1 copy of the driver's 1485 * code is ever loaded, the driver has to remember which context it's 1486 * operating in. This is done via use of the dbdata argument. 1487 * so we really only need to clean it up since we are not using driverarg. 1488 */ 1489 1490static void 1491odbc_destroy(void *driverarg, void *dbdata) 1492{ 1493 UNUSED(driverarg); 1494 1495 destroy_odbc_instance((odbc_instance_t *) dbdata); 1496} 1497 1498 1499/* pointers to all our runtime methods. */ 1500/* this is used during driver registration */ 1501/* i.e. in dlz_odbc_init below. */ 1502static dns_sdlzmethods_t dlz_odbc_methods = { 1503 odbc_create, 1504 odbc_destroy, 1505 odbc_findzone, 1506 odbc_lookup, 1507 odbc_authority, 1508 odbc_allnodes, 1509 odbc_allowzonexfr, 1510 NULL, 1511 NULL, 1512 NULL, 1513 NULL, 1514 NULL, 1515 NULL, 1516 NULL, 1517}; 1518 1519/*% 1520 * Wrapper around dns_sdlzregister(). 1521 */ 1522isc_result_t 1523dlz_odbc_init(void) { 1524 isc_result_t result; 1525 1526 /* 1527 * Write debugging message to log 1528 */ 1529 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1530 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1531 "Registering DLZ odbc driver."); 1532 1533 /* 1534 * Driver is always threadsafe. When multithreaded all 1535 * functions use multithreaded code. When not multithreaded, 1536 * all functions can only be entered once, but only 1 thread 1537 * of operation is available in Bind. So everything is still 1538 * threadsafe. 1539 */ 1540 result = dns_sdlzregister("odbc", &dlz_odbc_methods, NULL, 1541 DNS_SDLZFLAG_RELATIVEOWNER | 1542 DNS_SDLZFLAG_RELATIVERDATA | 1543 DNS_SDLZFLAG_THREADSAFE, 1544 ns_g_mctx, &dlz_odbc); 1545 /* if we can't register the driver, there are big problems. */ 1546 if (result != ISC_R_SUCCESS) { 1547 UNEXPECTED_ERROR(__FILE__, __LINE__, 1548 "dns_sdlzregister() failed: %s", 1549 isc_result_totext(result)); 1550 result = ISC_R_UNEXPECTED; 1551 } 1552 1553 1554 return result; 1555} 1556 1557/*% 1558 * Wrapper around dns_sdlzunregister(). 1559 */ 1560void 1561dlz_odbc_clear(void) { 1562 1563 /* 1564 * Write debugging message to log 1565 */ 1566 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1567 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 1568 "Unregistering DLZ odbc driver."); 1569 1570 /* unregister the driver. */ 1571 if (dlz_odbc != NULL) 1572 dns_sdlzunregister(&dlz_odbc); 1573} 1574 1575#endif 1576