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