1 /* $NetBSD: dlz_odbc_driver.c,v 1.5 2014/12/10 04:37:55 christos Exp $ */
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
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
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.
64 #include <dns/result.h>
67 #include <isc/platform.h>
68 #include <isc/print.h>
69 #include <isc/result.h>
70 #include <isc/string.h>
73 #include <named/globals.h>
75 #include <dlz/sdlz_helper.h>
76 #include <dlz/dlz_odbc_driver.h>
82 static dns_sdlzimplementation_t
*dlz_odbc
= NULL
;
84 #define dbc_search_limit 30
91 #define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0)
98 * structure to hold ODBC connection & statement
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
114 #ifdef ISC_PLATFORM_USETHREADS
116 db_list_t
*db
; /* handle to a list of DB */
120 dbinstance_t
*db
; /* handle to db */
124 SQLHENV sql_env
; /* handle to SQL environment */
130 /* forward reference */
133 odbc_makesafe(char *to
, const char *from
, size_t length
);
143 return strlen((char *) a
);
146 /*% propertly cleans up an odbc_instance_t */
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
) {
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
,
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
*)
179 SQLFreeHandle(SQL_HANDLE_DBC
,
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 */
243 odbc_connect(odbc_instance_t
*dbi
, odbc_db_t
**dbc
) {
245 odbc_db_t
*ndb
= *dbc
;
247 isc_result_t result
= ISC_R_SUCCESS
;
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
);
259 /* if connection handle != null free it */
260 if (ndb
->dbc
!= NULL
) {
261 SQLFreeHandle(SQL_HANDLE_DBC
, ndb
->dbc
);
265 ndb
= isc_mem_allocate(ns_g_mctx
, sizeof(odbc_db_t
));
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
;
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
;
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
;
305 return ISC_R_SUCCESS
;
311 /* if statement handle != null free it */
312 if (ndb
->stmnt
!= NULL
) {
313 SQLFreeHandle(SQL_HANDLE_STMT
, ndb
->stmnt
);
317 /* if connection handle != null free it */
318 if (ndb
->dbc
!= NULL
) {
319 SQLDisconnect(ndb
->dbc
);
320 SQLFreeHandle(SQL_HANDLE_DBC
, ndb
->dbc
);
323 /* free memory holding ndb */
324 isc_mem_free(ns_g_mctx
, ndb
);
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
;
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. */
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",
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.
386 odbc_escape_string(const char *instr
) {
396 outstr
= isc_mem_allocate(ns_g_mctx
,(2 * len
* sizeof(char)) + 1);
400 odbc_makesafe(outstr
, instr
, len
);
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.
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
424 odbc_makesafe(char *to
, const char *from
, size_t length
)
426 const char *source
= from
;
428 unsigned int remaining
= length
;
430 while (remaining
> 0)
438 /* target and remaining are updated below. */
445 /* target and remaining are updated below. */
450 /* target and remaining are updated below. */
457 /* Write the terminating NUL character. */
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.
482 odbc_get_resultset(const char *zone
, const char *record
,
483 const char *client
, unsigned int query
,
484 void *dbdata
, dbinstance_t
**r_dbi
)
488 dbinstance_t
*dbi
= NULL
;
489 char *querystring
= NULL
;
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 */
513 result
= ISC_R_FAILURE
;
517 /* what type of query are we going to run? */
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
;
532 /* same as comments as ALLNODES */
533 if (dbi
->allowxfr_q
== NULL
) {
534 result
= ISC_R_NOTIMPLEMENTED
;
539 /* same as comments as ALLNODES */
540 if (dbi
->authority_q
== NULL
) {
541 result
= ISC_R_NOTIMPLEMENTED
;
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
;
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
;
569 * this should never happen. If it does, the code is
572 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
573 "Incorrect query flag passed to "
574 "odbc_get_resultset");
575 result
= ISC_R_UNEXPECTED
;
581 * was a zone string passed? If so, make it safe for use in
585 dbi
->zone
= odbc_escape_string(zone
);
586 if (dbi
->zone
== NULL
) {
587 result
= ISC_R_NOMEMORY
;
590 } else { /* no string passed, set the string pointer to NULL */
595 * was a record string passed? If so, make it safe for use in
598 if (record
!= NULL
) {
599 dbi
->record
= odbc_escape_string(record
);
600 if (dbi
->record
== NULL
) {
601 result
= ISC_R_NOMEMORY
;
604 } else { /* no string passed, set the string pointer to NULL */
609 * was a client string passed? If so, make it safe for use in
612 if (client
!= NULL
) {
613 dbi
->client
= odbc_escape_string(client
);
614 if (dbi
->client
== NULL
) {
615 result
= ISC_R_NOMEMORY
;
618 } else { /* no string passed, set the string pointer to NULL */
623 * what type of query are we going to run?
624 * this time we build the actual query to run.
628 querystring
= build_querystring(ns_g_mctx
, dbi
->allnodes_q
);
631 querystring
= build_querystring(ns_g_mctx
, dbi
->allowxfr_q
);
634 querystring
= build_querystring(ns_g_mctx
, dbi
->authority_q
);
637 querystring
= build_querystring(ns_g_mctx
, dbi
->findzone_q
);
640 querystring
= build_querystring(ns_g_mctx
, dbi
->lookup_q
);
644 * this should never happen. If it does, the code is
647 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
648 "Incorrect query flag passed to "
649 "odbc_get_resultset");
650 result
= ISC_R_UNEXPECTED
;
654 /* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
655 if (querystring
== NULL
) {
656 result
= ISC_R_NOMEMORY
;
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
)) {
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
)
683 /* incase this is the last time through the loop */
684 result
= ISC_R_FAILURE
;
686 result
= ISC_R_SUCCESS
;
689 /* result set ok, break loop */
694 cleanup
: /* it's always good to cleanup after yourself */
696 /* if we couldn't even allocate DBI, just return 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
);
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);
737 odbc_getField(SQLHSTMT
*stmnt
, SQLSMALLINT field
, char **data
) {
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);
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);
764 odbc_getManyFields(SQLHSTMT
*stmnt
, SQLSMALLINT startField
,
765 SQLSMALLINT endField
, char **retData
) {
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 */
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
);
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
))) {
804 result
= ISC_R_SUCCESS
;
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
);
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.
829 odbc_process_rs(dns_sdlzlookup_t
*lookup
, dbinstance_t
*dbi
)
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
;
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
871 if ((result
= odbc_getField(stmnt
, 1,
872 &data
)) == ISC_R_SUCCESS
) {
873 result
= dns_sdlz_putrr(lookup
, "a",
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
,
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
))
899 (result
= odbc_getField(stmnt
, 2, &type
))
901 (result
= odbc_getManyFields(stmnt
, 3,
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
,
912 "Odbc driver ttl must "
913 "be a postive number");
914 result
= ISC_R_FAILURE
;
917 * successful converting TTL,
918 * tell Bind everything
920 result
= dns_sdlz_putrr(lookup
, type
,
923 } /* closes bid if () */
924 } /* closes switch(fields) */
928 isc_mem_free(ns_g_mctx
, ttl_s
);
930 isc_mem_free(ns_g_mctx
, type
);
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 */
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
);
962 * SDLZ interface methods
965 /*% determine if the zone is supported by (in) the database */
968 odbc_findzone(void *driverarg
, void *dbdata
, const char *name
,
969 dns_clientinfomethods_t
*methods
, dns_clientinfo_t
*clientinfo
)
973 dbinstance_t
*dbi
= NULL
;
979 /* run the query and get the result set from the database. */
980 /* if result != ISC_R_SUCCESS cursor and mutex already cleaned up. */
981 /* so we don't have to do it here. */
982 result
= odbc_get_resultset(name
, NULL
, NULL
, FINDZONE
, dbdata
, &dbi
);
984 /* Check that we got a result set with data */
985 if (result
== ISC_R_SUCCESS
&&
986 !sqlOK(SQLFetch(((odbc_db_t
*) (dbi
->dbconn
))->stmnt
))) {
987 result
= ISC_R_NOTFOUND
;
991 /* get rid of result set, we are done with it. */
992 SQLCloseCursor(((odbc_db_t
*) (dbi
->dbconn
))->stmnt
);
994 #ifdef ISC_PLATFORM_USETHREADS
996 /* free lock on dbi so someone else can use it. */
997 isc_mutex_unlock(&dbi
->instance_lock
);
1004 /*% Determine if the client is allowed to perform a zone transfer */
1006 odbc_allowzonexfr(void *driverarg
, void *dbdata
, const char *name
,
1009 isc_result_t result
;
1010 dbinstance_t
*dbi
= NULL
;
1014 /* first check if the zone is supported by the database. */
1015 result
= odbc_findzone(driverarg
, dbdata
, name
, NULL
, NULL
);
1016 if (result
!= ISC_R_SUCCESS
)
1017 return (ISC_R_NOTFOUND
);
1020 * if we get to this point we know the zone is supported by
1021 * the database. the only questions now are is the zone
1022 * transfer is allowed for this client and did the config file
1023 * have an allow zone xfr query
1025 * Run our query, and get a result set from the database. if
1026 * result != ISC_R_SUCCESS cursor and mutex already cleaned
1027 * up, so we don't have to do it here.
1029 result
= odbc_get_resultset(name
, NULL
, client
, ALLOWXFR
,
1032 /* if we get "not implemented", send it along. */
1033 if (result
== ISC_R_NOTIMPLEMENTED
)
1036 /* Check that we got a result set with data */
1037 if (result
== ISC_R_SUCCESS
&&
1038 !sqlOK(SQLFetch(((odbc_db_t
*) (dbi
->dbconn
))->stmnt
))) {
1039 result
= ISC_R_NOPERM
;
1043 /* get rid of result set, we are done with it. */
1044 SQLCloseCursor(((odbc_db_t
*) (dbi
->dbconn
))->stmnt
);
1046 #ifdef ISC_PLATFORM_USETHREADS
1048 /* free lock on dbi so someone else can use it. */
1049 isc_mutex_unlock(&dbi
->instance_lock
);
1058 * If the client is allowed to perform a zone transfer, the next order of
1059 * business is to get all the nodes in the zone, so bind can respond to the
1064 odbc_allnodes(const char *zone
, void *driverarg
, void *dbdata
,
1065 dns_sdlzallnodes_t
*allnodes
)
1068 isc_result_t result
;
1069 dbinstance_t
*dbi
= NULL
;
1081 /* run the query and get the result set from the database. */
1082 result
= odbc_get_resultset(zone
, NULL
, NULL
, ALLNODES
, dbdata
, &dbi
);
1084 /* if we get "not implemented", send it along */
1085 if (result
== ISC_R_NOTIMPLEMENTED
)
1088 /* if we didn't get a result set, log an err msg. */
1089 if (result
!= ISC_R_SUCCESS
) {
1090 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1091 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1092 "Odbc driver unable to return "
1093 "result set for all nodes query");
1094 return (ISC_R_FAILURE
);
1097 stmnt
= ((odbc_db_t
*) (dbi
->dbconn
))->stmnt
;
1099 /* get number of columns */
1100 if (!sqlOK(SQLNumResultCols(stmnt
, &fields
))) {
1101 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1102 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1103 "Odbc driver unable to process result set");
1104 result
= ISC_R_FAILURE
;
1105 goto allnodes_cleanup
;
1108 if (fields
< 4) { /* gotta have at least 4 columns */
1109 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1110 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1111 "Odbc driver too few fields returned by "
1113 result
= ISC_R_FAILURE
;
1114 goto allnodes_cleanup
;
1117 /* get things ready for processing */
1118 result
= ISC_R_FAILURE
;
1120 while (sqlOK(SQLFetch(stmnt
))) {
1122 /* set to null for next pass through */
1123 data
= host
= type
= ttl_s
= NULL
;
1126 * attempt to get DNS ttl, type, host, data then tell
1129 if ((result
= odbc_getField(stmnt
, 1,
1130 &ttl_s
)) == ISC_R_SUCCESS
&&
1131 (result
= odbc_getField(stmnt
, 2,
1132 &type
)) == ISC_R_SUCCESS
&&
1133 (result
= odbc_getField(stmnt
, 3,
1134 &host
)) == ISC_R_SUCCESS
&&
1135 (result
= odbc_getManyFields(stmnt
, 4, fields
,
1136 &data
)) == ISC_R_SUCCESS
) {
1137 /* convert ttl string to int */
1138 ttl
= strtol(ttl_s
, &endp
, 10);
1139 /* failure converting ttl. */
1140 if (*endp
!= '\0' || ttl
< 0) {
1141 isc_log_write(dns_lctx
,
1142 DNS_LOGCATEGORY_DATABASE
,
1143 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1144 "Odbc driver ttl must be "
1145 "a postive number");
1146 result
= ISC_R_FAILURE
;
1148 /* successful converting TTL, tell Bind */
1149 result
= dns_sdlz_putnamedrr(allnodes
, host
,
1152 } /* closes big if () */
1156 isc_mem_free(ns_g_mctx
, ttl_s
);
1158 isc_mem_free(ns_g_mctx
, type
);
1160 isc_mem_free(ns_g_mctx
, host
);
1162 isc_mem_free(ns_g_mctx
, data
);
1164 /* if we weren't successful, log err msg */
1165 if (result
!= ISC_R_SUCCESS
) {
1166 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1167 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1168 "dns_sdlz_putnamedrr returned error. "
1169 "Error code was: %s",
1170 isc_result_totext(result
));
1171 result
= ISC_R_FAILURE
;
1172 goto allnodes_cleanup
;
1174 } /* closes while loop */
1179 SQLCloseCursor(((odbc_db_t
*) (dbi
->dbconn
))->stmnt
);
1181 #ifdef ISC_PLATFORM_USETHREADS
1183 /* free lock on dbi so someone else can use it. */
1184 isc_mutex_unlock(&dbi
->instance_lock
);
1192 * if the lookup function does not return SOA or NS records for the zone,
1193 * use this function to get that information for Bind.
1197 odbc_authority(const char *zone
, void *driverarg
, void *dbdata
,
1198 dns_sdlzlookup_t
*lookup
)
1200 isc_result_t result
;
1201 dbinstance_t
*dbi
= NULL
;
1205 /* run the query and get the result set from the database. */
1206 result
= odbc_get_resultset(zone
, NULL
, NULL
, AUTHORITY
, dbdata
, &dbi
);
1207 /* if we get "not implemented", send it along */
1208 if (result
== ISC_R_NOTIMPLEMENTED
)
1210 /* if we didn't get a result set, log an err msg. */
1211 if (result
!= ISC_R_SUCCESS
) {
1212 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1213 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1214 "Odbc driver unable to return "
1215 "result set for authority query");
1216 return (ISC_R_FAILURE
);
1218 /* lookup and authority result sets are processed in the same manner */
1219 /* odbc_process_rs does the job for both functions. */
1220 return odbc_process_rs(lookup
, dbi
);
1223 /*% if zone is supported, lookup up a (or multiple) record(s) in it */
1226 odbc_lookup(const char *zone
, const char *name
, void *driverarg
,
1227 void *dbdata
, dns_sdlzlookup_t
*lookup
,
1228 dns_clientinfomethods_t
*methods
, dns_clientinfo_t
*clientinfo
)
1230 isc_result_t result
;
1231 dbinstance_t
*dbi
= NULL
;
1237 /* run the query and get the result set from the database. */
1238 result
= odbc_get_resultset(zone
, name
, NULL
, LOOKUP
, dbdata
, &dbi
);
1239 /* if we didn't get a result set, log an err msg. */
1240 if (result
!= ISC_R_SUCCESS
) {
1241 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1242 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1243 "Odbc driver unable to return "
1244 "result set for lookup query");
1245 return (ISC_R_FAILURE
);
1247 /* lookup and authority result sets are processed in the same manner */
1248 /* odbc_process_rs does the job for both functions. */
1249 return odbc_process_rs(lookup
, dbi
);
1253 * create an instance of the driver. Remember, only 1 copy of the driver's
1254 * code is ever loaded, the driver has to remember which context it's
1255 * operating in. This is done via use of the dbdata argument which is
1256 * passed into all query functions.
1259 odbc_create(const char *dlzname
, unsigned int argc
, char *argv
[],
1260 void *driverarg
, void **dbdata
)
1262 isc_result_t result
;
1263 odbc_instance_t
*odbc_inst
= NULL
;
1264 dbinstance_t
*db
= NULL
;
1267 #ifdef ISC_PLATFORM_USETHREADS
1268 /* if multi-threaded, we need a few extra variables. */
1273 #endif /* ISC_PLATFORM_USETHREADS */
1278 #ifdef ISC_PLATFORM_USETHREADS
1279 /* if debugging, let user know we are multithreaded. */
1280 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1281 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(1),
1282 "Odbc driver running multithreaded");
1283 #else /* ISC_PLATFORM_USETHREADS */
1284 /* if debugging, let user know we are single threaded. */
1285 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1286 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(1),
1287 "Odbc driver running single threaded");
1288 #endif /* ISC_PLATFORM_USETHREADS */
1290 /* verify we have at least 5 arg's passed to the driver */
1292 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1293 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1294 "Odbc driver requires at least "
1295 "4 command line args.");
1296 return (ISC_R_FAILURE
);
1299 /* no more than 8 arg's should be passed to the driver */
1301 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1302 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1303 "Odbc driver cannot accept more than "
1304 "7 command line args.");
1305 return (ISC_R_FAILURE
);
1308 /* multithreaded build can have multiple DB connections */
1309 #ifdef ISC_PLATFORM_USETHREADS
1311 /* check how many db connections we should create */
1312 dbcount
= strtol(argv
[1], &endp
, 10);
1313 if (*endp
!= '\0' || dbcount
< 0) {
1314 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1315 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1316 "Odbc driver database connection count "
1317 "must be positive.");
1318 return (ISC_R_FAILURE
);
1321 #endif /* ISC_PLATFORM_USETHREADS */
1323 /* allocate memory for odbc instance */
1324 odbc_inst
= isc_mem_get(ns_g_mctx
, sizeof(odbc_instance_t
));
1325 if (odbc_inst
== NULL
)
1326 return (ISC_R_NOMEMORY
);
1327 memset(odbc_inst
, 0, sizeof(odbc_instance_t
));
1329 /* parse connection string and get paramters. */
1331 /* get odbc database dsn - required */
1332 odbc_inst
->dsn
= (SQLCHAR
*) getParameterValue(argv
[2],
1334 if (odbc_inst
->dsn
== NULL
) {
1335 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1336 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1337 "odbc driver requires a dns parameter.");
1338 result
= ISC_R_FAILURE
;
1341 /* get odbc database username */
1342 /* if no username was passed, set odbc_inst.user = NULL; */
1343 odbc_inst
->user
= (SQLCHAR
*) getParameterValue(argv
[2],
1346 /* get odbc database password */
1347 /* if no password was passed, set odbc_inst.pass = NULL; */
1348 odbc_inst
->pass
= (SQLCHAR
*) getParameterValue(argv
[2], "pass=");
1350 /* create odbc environment & set environment to ODBC V3 */
1351 if (odbc_inst
->sql_env
== NULL
) {
1352 /* create environment handle */
1353 sqlRes
= SQLAllocHandle(SQL_HANDLE_ENV
, SQL_NULL_HANDLE
,
1354 &(odbc_inst
->sql_env
));
1355 if (!sqlOK(sqlRes
)) {
1356 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1357 DNS_LOGMODULE_DLZ
, ISC_LOG_INFO
,
1358 "Odbc driver unable to allocate memory");
1359 result
= ISC_R_NOMEMORY
;
1362 /*set ODBC version = 3 */
1363 sqlRes
= SQLSetEnvAttr(odbc_inst
->sql_env
,
1364 SQL_ATTR_ODBC_VERSION
,
1365 (void *) SQL_OV_ODBC3
, 0);
1366 if (!sqlOK(sqlRes
)) {
1367 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1368 DNS_LOGMODULE_DLZ
, ISC_LOG_INFO
,
1369 "Unable to configure ODBC environment");
1370 result
= ISC_R_NOMEMORY
;
1375 #ifdef ISC_PLATFORM_USETHREADS
1377 /* allocate memory for database connection list */
1378 odbc_inst
->db
= isc_mem_get(ns_g_mctx
, sizeof(db_list_t
));
1379 if (odbc_inst
->db
== NULL
) {
1380 result
= ISC_R_NOMEMORY
;
1385 /* initialize DB connection list */
1386 ISC_LIST_INIT(*odbc_inst
->db
);
1388 /* create the appropriate number of database instances (DBI) */
1389 /* append each new DBI to the end of the list */
1390 for (i
=0; i
< dbcount
; i
++) {
1392 #endif /* ISC_PLATFORM_USETHREADS */
1394 /* how many queries were passed in from config file? */
1397 result
= build_sqldbinstance(ns_g_mctx
, NULL
, NULL
,
1398 NULL
, argv
[3], argv
[4],
1402 result
= build_sqldbinstance(ns_g_mctx
, NULL
, NULL
,
1403 argv
[5], argv
[3], argv
[4],
1407 result
= build_sqldbinstance(ns_g_mctx
, argv
[6], NULL
,
1408 argv
[5], argv
[3], argv
[4],
1412 result
= build_sqldbinstance(ns_g_mctx
, argv
[6],
1413 argv
[7], argv
[5], argv
[3],
1414 argv
[4], NULL
, &db
);
1417 /* not really needed, should shut up compiler. */
1418 result
= ISC_R_FAILURE
;
1421 /* unsuccessful?, log err msg and cleanup. */
1422 if (result
!= ISC_R_SUCCESS
) {
1423 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1424 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1425 "Odbc driver could not create "
1426 "database instance object.");
1430 #ifdef ISC_PLATFORM_USETHREADS
1432 /* when multithreaded, build a list of DBI's */
1433 ISC_LINK_INIT(db
, link
);
1434 ISC_LIST_APPEND(*odbc_inst
->db
, db
, link
);
1438 result
= odbc_connect(odbc_inst
, (odbc_db_t
**) &(db
->dbconn
));
1440 if (result
!= ISC_R_SUCCESS
) {
1442 #ifdef ISC_PLATFORM_USETHREADS
1445 * if multi threaded, let user know which
1446 * connection failed. user could be
1447 * attempting to create 10 db connections and
1448 * for some reason the db backend only allows
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 number %u after 3 attempts",
1457 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1458 DNS_LOGMODULE_DLZ
, ISC_LOG_ERROR
,
1459 "Odbc driver failed to create database "
1460 "connection after 3 attempts");
1465 #ifdef ISC_PLATFORM_USETHREADS
1467 /* set DB = null for next loop through. */
1470 } /* end for loop */
1473 /* tell odbc_inst about the db connection we just created. */
1478 /* set dbdata to the odbc_instance we created. */
1479 *dbdata
= odbc_inst
;
1481 /* hey, we got through all of that ok, return success. */
1482 return(ISC_R_SUCCESS
);
1486 destroy_odbc_instance(odbc_inst
);
1492 * destroy an instance of the driver. Remember, only 1 copy of the driver's
1493 * code is ever loaded, the driver has to remember which context it's
1494 * operating in. This is done via use of the dbdata argument.
1495 * so we really only need to clean it up since we are not using driverarg.
1499 odbc_destroy(void *driverarg
, void *dbdata
)
1503 destroy_odbc_instance((odbc_instance_t
*) dbdata
);
1507 /* pointers to all our runtime methods. */
1508 /* this is used during driver registration */
1509 /* i.e. in dlz_odbc_init below. */
1510 static dns_sdlzmethods_t dlz_odbc_methods
= {
1528 * Wrapper around dns_sdlzregister().
1531 dlz_odbc_init(void) {
1532 isc_result_t result
;
1535 * Write debugging message to log
1537 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1538 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
1539 "Registering DLZ odbc driver.");
1542 * Driver is always threadsafe. When multithreaded all
1543 * functions use multithreaded code. When not multithreaded,
1544 * all functions can only be entered once, but only 1 thread
1545 * of operation is available in Bind. So everything is still
1548 result
= dns_sdlzregister("odbc", &dlz_odbc_methods
, NULL
,
1549 DNS_SDLZFLAG_RELATIVEOWNER
|
1550 DNS_SDLZFLAG_RELATIVERDATA
|
1551 DNS_SDLZFLAG_THREADSAFE
,
1552 ns_g_mctx
, &dlz_odbc
);
1553 /* if we can't register the driver, there are big problems. */
1554 if (result
!= ISC_R_SUCCESS
) {
1555 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
1556 "dns_sdlzregister() failed: %s",
1557 isc_result_totext(result
));
1558 result
= ISC_R_UNEXPECTED
;
1566 * Wrapper around dns_sdlzunregister().
1569 dlz_odbc_clear(void) {
1572 * Write debugging message to log
1574 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1575 DNS_LOGMODULE_DLZ
, ISC_LOG_DEBUG(2),
1576 "Unregistering DLZ odbc driver.");
1578 /* unregister the driver. */
1579 if (dlz_odbc
!= NULL
)
1580 dns_sdlzunregister(&dlz_odbc
);