No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / dlz / drivers / dlz_odbc_driver.c
blob9cd12f811e0b5e253027a451bb7eb9a532ae02c3
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
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.
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.
20 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21 * conceived and contributed by Rob Butler.
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.
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.
39 * Copyright (C) 1999-2001 Internet Software Consortium.
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.
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.
55 #ifdef DLZ_ODBC
57 #include <config.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <stdlib.h>
62 #include <dns/log.h>
63 #include <dns/sdlz.h>
64 #include <dns/result.h>
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>
73 #include <named/globals.h>
75 #include <dlz/sdlz_helper.h>
76 #include <dlz/dlz_odbc_driver.h>
78 #include <sql.h>
79 #include <sqlext.h>
80 #include <sqltypes.h>
82 static dns_sdlzimplementation_t *dlz_odbc = NULL;
84 #define dbc_search_limit 30
85 #define ALLNODES 1
86 #define ALLOWXFR 2
87 #define AUTHORITY 3
88 #define FINDZONE 4
89 #define LOOKUP 5
91 #define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0)
94 * Private Structures
98 * structure to hold ODBC connection & statement
101 typedef struct{
102 SQLHDBC dbc;
103 SQLHSTMT stmnt;
104 } odbc_db_t;
107 * Structure to hold everthing needed by this "instance" of the odbc driver
108 * remember, the driver code is only loaded once, but may have many separate
109 * instances
112 typedef struct {
114 #ifdef ISC_PLATFORM_USETHREADS
116 db_list_t *db; /* handle to a list of DB */
118 #else
120 dbinstance_t *db; /* handle to db */
122 #endif
124 SQLHENV sql_env; /* handle to SQL environment */
125 SQLCHAR *dsn;
126 SQLCHAR *user;
127 SQLCHAR *pass;
128 } odbc_instance_t;
130 /* forward reference */
132 static size_t
133 odbc_makesafe(char *to, const char *from, size_t length);
136 * Private methods
139 static SQLSMALLINT
140 safeLen(void *a) {
141 if (a == NULL)
142 return 0;
143 return strlen((char *) a);
146 /*% propertly cleans up an odbc_instance_t */
148 static void
149 destroy_odbc_instance(odbc_instance_t *odbc_inst) {
151 #ifdef ISC_PLATFORM_USETHREADS
153 dbinstance_t *ndbi = NULL;
154 dbinstance_t *dbi = NULL;
156 /* get the first DBI in the list */
157 ndbi = ISC_LIST_HEAD(*odbc_inst->db);
159 /* loop through the list */
160 while (ndbi != NULL) {
161 dbi = ndbi;
162 /* get the next DBI in the list */
163 ndbi = ISC_LIST_NEXT(dbi, link);
165 /* if we have a connection / statement object in memory */
166 if (dbi->dbconn != NULL) {
167 /* free statement handle */
168 if (((odbc_db_t *) (dbi->dbconn))->stmnt != NULL) {
169 SQLFreeHandle(SQL_HANDLE_STMT,
170 ((odbc_db_t *)
171 (dbi->dbconn))->stmnt);
172 ((odbc_db_t *) (dbi->dbconn))->stmnt = NULL;
175 /* disconnect from database & free connection handle */
176 if (((odbc_db_t *) (dbi->dbconn))->dbc != NULL) {
177 SQLDisconnect(((odbc_db_t *)
178 dbi->dbconn)->dbc);
179 SQLFreeHandle(SQL_HANDLE_DBC,
180 ((odbc_db_t *)
181 (dbi->dbconn))->dbc);
182 ((odbc_db_t *) (dbi->dbconn))->dbc = NULL;
185 /* free memory that held connection & statement. */
186 isc_mem_free(ns_g_mctx, dbi->dbconn);
188 /* release all memory that comprised a DBI */
189 destroy_sqldbinstance(dbi);
191 /* release memory for the list structure */
192 isc_mem_put(ns_g_mctx, odbc_inst->db, sizeof(db_list_t));
194 #else /* ISC_PLATFORM_USETHREADS */
196 /* free statement handle */
197 if (((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt != NULL) {
198 SQLFreeHandle(SQL_HANDLE_STMT,
199 ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt);
200 ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt = NULL;
203 /* disconnect from database, free connection handle */
204 if (((odbc_db_t *) (odbc_inst->db->dbconn))->dbc != NULL) {
205 SQLDisconnect(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc);
206 SQLFreeHandle(SQL_HANDLE_DBC,
207 ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc);
208 ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc = NULL;
210 /* free mem for the odbc_db_t structure held in db */
211 if (((odbc_db_t *) odbc_inst->db->dbconn) != NULL) {
212 isc_mem_free(ns_g_mctx, odbc_inst->db->dbconn);
213 odbc_inst->db->dbconn = NULL;
216 if (odbc_inst->db != NULL)
217 destroy_sqldbinstance(odbc_inst->db);
219 #endif /* ISC_PLATFORM_USETHREADS */
222 /* free sql environment */
223 if (odbc_inst->sql_env != NULL)
224 SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env);
226 /* free ODBC instance strings */
227 if (odbc_inst->dsn != NULL)
228 isc_mem_free(ns_g_mctx, odbc_inst->dsn);
229 if (odbc_inst->pass != NULL)
230 isc_mem_free(ns_g_mctx, odbc_inst->pass);
231 if (odbc_inst->user != NULL)
232 isc_mem_free(ns_g_mctx, odbc_inst->user);
234 /* free memory for odbc_inst */
235 if (odbc_inst != NULL)
236 isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t));
240 /*% Connects to database, and creates ODBC statements */
242 static isc_result_t
243 odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc) {
245 odbc_db_t *ndb = *dbc;
246 SQLRETURN sqlRes;
247 isc_result_t result = ISC_R_SUCCESS;
249 if (ndb != NULL) {
251 * if db != null, we have to do some cleanup
252 * if statement handle != null free it
254 if (ndb->stmnt != NULL) {
255 SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt);
256 ndb->stmnt = NULL;
259 /* if connection handle != null free it */
260 if (ndb->dbc != NULL) {
261 SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc);
262 ndb->dbc = NULL;
264 } else {
265 ndb = isc_mem_allocate(ns_g_mctx, sizeof(odbc_db_t));
266 if (ndb == NULL) {
267 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
268 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
269 "Odbc driver unable to allocate memory");
270 return ISC_R_NOMEMORY;
272 memset(ndb, 0, sizeof(odbc_db_t));
275 sqlRes = SQLAllocHandle(SQL_HANDLE_DBC, dbi->sql_env, &(ndb->dbc));
276 if (!sqlOK(sqlRes)) {
277 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
278 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
279 "Odbc driver unable to allocate memory");
280 result = ISC_R_NOMEMORY;
281 goto cleanup;
284 sqlRes = SQLConnect(ndb->dbc, dbi->dsn, safeLen(dbi->dsn), dbi->user,
285 safeLen(dbi->user), dbi->pass, safeLen(dbi->pass));
286 if (!sqlOK(sqlRes)) {
287 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
288 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
289 "Odbc driver unable to connect");
290 result = ISC_R_FAILURE;
291 goto cleanup;
294 sqlRes = SQLAllocHandle(SQL_HANDLE_STMT, ndb->dbc, &(ndb->stmnt));
295 if (!sqlOK(sqlRes)) {
296 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
297 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
298 "Odbc driver unable to allocate memory");
299 result = ISC_R_NOMEMORY;
300 goto cleanup;
303 *dbc = ndb;
305 return ISC_R_SUCCESS;
307 cleanup:
309 if (ndb != NULL) {
311 /* if statement handle != null free it */
312 if (ndb->stmnt != NULL) {
313 SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt);
314 ndb->stmnt = NULL;
317 /* if connection handle != null free it */
318 if (ndb->dbc != NULL) {
319 SQLDisconnect(ndb->dbc);
320 SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc);
321 ndb->dbc = NULL;
323 /* free memory holding ndb */
324 isc_mem_free(ns_g_mctx, ndb);
327 return result;
331 * Loops through the list of DB instances, attempting to lock
332 * on the mutex. If successful, the DBI is reserved for use
333 * and the thread can perform queries against the database.
334 * If the lock fails, the next one in the list is tried.
335 * looping continues until a lock is obtained, or until
336 * the list has been searched dbc_search_limit times.
337 * This function is only used when the driver is compiled for
338 * multithreaded operation.
341 #ifdef ISC_PLATFORM_USETHREADS
343 static dbinstance_t *
344 odbc_find_avail_conn(db_list_t *dblist)
346 dbinstance_t *dbi = NULL;
347 dbinstance_t *head;
348 int count = 0;
350 /* get top of list */
351 head = dbi = ISC_LIST_HEAD(*dblist);
353 /* loop through list */
354 while (count < dbc_search_limit) {
355 /* try to lock on the mutex */
356 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
357 return dbi; /* success, return the DBI for use. */
359 /* not successful, keep trying */
360 dbi = ISC_LIST_NEXT(dbi, link);
362 /* check to see if we have gone to the top of the list. */
363 if (dbi == NULL) {
364 count++;
365 dbi = head;
368 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
369 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
370 "Odbc driver unable to find available "
371 "connection after searching %d times",
372 count);
373 return NULL;
376 #endif /* ISC_PLATFORM_USETHREADS */
378 /*% Allocates memory for a new string, and then constructs the new
379 * string by "escaping" the input string. The new string is
380 * safe to be used in queries. This is necessary because we cannot
381 * be sure of what types of strings are passed to us, and we don't
382 * want special characters in the string causing problems.
385 static char *
386 odbc_escape_string(const char *instr) {
388 char *outstr;
389 unsigned int len;
391 if (instr == NULL)
392 return NULL;
394 len = strlen(instr);
396 outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
397 if (outstr == NULL)
398 return NULL;
400 odbc_makesafe(outstr, instr, len);
402 return outstr;
405 /* ---------------
406 * Escaping arbitrary strings to get valid SQL strings/identifiers.
408 * Replaces "\\" with "\\\\" and "'" with "''".
409 * length is the length of the buffer pointed to by
410 * from. The buffer at to must be at least 2*length + 1 characters
411 * long. A terminating NUL character is written.
413 * NOTICE!!!
414 * This function was borrowed directly from PostgreSQL's libpq.
416 * The copyright statements from the original file containing this
417 * function are included below:
418 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
419 * Portions Copyright (c) 1994, Regents of the University of California
420 * ---------------
423 static size_t
424 odbc_makesafe(char *to, const char *from, size_t length)
426 const char *source = from;
427 char *target = to;
428 unsigned int remaining = length;
430 while (remaining > 0)
432 switch (*source)
434 case '\\':
435 *target = '\\';
436 target++;
437 *target = '\\';
438 /* target and remaining are updated below. */
439 break;
441 case '\'':
442 *target = '\'';
443 target++;
444 *target = '\'';
445 /* target and remaining are updated below. */
446 break;
448 default:
449 *target = *source;
450 /* target and remaining are updated below. */
452 source++;
453 target++;
454 remaining--;
457 /* Write the terminating NUL character. */
458 *target = '\0';
460 return target - to;
464 * This function is the real core of the driver. Zone, record
465 * and client strings are passed in (or NULL is passed if the
466 * string is not available). The type of query we want to run
467 * is indicated by the query flag, and the dbdata object is passed
468 * passed in to. dbdata really holds either:
469 * 1) a list of database instances (in multithreaded mode) OR
470 * 2) a single database instance (in single threaded mode)
471 * The function will construct the query and obtain an available
472 * database instance (DBI). It will then run the query and hopefully
473 * obtain a result set. The data base instance that is used is returned
474 * to the caller so they can get the data from the result set from it.
475 * If successfull, it will be the responsibility of the caller to close
476 * the cursor, and unlock the mutex of the DBI when they are done with it.
477 * If not successfull, this function will perform all the cleanup.
481 static isc_result_t
482 odbc_get_resultset(const char *zone, const char *record,
483 const char *client, unsigned int query,
484 void *dbdata, dbinstance_t **r_dbi)
487 isc_result_t result;
488 dbinstance_t *dbi = NULL;
489 char *querystring = NULL;
490 unsigned int j = 0;
491 SQLRETURN sqlRes;
493 REQUIRE(*r_dbi == NULL);
495 /* get db instance / connection */
496 #ifdef ISC_PLATFORM_USETHREADS
498 /* find an available DBI from the list */
499 dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db);
501 #else /* ISC_PLATFORM_USETHREADS */
504 * only 1 DBI - no need to lock instance lock either
505 * only 1 thread in the whole process, no possible contention.
507 dbi = (dbinstance_t *) ((odbc_instance_t *) dbdata)->db;
509 #endif /* ISC_PLATFORM_USETHREADS */
511 /* if DBI is null, can't do anything else */
512 if (dbi == NULL) {
513 result = ISC_R_FAILURE;
514 goto cleanup;
517 /* what type of query are we going to run? */
518 switch(query) {
519 case ALLNODES:
521 * if the query was not passed in from the config file
522 * then we can't run it. return not_implemented, so
523 * it's like the code for that operation was never
524 * built into the driver.... AHHH flexibility!!!
526 if (dbi->allnodes_q == NULL) {
527 result = ISC_R_NOTIMPLEMENTED;
528 goto cleanup;
530 break;
531 case ALLOWXFR:
532 /* same as comments as ALLNODES */
533 if (dbi->allowxfr_q == NULL) {
534 result = ISC_R_NOTIMPLEMENTED;
535 goto cleanup;
537 break;
538 case AUTHORITY:
539 /* same as comments as ALLNODES */
540 if (dbi->authority_q == NULL) {
541 result = ISC_R_NOTIMPLEMENTED;
542 goto cleanup;
544 break;
545 case FINDZONE:
546 /* this is required. It's the whole point of DLZ! */
547 if (dbi->findzone_q == NULL) {
548 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
549 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
550 "No query specified for findzone. "
551 "Findzone requires a query");
552 result = ISC_R_FAILURE;
553 goto cleanup;
555 break;
556 case LOOKUP:
557 /* this is required. It's also a major point of DLZ! */
558 if (dbi->lookup_q == NULL) {
559 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
560 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
561 "No query specified for lookup. "
562 "Lookup requires a query");
563 result = ISC_R_FAILURE;
564 goto cleanup;
566 break;
567 default:
569 * this should never happen. If it does, the code is
570 * screwed up!
572 UNEXPECTED_ERROR(__FILE__, __LINE__,
573 "Incorrect query flag passed to "
574 "odbc_get_resultset");
575 result = ISC_R_UNEXPECTED;
576 goto cleanup;
581 * was a zone string passed? If so, make it safe for use in
582 * queries.
584 if (zone != NULL) {
585 dbi->zone = odbc_escape_string(zone);
586 if (dbi->zone == NULL) {
587 result = ISC_R_NOMEMORY;
588 goto cleanup;
590 } else { /* no string passed, set the string pointer to NULL */
591 dbi->zone = NULL;
595 * was a record string passed? If so, make it safe for use in
596 * queries.
598 if (record != NULL) {
599 dbi->record = odbc_escape_string(record);
600 if (dbi->record == NULL) {
601 result = ISC_R_NOMEMORY;
602 goto cleanup;
604 } else { /* no string passed, set the string pointer to NULL */
605 dbi->record = NULL;
609 * was a client string passed? If so, make it safe for use in
610 * queries.
612 if (client != NULL) {
613 dbi->client = odbc_escape_string(client);
614 if (dbi->client == NULL) {
615 result = ISC_R_NOMEMORY;
616 goto cleanup;
618 } else { /* no string passed, set the string pointer to NULL */
619 dbi->client = NULL;
623 * what type of query are we going to run?
624 * this time we build the actual query to run.
626 switch(query) {
627 case ALLNODES:
628 querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
629 break;
630 case ALLOWXFR:
631 querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
632 break;
633 case AUTHORITY:
634 querystring = build_querystring(ns_g_mctx, dbi->authority_q);
635 break;
636 case FINDZONE:
637 querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
638 break;
639 case LOOKUP:
640 querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
641 break;
642 default:
644 * this should never happen. If it does, the code is
645 * screwed up!
647 UNEXPECTED_ERROR(__FILE__, __LINE__,
648 "Incorrect query flag passed to "
649 "odbc_get_resultset");
650 result = ISC_R_UNEXPECTED;
651 goto cleanup;
654 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
655 if (querystring == NULL) {
656 result = ISC_R_NOMEMORY;
657 goto cleanup;
660 /* output the full query string during debug so we can see */
661 /* what lame error the query has. */
662 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
663 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
664 "\nQuery String: %s\n", querystring);
666 /* attempt query up to 3 times. */
667 for (j=0; j < 3; j++) {
668 /* try to get result set */
669 sqlRes = SQLExecDirect(((odbc_db_t *) dbi->dbconn)->stmnt,
670 (SQLCHAR *) querystring,
671 (SQLINTEGER) strlen(querystring));
673 /* if error, reset DB connection */
674 if (!sqlOK(sqlRes)) {
675 /* close cursor */
676 SQLCloseCursor(((odbc_db_t *) dbi->dbconn)->stmnt);
677 /* attempt to reconnect */
678 result = odbc_connect((odbc_instance_t *) dbdata,
679 (odbc_db_t **) &(dbi->dbconn));
680 /* check if we reconnected */
681 if (result != ISC_R_SUCCESS)
682 break;
683 /* incase this is the last time through the loop */
684 result = ISC_R_FAILURE;
685 } else {
686 result = ISC_R_SUCCESS;
687 /* return dbi */
688 *r_dbi = dbi;
689 /* result set ok, break loop */
690 break;
692 } /* end for loop */
694 cleanup: /* it's always good to cleanup after yourself */
696 /* if we couldn't even allocate DBI, just return NULL */
697 if (dbi == NULL)
698 return ISC_R_FAILURE;
700 /* free dbi->zone string */
701 if (dbi->zone != NULL)
702 isc_mem_free(ns_g_mctx, dbi->zone);
704 /* free dbi->record string */
705 if (dbi->record != NULL)
706 isc_mem_free(ns_g_mctx, dbi->record);
708 /* free dbi->client string */
709 if (dbi->client != NULL)
710 isc_mem_free(ns_g_mctx, dbi->client);
712 #ifdef ISC_PLATFORM_USETHREADS
714 /* if we are done using this dbi, release the lock */
715 if (result != ISC_R_SUCCESS)
716 isc_mutex_unlock(&dbi->instance_lock);
718 #endif /* ISC_PLATFORM_USETHREADS */
720 /* release query string */
721 if (querystring != NULL)
722 isc_mem_free(ns_g_mctx, querystring );
724 /* return result */
725 return result;
730 * Gets a single field from the ODBC statement. The memory for the
731 * returned data is dynamically allocated. If this method is successful
732 * it is the reponsibility of the caller to free the memory using
733 * isc_mem_free(ns_g_mctx, *ptr);
736 static isc_result_t
737 odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data) {
739 SQLINTEGER size;
741 REQUIRE(data != NULL && *data == NULL);
743 if (sqlOK(SQLColAttribute(stmnt, field, SQL_DESC_DISPLAY_SIZE,
744 NULL, 0, NULL, &size)) && size > 0) {
745 *data = isc_mem_allocate(ns_g_mctx, size + 1);
746 if (data != NULL) {
747 if (sqlOK(SQLGetData(stmnt, field, SQL_C_CHAR,
748 *data, size + 1,&size)))
749 return ISC_R_SUCCESS;
750 isc_mem_free(ns_g_mctx, *data);
753 return ISC_R_FAILURE;
757 * Gets multiple fields from the ODBC statement. The memory for the
758 * returned data is dynamically allocated. If this method is successful
759 * it is the reponsibility of the caller to free the memory using
760 * isc_mem_free(ns_g_mctx, *ptr);
763 static isc_result_t
764 odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField,
765 SQLSMALLINT endField, char **retData) {
767 isc_result_t result;
768 SQLINTEGER size;
769 int totSize = 0;
770 SQLSMALLINT i;
771 int j = 0;
772 char *data;
774 REQUIRE(retData != NULL && *retData == NULL);
775 REQUIRE(startField > 0 && startField <= endField);
777 /* determine how large the data is */
778 for (i=startField; i <= endField; i++)
779 if (sqlOK(SQLColAttribute(stmnt, i, SQL_DESC_DISPLAY_SIZE,
780 NULL, 0, NULL, &size)) && size > 0) {
781 /* always allow for a " " (space) character */
782 totSize += (size + 1);
783 /* after the data item */
786 if (totSize < 1)
787 return ISC_R_FAILURE;
789 /* allow for a "\n" at the end of the string/ */
790 data = isc_mem_allocate(ns_g_mctx, ++totSize);
791 if (data == NULL)
792 return ISC_R_NOMEMORY;
794 result = ISC_R_FAILURE;
796 /* get the data and concat all fields into a large string */
797 for (i=startField; i <= endField; i++) {
798 if (sqlOK(SQLGetData(stmnt, i, SQL_C_CHAR, &(data[j]),
799 totSize - j, &size))) {
800 if (size > 0) {
801 j += size;
802 data[j++] = ' ';
803 data[j] = '\0';
804 result = ISC_R_SUCCESS;
806 } else {
807 isc_mem_free(ns_g_mctx, data);
808 return ISC_R_FAILURE;
812 if (result != ISC_R_SUCCESS) {
813 isc_mem_free(ns_g_mctx, data);
814 return result;
817 *retData = data;
818 return ISC_R_SUCCESS;
823 * The processing of result sets for lookup and authority are
824 * exactly the same. So that functionality has been moved
825 * into this function to minimize code.
828 static isc_result_t
829 odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *dbi)
833 isc_result_t result;
834 SQLSMALLINT fields;
835 SQLHSTMT *stmnt;
836 char *ttl_s;
837 char *type;
838 char *data;
839 char *endp;
840 int ttl;
842 REQUIRE(dbi != NULL);
844 stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt;
846 /* get number of columns */
847 if (!sqlOK(SQLNumResultCols(stmnt, &fields))) {
848 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
849 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
850 "Odbc driver unable to process result set");
851 result = ISC_R_FAILURE;
852 goto process_rs_cleanup;
855 /* get things ready for processing */
856 result = ISC_R_FAILURE;
858 while (sqlOK(SQLFetch(stmnt))) {
860 /* set to null for next pass through */
861 data = type = ttl_s = NULL;
863 switch(fields) {
864 case 1:
866 * one column in rs, it's the data field. use
867 * default type of A record, and default TTL
868 * of 86400. attempt to get data, & tell bind
869 * about it.
871 if ((result = odbc_getField(stmnt, 1,
872 &data)) == ISC_R_SUCCESS) {
873 result = dns_sdlz_putrr(lookup, "a",
874 86400, data);
876 break;
877 case 2:
879 * two columns, data field, and data type.
880 * use default TTL of 86400. attempt to get
881 * DNS type & data, then tell bind about it.
883 if ((result = odbc_getField(stmnt, 1,
884 &type)) == ISC_R_SUCCESS &&
885 (result = odbc_getField(stmnt, 2,
886 &data)) == ISC_R_SUCCESS) {
887 result = dns_sdlz_putrr(lookup, type,
888 86400, data);
890 break;
891 default:
893 * 3 fields or more, concatenate the last ones
894 * together. attempt to get DNS ttl, type,
895 * data then tell Bind about them.
897 if ((result = odbc_getField(stmnt, 1, &ttl_s))
898 == ISC_R_SUCCESS &&
899 (result = odbc_getField(stmnt, 2, &type))
900 == ISC_R_SUCCESS &&
901 (result = odbc_getManyFields(stmnt, 3,
902 fields, &data))
903 == ISC_R_SUCCESS) {
904 /* try to convert ttl string to int */
905 ttl = strtol(ttl_s, &endp, 10);
906 /* failure converting ttl. */
907 if (*endp != '\0' || ttl < 0) {
908 isc_log_write(dns_lctx,
909 DNS_LOGCATEGORY_DATABASE,
910 DNS_LOGMODULE_DLZ,
911 ISC_LOG_ERROR,
912 "Odbc driver ttl must "
913 "be a postive number");
914 result = ISC_R_FAILURE;
915 } else {
917 * successful converting TTL,
918 * tell Bind everything
920 result = dns_sdlz_putrr(lookup, type,
921 ttl, data);
923 } /* closes bid if () */
924 } /* closes switch(fields) */
926 /* clean up mem */
927 if (ttl_s != NULL)
928 isc_mem_free(ns_g_mctx, ttl_s);
929 if (type != NULL)
930 isc_mem_free(ns_g_mctx, type);
931 if (data != NULL)
932 isc_mem_free(ns_g_mctx, data);
934 /* I sure hope we were successful */
935 if (result != ISC_R_SUCCESS) {
936 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
937 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
938 "dns_sdlz_putrr returned error. "
939 "Error code was: %s",
940 isc_result_totext(result));
941 result = ISC_R_FAILURE;
942 goto process_rs_cleanup;
944 } /* closes while loop */
946 process_rs_cleanup:
948 /* close cursor */
949 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
951 #ifdef ISC_PLATFORM_USETHREADS
953 /* free lock on dbi so someone else can use it. */
954 isc_mutex_unlock(&dbi->instance_lock);
956 #endif
958 return result;
962 * SDLZ interface methods
965 /*% determine if the zone is supported by (in) the database */
967 static isc_result_t
968 odbc_findzone(void *driverarg, void *dbdata, const char *name)
971 isc_result_t result;
972 dbinstance_t *dbi = NULL;
974 UNUSED(driverarg);
976 /* run the query and get the result set from the database. */
977 /* if result != ISC_R_SUCCESS cursor and mutex already cleaned up. */
978 /* so we don't have to do it here. */
979 result = odbc_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &dbi);
981 /* Check that we got a result set with data */
982 if (result == ISC_R_SUCCESS &&
983 !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) {
984 result = ISC_R_NOTFOUND;
987 if (dbi != NULL) {
988 /* get rid of result set, we are done with it. */
989 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
991 #ifdef ISC_PLATFORM_USETHREADS
993 /* free lock on dbi so someone else can use it. */
994 isc_mutex_unlock(&dbi->instance_lock);
995 #endif
998 return result;
1001 /*% Determine if the client is allowed to perform a zone transfer */
1002 static isc_result_t
1003 odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name,
1004 const char *client)
1006 isc_result_t result;
1007 dbinstance_t *dbi = NULL;
1009 UNUSED(driverarg);
1011 /* first check if the zone is supported by the database. */
1012 result = odbc_findzone(driverarg, dbdata, name);
1013 if (result != ISC_R_SUCCESS)
1014 return (ISC_R_NOTFOUND);
1017 * if we get to this point we know the zone is supported by
1018 * the database. the only questions now are is the zone
1019 * transfer is allowed for this client and did the config file
1020 * have an allow zone xfr query
1022 * Run our query, and get a result set from the database. if
1023 * result != ISC_R_SUCCESS cursor and mutex already cleaned
1024 * up, so we don't have to do it here.
1026 result = odbc_get_resultset(name, NULL, client, ALLOWXFR,
1027 dbdata, &dbi);
1029 /* if we get "not implemented", send it along. */
1030 if (result == ISC_R_NOTIMPLEMENTED)
1031 return result;
1033 /* Check that we got a result set with data */
1034 if (result == ISC_R_SUCCESS &&
1035 !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) {
1036 result = ISC_R_NOPERM;
1039 if (dbi != NULL) {
1040 /* get rid of result set, we are done with it. */
1041 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
1043 #ifdef ISC_PLATFORM_USETHREADS
1045 /* free lock on dbi so someone else can use it. */
1046 isc_mutex_unlock(&dbi->instance_lock);
1047 #endif
1051 return result;
1055 * If the client is allowed to perform a zone transfer, the next order of
1056 * business is to get all the nodes in the zone, so bind can respond to the
1057 * query.
1060 static isc_result_t
1061 odbc_allnodes(const char *zone, void *driverarg, void *dbdata,
1062 dns_sdlzallnodes_t *allnodes)
1065 isc_result_t result;
1066 dbinstance_t *dbi = NULL;
1067 SQLHSTMT *stmnt;
1068 SQLSMALLINT fields;
1069 char *data;
1070 char *type;
1071 char *ttl_s;
1072 int ttl;
1073 char *host;
1074 char *endp;
1076 UNUSED(driverarg);
1078 /* run the query and get the result set from the database. */
1079 result = odbc_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &dbi);
1081 /* if we get "not implemented", send it along */
1082 if (result == ISC_R_NOTIMPLEMENTED)
1083 return result;
1085 /* if we didn't get a result set, log an err msg. */
1086 if (result != ISC_R_SUCCESS) {
1087 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1088 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1089 "Odbc driver unable to return "
1090 "result set for all nodes query");
1091 return (ISC_R_FAILURE);
1094 stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt;
1096 /* get number of columns */
1097 if (!sqlOK(SQLNumResultCols(stmnt, &fields))) {
1098 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1099 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1100 "Odbc driver unable to process result set");
1101 result = ISC_R_FAILURE;
1102 goto allnodes_cleanup;
1105 if (fields < 4) { /* gotta have at least 4 columns */
1106 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1107 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1108 "Odbc driver too few fields returned by "
1109 "all nodes query");
1110 result = ISC_R_FAILURE;
1111 goto allnodes_cleanup;
1114 /* get things ready for processing */
1115 result = ISC_R_FAILURE;
1117 while (sqlOK(SQLFetch(stmnt))) {
1119 /* set to null for next pass through */
1120 data = host = type = ttl_s = NULL;
1123 * attempt to get DNS ttl, type, host, data then tell
1124 * Bind about them
1126 if ((result = odbc_getField(stmnt, 1,
1127 &ttl_s)) == ISC_R_SUCCESS &&
1128 (result = odbc_getField(stmnt, 2,
1129 &type)) == ISC_R_SUCCESS &&
1130 (result = odbc_getField(stmnt, 3,
1131 &host)) == ISC_R_SUCCESS &&
1132 (result = odbc_getManyFields(stmnt, 4, fields,
1133 &data)) == ISC_R_SUCCESS) {
1134 /* convert ttl string to int */
1135 ttl = strtol(ttl_s, &endp, 10);
1136 /* failure converting ttl. */
1137 if (*endp != '\0' || ttl < 0) {
1138 isc_log_write(dns_lctx,
1139 DNS_LOGCATEGORY_DATABASE,
1140 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1141 "Odbc driver ttl must be "
1142 "a postive number");
1143 result = ISC_R_FAILURE;
1144 } else {
1145 /* successful converting TTL, tell Bind */
1146 result = dns_sdlz_putnamedrr(allnodes, host,
1147 type, ttl, data);
1149 } /* closes big if () */
1151 /* clean up mem */
1152 if (ttl_s != NULL)
1153 isc_mem_free(ns_g_mctx, ttl_s);
1154 if (type != NULL)
1155 isc_mem_free(ns_g_mctx, type);
1156 if (host != NULL)
1157 isc_mem_free(ns_g_mctx, host);
1158 if (data != NULL)
1159 isc_mem_free(ns_g_mctx, data);
1161 /* if we weren't successful, log err msg */
1162 if (result != ISC_R_SUCCESS) {
1163 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1164 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1165 "dns_sdlz_putnamedrr returned error. "
1166 "Error code was: %s",
1167 isc_result_totext(result));
1168 result = ISC_R_FAILURE;
1169 goto allnodes_cleanup;
1171 } /* closes while loop */
1173 allnodes_cleanup:
1175 /* close cursor */
1176 SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
1178 #ifdef ISC_PLATFORM_USETHREADS
1180 /* free lock on dbi so someone else can use it. */
1181 isc_mutex_unlock(&dbi->instance_lock);
1183 #endif
1185 return result;
1189 * if the lookup function does not return SOA or NS records for the zone,
1190 * use this function to get that information for Bind.
1193 static isc_result_t
1194 odbc_authority(const char *zone, void *driverarg, void *dbdata,
1195 dns_sdlzlookup_t *lookup)
1197 isc_result_t result;
1198 dbinstance_t *dbi = NULL;
1200 UNUSED(driverarg);
1202 /* run the query and get the result set from the database. */
1203 result = odbc_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &dbi);
1204 /* if we get "not implemented", send it along */
1205 if (result == ISC_R_NOTIMPLEMENTED)
1206 return result;
1207 /* if we didn't get a result set, log an err msg. */
1208 if (result != ISC_R_SUCCESS) {
1209 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1210 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1211 "Odbc driver unable to return "
1212 "result set for authority query");
1213 return (ISC_R_FAILURE);
1215 /* lookup and authority result sets are processed in the same manner */
1216 /* odbc_process_rs does the job for both functions. */
1217 return odbc_process_rs(lookup, dbi);
1220 /*% if zone is supported, lookup up a (or multiple) record(s) in it */
1222 static isc_result_t
1223 odbc_lookup(const char *zone, const char *name, void *driverarg,
1224 void *dbdata, dns_sdlzlookup_t *lookup)
1226 isc_result_t result;
1227 dbinstance_t *dbi = NULL;
1229 UNUSED(driverarg);
1231 /* run the query and get the result set from the database. */
1232 result = odbc_get_resultset(zone, name, NULL, LOOKUP, dbdata, &dbi);
1233 /* if we didn't get a result set, log an err msg. */
1234 if (result != ISC_R_SUCCESS) {
1235 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1236 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1237 "Odbc driver unable to return "
1238 "result set for lookup query");
1239 return (ISC_R_FAILURE);
1241 /* lookup and authority result sets are processed in the same manner */
1242 /* odbc_process_rs does the job for both functions. */
1243 return odbc_process_rs(lookup, dbi);
1247 * create an instance of the driver. Remember, only 1 copy of the driver's
1248 * code is ever loaded, the driver has to remember which context it's
1249 * operating in. This is done via use of the dbdata argument which is
1250 * passed into all query functions.
1252 static isc_result_t
1253 odbc_create(const char *dlzname, unsigned int argc, char *argv[],
1254 void *driverarg, void **dbdata)
1256 isc_result_t result;
1257 odbc_instance_t *odbc_inst = NULL;
1258 dbinstance_t *db = NULL;
1259 SQLRETURN sqlRes;
1261 #ifdef ISC_PLATFORM_USETHREADS
1262 /* if multi-threaded, we need a few extra variables. */
1263 int dbcount;
1264 int i;
1265 char *endp;
1267 #endif /* ISC_PLATFORM_USETHREADS */
1269 UNUSED(dlzname);
1270 UNUSED(driverarg);
1272 #ifdef ISC_PLATFORM_USETHREADS
1273 /* if debugging, let user know we are multithreaded. */
1274 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1275 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1276 "Odbc driver running multithreaded");
1277 #else /* ISC_PLATFORM_USETHREADS */
1278 /* if debugging, let user know we are single threaded. */
1279 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1280 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1281 "Odbc driver running single threaded");
1282 #endif /* ISC_PLATFORM_USETHREADS */
1284 /* verify we have at least 5 arg's passed to the driver */
1285 if (argc < 5) {
1286 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1287 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1288 "Odbc driver requires at least "
1289 "4 command line args.");
1290 return (ISC_R_FAILURE);
1293 /* no more than 8 arg's should be passed to the driver */
1294 if (argc > 8) {
1295 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1296 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1297 "Odbc driver cannot accept more than "
1298 "7 command line args.");
1299 return (ISC_R_FAILURE);
1302 /* multithreaded build can have multiple DB connections */
1303 #ifdef ISC_PLATFORM_USETHREADS
1305 /* check how many db connections we should create */
1306 dbcount = strtol(argv[1], &endp, 10);
1307 if (*endp != '\0' || dbcount < 0) {
1308 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1309 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1310 "Odbc driver database connection count "
1311 "must be positive.");
1312 return (ISC_R_FAILURE);
1315 #endif /* ISC_PLATFORM_USETHREADS */
1317 /* allocate memory for odbc instance */
1318 odbc_inst = isc_mem_get(ns_g_mctx, sizeof(odbc_instance_t));
1319 if (odbc_inst == NULL)
1320 return (ISC_R_NOMEMORY);
1321 memset(odbc_inst, 0, sizeof(odbc_instance_t));
1323 /* parse connection string and get paramters. */
1325 /* get odbc database dsn - required */
1326 odbc_inst->dsn = (SQLCHAR *) getParameterValue(argv[2],
1327 "dsn=");
1328 if (odbc_inst->dsn == NULL) {
1329 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1330 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1331 "odbc driver requires a dns parameter.");
1332 result = ISC_R_FAILURE;
1333 goto cleanup;
1335 /* get odbc database username */
1336 /* if no username was passed, set odbc_inst.user = NULL; */
1337 odbc_inst->user = (SQLCHAR *) getParameterValue(argv[2],
1338 "user=");
1340 /* get odbc database password */
1341 /* if no password was passed, set odbc_inst.pass = NULL; */
1342 odbc_inst->pass = (SQLCHAR *) getParameterValue(argv[2], "pass=");
1344 /* create odbc environment & set environment to ODBC V3 */
1345 if (odbc_inst->sql_env == NULL) {
1346 /* create environment handle */
1347 sqlRes = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,
1348 &(odbc_inst->sql_env));
1349 if (!sqlOK(sqlRes)) {
1350 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1351 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
1352 "Odbc driver unable to allocate memory");
1353 result = ISC_R_NOMEMORY;
1354 goto cleanup;
1356 /*set ODBC version = 3 */
1357 sqlRes = SQLSetEnvAttr(odbc_inst->sql_env,
1358 SQL_ATTR_ODBC_VERSION,
1359 (void *) SQL_OV_ODBC3, 0);
1360 if (!sqlOK(sqlRes)) {
1361 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1362 DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
1363 "Unable to configure ODBC environment");
1364 result = ISC_R_NOMEMORY;
1365 goto cleanup;
1369 #ifdef ISC_PLATFORM_USETHREADS
1371 /* allocate memory for database connection list */
1372 odbc_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1373 if (odbc_inst->db == NULL) {
1374 result = ISC_R_NOMEMORY;
1375 goto cleanup;
1379 /* initialize DB connection list */
1380 ISC_LIST_INIT(*odbc_inst->db);
1382 /* create the appropriate number of database instances (DBI) */
1383 /* append each new DBI to the end of the list */
1384 for (i=0; i < dbcount; i++) {
1386 #endif /* ISC_PLATFORM_USETHREADS */
1388 /* how many queries were passed in from config file? */
1389 switch(argc) {
1390 case 5:
1391 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1392 NULL, argv[3], argv[4],
1393 NULL, &db);
1394 break;
1395 case 6:
1396 result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1397 argv[5], argv[3], argv[4],
1398 NULL, &db);
1399 break;
1400 case 7:
1401 result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
1402 argv[5], argv[3], argv[4],
1403 NULL, &db);
1404 break;
1405 case 8:
1406 result = build_sqldbinstance(ns_g_mctx, argv[6],
1407 argv[7], argv[5], argv[3],
1408 argv[4], NULL, &db);
1409 break;
1410 default:
1411 /* not really needed, should shut up compiler. */
1412 result = ISC_R_FAILURE;
1415 /* unsuccessful?, log err msg and cleanup. */
1416 if (result != ISC_R_SUCCESS) {
1417 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1418 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1419 "Odbc driver could not create "
1420 "database instance object.");
1421 goto cleanup;
1424 #ifdef ISC_PLATFORM_USETHREADS
1426 /* when multithreaded, build a list of DBI's */
1427 ISC_LINK_INIT(db, link);
1428 ISC_LIST_APPEND(*odbc_inst->db, db, link);
1430 #endif
1432 result = odbc_connect(odbc_inst, (odbc_db_t **) &(db->dbconn));
1434 if (result != ISC_R_SUCCESS) {
1436 #ifdef ISC_PLATFORM_USETHREADS
1439 * if multi threaded, let user know which
1440 * connection failed. user could be
1441 * attempting to create 10 db connections and
1442 * for some reason the db backend only allows
1443 * 9.
1445 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1446 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1447 "Odbc driver failed to create database "
1448 "connection number %u after 3 attempts",
1449 i+1);
1450 #else
1451 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1452 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1453 "Odbc driver failed to create database "
1454 "connection after 3 attempts");
1455 #endif
1456 goto cleanup;
1459 #ifdef ISC_PLATFORM_USETHREADS
1461 /* set DB = null for next loop through. */
1462 db = NULL;
1464 } /* end for loop */
1466 #else
1467 /* tell odbc_inst about the db connection we just created. */
1468 odbc_inst->db = db;
1470 #endif
1472 /* set dbdata to the odbc_instance we created. */
1473 *dbdata = odbc_inst;
1475 /* hey, we got through all of that ok, return success. */
1476 return(ISC_R_SUCCESS);
1478 cleanup:
1480 destroy_odbc_instance(odbc_inst);
1482 return result;
1486 * destroy an instance of the driver. Remember, only 1 copy of the driver's
1487 * code is ever loaded, the driver has to remember which context it's
1488 * operating in. This is done via use of the dbdata argument.
1489 * so we really only need to clean it up since we are not using driverarg.
1492 static void
1493 odbc_destroy(void *driverarg, void *dbdata)
1495 UNUSED(driverarg);
1497 destroy_odbc_instance((odbc_instance_t *) dbdata);
1501 /* pointers to all our runtime methods. */
1502 /* this is used during driver registration */
1503 /* i.e. in dlz_odbc_init below. */
1504 static dns_sdlzmethods_t dlz_odbc_methods = {
1505 odbc_create,
1506 odbc_destroy,
1507 odbc_findzone,
1508 odbc_lookup,
1509 odbc_authority,
1510 odbc_allnodes,
1511 odbc_allowzonexfr
1515 * Wrapper around dns_sdlzregister().
1517 isc_result_t
1518 dlz_odbc_init(void) {
1519 isc_result_t result;
1522 * Write debugging message to log
1524 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1525 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1526 "Registering DLZ odbc driver.");
1529 * Driver is always threadsafe. When multithreaded all
1530 * functions use multithreaded code. When not multithreaded,
1531 * all functions can only be entered once, but only 1 thread
1532 * of operation is available in Bind. So everything is still
1533 * threadsafe.
1535 result = dns_sdlzregister("odbc", &dlz_odbc_methods, NULL,
1536 DNS_SDLZFLAG_RELATIVEOWNER |
1537 DNS_SDLZFLAG_RELATIVERDATA |
1538 DNS_SDLZFLAG_THREADSAFE,
1539 ns_g_mctx, &dlz_odbc);
1540 /* if we can't register the driver, there are big problems. */
1541 if (result != ISC_R_SUCCESS) {
1542 UNEXPECTED_ERROR(__FILE__, __LINE__,
1543 "dns_sdlzregister() failed: %s",
1544 isc_result_totext(result));
1545 result = ISC_R_UNEXPECTED;
1549 return result;
1553 * Wrapper around dns_sdlzunregister().
1555 void
1556 dlz_odbc_clear(void) {
1559 * Write debugging message to log
1561 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1562 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1563 "Unregistering DLZ odbc driver.");
1565 /* unregister the driver. */
1566 if (dlz_odbc != NULL)
1567 dns_sdlzunregister(&dlz_odbc);
1570 #endif