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