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