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