Blame contrib/dlz/drivers/dlz_postgres_driver.c

Packit Service ae04f2
/*
Packit Service ae04f2
 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
Packit Service ae04f2
 *
Packit Service ae04f2
 * Permission to use, copy, modify, and distribute this software for any
Packit Service ae04f2
 * purpose with or without fee is hereby granted, provided that the
Packit Service ae04f2
 * above copyright notice and this permission notice appear in all
Packit Service ae04f2
 * copies.
Packit Service ae04f2
 *
Packit Service ae04f2
 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
Packit Service ae04f2
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
Packit Service ae04f2
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
Packit Service ae04f2
 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
Packit Service ae04f2
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
Packit Service ae04f2
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Packit Service ae04f2
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
Packit Service ae04f2
 * USE OR PERFORMANCE OF THIS SOFTWARE.
Packit Service ae04f2
 *
Packit Service ae04f2
 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
Packit Service ae04f2
 * conceived and contributed by Rob Butler.
Packit Service ae04f2
 *
Packit Service ae04f2
 * Permission to use, copy, modify, and distribute this software for any
Packit Service ae04f2
 * purpose with or without fee is hereby granted, provided that the
Packit Service ae04f2
 * above copyright notice and this permission notice appear in all
Packit Service ae04f2
 * copies.
Packit Service ae04f2
 *
Packit Service ae04f2
 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
Packit Service ae04f2
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
Packit Service ae04f2
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
Packit Service ae04f2
 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
Packit Service ae04f2
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
Packit Service ae04f2
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Packit Service ae04f2
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
Packit Service ae04f2
 * USE OR PERFORMANCE OF THIS SOFTWARE.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
/*
Packit Service ae04f2
 * Copyright (C) 1999-2001, 2016  Internet Systems Consortium, Inc. ("ISC")
Packit Service ae04f2
 *
Packit Service ae04f2
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit Service ae04f2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit Service ae04f2
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
#ifdef DLZ_POSTGRES
Packit Service ae04f2
Packit Service ae04f2
#include <config.h>
Packit Service ae04f2
#include <stdio.h>
Packit Service ae04f2
#include <string.h>
Packit Service ae04f2
#include <stdlib.h>
Packit Service ae04f2
Packit Service ae04f2
#include <dns/log.h>
Packit Service ae04f2
#include <dns/sdlz.h>
Packit Service ae04f2
#include <dns/result.h>
Packit Service ae04f2
Packit Service ae04f2
#include <isc/mem.h>
Packit Service ae04f2
#include <isc/platform.h>
Packit Service ae04f2
#include <isc/print.h>
Packit Service ae04f2
#include <isc/result.h>
Packit Service ae04f2
#include <isc/string.h>
Packit Service ae04f2
#include <isc/util.h>
Packit Service ae04f2
Packit Service ae04f2
#include <named/globals.h>
Packit Service ae04f2
Packit Service ae04f2
#include <dlz/sdlz_helper.h>
Packit Service ae04f2
#include <dlz/dlz_postgres_driver.h>
Packit Service ae04f2
Packit Service ae04f2
/* temporarily include time. */
Packit Service ae04f2
#include <time.h>
Packit Service ae04f2
Packit Service ae04f2
#include <libpq-fe.h>
Packit Service ae04f2
Packit Service ae04f2
static dns_sdlzimplementation_t *dlz_postgres = NULL;
Packit Service ae04f2
Packit Service ae04f2
#define dbc_search_limit 30
Packit Service ae04f2
#define ALLNODES 1
Packit Service ae04f2
#define ALLOWXFR 2
Packit Service ae04f2
#define AUTHORITY 3
Packit Service ae04f2
#define FINDZONE 4
Packit Service ae04f2
#define LOOKUP 5
Packit Service ae04f2
Packit Service ae04f2
/*
Packit Service ae04f2
 * Private methods
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
/* ---------------
Packit Service ae04f2
 * Escaping arbitrary strings to get valid SQL strings/identifiers.
Packit Service ae04f2
 *
Packit Service ae04f2
 * Replaces "\\" with "\\\\" and "'" with "''".
Packit Service ae04f2
 * length is the length of the buffer pointed to by
Packit Service ae04f2
 * from.  The buffer at to must be at least 2*length + 1 characters
Packit Service ae04f2
 * long.  A terminating NUL character is written.
Packit Service ae04f2
 *
Packit Service ae04f2
 * NOTICE!!!
Packit Service ae04f2
 * This function was borrowed directly from PostgreSQL's libpq.
Packit Service ae04f2
 * The function was originally called PQescapeString and renamed
Packit Service ae04f2
 * to postgres_makesafe to avoid a naming collision.
Packit Service ae04f2
 * PQescapeString is a new function made available in Postgres 7.2.
Packit Service ae04f2
 * For some reason the function is not properly exported on Win32
Packit Service ae04f2
 * builds making the function unavailable on Windows.  Also, since
Packit Service ae04f2
 * this function is new it would require building this driver with
Packit Service ae04f2
 * the libpq 7.2.  By borrowing this function the Windows problem
Packit Service ae04f2
 * is solved, and the dependence on libpq 7.2 is removed.  Libpq is
Packit Service ae04f2
 * still required of course, but an older version should work now too.
Packit Service ae04f2
 *
Packit Service ae04f2
 * The copyright statements from the original file containing this
Packit Service ae04f2
 * function are included below:
Packit Service ae04f2
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
Packit Service ae04f2
 * Portions Copyright (c) 1994, Regents of the University of California
Packit Service ae04f2
 * ---------------
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
static size_t
Packit Service ae04f2
postgres_makesafe(char *to, const char *from, size_t length)
Packit Service ae04f2
{
Packit Service ae04f2
	const char *source = from;
Packit Service ae04f2
	char	   *target = to;
Packit Service ae04f2
	unsigned int remaining = length;
Packit Service ae04f2
Packit Service ae04f2
	while (remaining > 0)
Packit Service ae04f2
	{
Packit Service ae04f2
		switch (*source)
Packit Service ae04f2
		{
Packit Service ae04f2
		case '\\':
Packit Service ae04f2
			*target = '\\';
Packit Service ae04f2
			target++;
Packit Service ae04f2
			*target = '\\';
Packit Service ae04f2
			/* target and remaining are updated below. */
Packit Service ae04f2
			break;
Packit Service ae04f2
Packit Service ae04f2
		case '\'':
Packit Service ae04f2
			*target = '\'';
Packit Service ae04f2
			target++;
Packit Service ae04f2
			*target = '\'';
Packit Service ae04f2
			/* target and remaining are updated below. */
Packit Service ae04f2
			break;
Packit Service ae04f2
Packit Service ae04f2
		default:
Packit Service ae04f2
			*target = *source;
Packit Service ae04f2
			/* target and remaining are updated below. */
Packit Service ae04f2
		}
Packit Service ae04f2
		source++;
Packit Service ae04f2
		target++;
Packit Service ae04f2
		remaining--;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* Write the terminating NUL character. */
Packit Service ae04f2
	*target = '\0';
Packit Service ae04f2
Packit Service ae04f2
	return target - to;
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * Properly cleans up a list of database instances.
Packit Service ae04f2
 * This function is only used when the driver is compiled for
Packit Service ae04f2
 * multithreaded operation.
Packit Service ae04f2
 */
Packit Service ae04f2
static void
Packit Service ae04f2
postgres_destroy_dblist(db_list_t *dblist)
Packit Service ae04f2
{
Packit Service ae04f2
Packit Service ae04f2
	dbinstance_t *ndbi = NULL;
Packit Service ae04f2
	dbinstance_t *dbi = NULL;
Packit Service ae04f2
Packit Service ae04f2
	/* get the first DBI in the list */
Packit Service ae04f2
	ndbi = ISC_LIST_HEAD(*dblist);
Packit Service ae04f2
Packit Service ae04f2
	/* loop through the list */
Packit Service ae04f2
	while (ndbi != NULL) {
Packit Service ae04f2
		dbi = ndbi;
Packit Service ae04f2
		/* get the next DBI in the list */
Packit Service ae04f2
		ndbi = ISC_LIST_NEXT(dbi, link);
Packit Service ae04f2
		/* release DB connection */
Packit Service ae04f2
		if (dbi->dbconn != NULL)
Packit Service ae04f2
			PQfinish((PGconn *) dbi->dbconn);
Packit Service ae04f2
		/* release all memory that comprised a DBI */
Packit Service ae04f2
		destroy_sqldbinstance(dbi);
Packit Service ae04f2
	}
Packit Service ae04f2
	/* release memory for the list structure */
Packit Service ae04f2
	isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * Loops through the list of DB instances, attempting to lock
Packit Service ae04f2
 * on the mutex.  If successful, the DBI is reserved for use
Packit Service ae04f2
 * and the thread can perform queries against the database.
Packit Service ae04f2
 * If the lock fails, the next one in the list is tried.
Packit Service ae04f2
 * looping continues until a lock is obtained, or until
Packit Service ae04f2
 * the list has been searched dbc_search_limit times.
Packit Service ae04f2
 * This function is only used when the driver is compiled for
Packit Service ae04f2
 * multithreaded operation.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
static dbinstance_t *
Packit Service ae04f2
postgres_find_avail_conn(db_list_t *dblist)
Packit Service ae04f2
{
Packit Service ae04f2
	dbinstance_t *dbi = NULL;
Packit Service ae04f2
	dbinstance_t *head;
Packit Service ae04f2
	int count = 0;
Packit Service ae04f2
Packit Service ae04f2
	/* get top of list */
Packit Service ae04f2
	head = dbi = ISC_LIST_HEAD(*dblist);
Packit Service ae04f2
Packit Service ae04f2
	/* loop through list */
Packit Service ae04f2
	while (count < dbc_search_limit) {
Packit Service ae04f2
		/* try to lock on the mutex */
Packit Service ae04f2
		if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
Packit Service ae04f2
			return dbi; /* success, return the DBI for use. */
Packit Service ae04f2
Packit Service ae04f2
		/* not successful, keep trying */
Packit Service ae04f2
		dbi = ISC_LIST_NEXT(dbi, link);
Packit Service ae04f2
Packit Service ae04f2
		/* check to see if we have gone to the top of the list. */
Packit Service ae04f2
		if (dbi == NULL) {
Packit Service ae04f2
			count++;
Packit Service ae04f2
			dbi = head;
Packit Service ae04f2
		}
Packit Service ae04f2
	}
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
Packit Service ae04f2
		      "Postgres driver unable to find available connection "
Packit Service ae04f2
		      "after searching %d times",
Packit Service ae04f2
		      count);
Packit Service ae04f2
	return NULL;
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * Allocates memory for a new string, and then constructs the new
Packit Service ae04f2
 * string by "escaping" the input string.  The new string is
Packit Service ae04f2
 * safe to be used in queries.  This is necessary because we cannot
Packit Service ae04f2
 * be sure of what types of strings are passed to us, and we don't
Packit Service ae04f2
 * want special characters in the string causing problems.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
static char *
Packit Service ae04f2
postgres_escape_string(const char *instr) {
Packit Service ae04f2
Packit Service ae04f2
	char *outstr;
Packit Service ae04f2
	unsigned int len;
Packit Service ae04f2
Packit Service ae04f2
	if (instr == NULL)
Packit Service ae04f2
		return NULL;
Packit Service ae04f2
Packit Service ae04f2
	len = strlen(instr);
Packit Service ae04f2
Packit Service ae04f2
	outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
Packit Service ae04f2
	if (outstr == NULL)
Packit Service ae04f2
		return NULL;
Packit Service ae04f2
Packit Service ae04f2
	postgres_makesafe(outstr, instr, len);
Packit Service ae04f2
	/* PQescapeString(outstr, instr, len); */
Packit Service ae04f2
Packit Service ae04f2
	return outstr;
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * This function is the real core of the driver.   Zone, record
Packit Service ae04f2
 * and client strings are passed in (or NULL is passed if the
Packit Service ae04f2
 * string is not available).  The type of query we want to run
Packit Service ae04f2
 * is indicated by the query flag, and the dbdata object is passed
Packit Service ae04f2
 * passed in to.  dbdata really holds either:
Packit Service ae04f2
 *		1) a list of database instances (in multithreaded mode) OR
Packit Service ae04f2
 *		2) a single database instance (in single threaded mode)
Packit Service ae04f2
 * The function will construct the query and obtain an available
Packit Service ae04f2
 * database instance (DBI).  It will then run the query and hopefully
Packit Service ae04f2
 * obtain a result set.  Postgres is nice, in that once the result
Packit Service ae04f2
 * set is returned, we can make the db connection available for another
Packit Service ae04f2
 * thread to use, while this thread continues on.  So, the DBI is made
Packit Service ae04f2
 * available ASAP by unlocking the instance_lock after we have cleaned
Packit Service ae04f2
 * it up properly.
Packit Service ae04f2
 */
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_get_resultset(const char *zone, const char *record,
Packit Service ae04f2
		       const char *client, unsigned int query,
Packit Service ae04f2
		       void *dbdata, PGresult **rs)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	dbinstance_t *dbi = NULL;
Packit Service ae04f2
	char *querystring = NULL;
Packit Service ae04f2
	unsigned int i = 0;
Packit Service ae04f2
	unsigned int j = 0;
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporarily get a unique thread # */
Packit Service ae04f2
	unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	REQUIRE(*rs == NULL);
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d Getting DBI", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/* get db instance / connection */
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
	/* find an available DBI from the list */
Packit Service ae04f2
	dbi = postgres_find_avail_conn((db_list_t *) dbdata);
Packit Service ae04f2
Packit Service ae04f2
#else /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * only 1 DBI - no need to lock instance lock either
Packit Service ae04f2
	 * only 1 thread in the whole process, no possible contention.
Packit Service ae04f2
	 */
Packit Service ae04f2
	dbi =  (dbinstance_t *) dbdata;
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d Got DBI - checking query", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/* if DBI is null, can't do anything else */
Packit Service ae04f2
	if (dbi == NULL) {
Packit Service ae04f2
		result = ISC_R_FAILURE;
Packit Service ae04f2
		goto cleanup;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* what type of query are we going to run? */
Packit Service ae04f2
	switch(query) {
Packit Service ae04f2
	case ALLNODES:
Packit Service ae04f2
		/*
Packit Service ae04f2
		 * if the query was not passed in from the config file
Packit Service ae04f2
		 * then we can't run it.  return not_implemented, so
Packit Service ae04f2
		 * it's like the code for that operation was never
Packit Service ae04f2
		 * built into the driver.... AHHH flexibility!!!
Packit Service ae04f2
		 */
Packit Service ae04f2
		if (dbi->allnodes_q == NULL) {
Packit Service ae04f2
			result = ISC_R_NOTIMPLEMENTED;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
		break;
Packit Service ae04f2
	case ALLOWXFR:
Packit Service ae04f2
		/* same as comments as ALLNODES */
Packit Service ae04f2
		if (dbi->allowxfr_q == NULL) {
Packit Service ae04f2
			result = ISC_R_NOTIMPLEMENTED;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
		break;
Packit Service ae04f2
	case AUTHORITY:
Packit Service ae04f2
		/* same as comments as ALLNODES */
Packit Service ae04f2
		if (dbi->authority_q == NULL) {
Packit Service ae04f2
			result = ISC_R_NOTIMPLEMENTED;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
		break;
Packit Service ae04f2
	case FINDZONE:
Packit Service ae04f2
		/* this is required.  It's the whole point of DLZ! */
Packit Service ae04f2
		if (dbi->findzone_q == NULL) {
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
Packit Service ae04f2
				      "No query specified for findzone.  "
Packit Service ae04f2
				      "Findzone requires a query");
Packit Service ae04f2
			result = ISC_R_FAILURE;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
		break;
Packit Service ae04f2
	case LOOKUP:
Packit Service ae04f2
		/* this is required.  It's also a major point of DLZ! */
Packit Service ae04f2
		if (dbi->lookup_q == NULL) {
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
Packit Service ae04f2
				      "No query specified for lookup.  "
Packit Service ae04f2
				      "Lookup requires a query");
Packit Service ae04f2
			result = ISC_R_FAILURE;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
		break;
Packit Service ae04f2
	default:
Packit Service ae04f2
		/*
Packit Service ae04f2
		 * this should never happen.  If it does, the code is
Packit Service ae04f2
		 * screwed up!
Packit Service ae04f2
		 */
Packit Service ae04f2
		UNEXPECTED_ERROR(__FILE__, __LINE__,
Packit Service ae04f2
				 "Incorrect query flag passed to "
Packit Service ae04f2
				 "postgres_get_resultset");
Packit Service ae04f2
		result = ISC_R_UNEXPECTED;
Packit Service ae04f2
		goto cleanup;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d checked query", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * was a zone string passed?  If so, make it safe for use in
Packit Service ae04f2
	 * queries.
Packit Service ae04f2
	 */
Packit Service ae04f2
	if (zone != NULL) {
Packit Service ae04f2
		dbi->zone = postgres_escape_string(zone);
Packit Service ae04f2
		if (dbi->zone == NULL) {
Packit Service ae04f2
			result = ISC_R_NOMEMORY;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
	} else {	/* no string passed, set the string pointer to NULL */
Packit Service ae04f2
		dbi->zone = NULL;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d did zone", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * was a record string passed?  If so, make it safe for use in
Packit Service ae04f2
	 * queries.
Packit Service ae04f2
	 */
Packit Service ae04f2
	if (record != NULL) {
Packit Service ae04f2
		dbi->record = postgres_escape_string(record);
Packit Service ae04f2
		if (dbi->record == NULL) {
Packit Service ae04f2
			result = ISC_R_NOMEMORY;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
	} else {	/* no string passed, set the string pointer to NULL */
Packit Service ae04f2
		dbi->record = NULL;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d did record", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * was a client string passed?  If so, make it safe for use in
Packit Service ae04f2
	 * queries.
Packit Service ae04f2
	 */
Packit Service ae04f2
	if (client != NULL) {
Packit Service ae04f2
		dbi->client = postgres_escape_string(client);
Packit Service ae04f2
		if (dbi->client == NULL) {
Packit Service ae04f2
			result = ISC_R_NOMEMORY;
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
	} else {	/* no string passed, set the string pointer to NULL */
Packit Service ae04f2
		dbi->client = NULL;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
	DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d did client", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * what type of query are we going to run?
Packit Service ae04f2
	 * this time we build the actual query to run.
Packit Service ae04f2
	 */
Packit Service ae04f2
	switch(query) {
Packit Service ae04f2
	case ALLNODES:
Packit Service ae04f2
		querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
Packit Service ae04f2
		break;
Packit Service ae04f2
	case ALLOWXFR:
Packit Service ae04f2
		querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
Packit Service ae04f2
		break;
Packit Service ae04f2
	case AUTHORITY:
Packit Service ae04f2
		querystring = build_querystring(ns_g_mctx, dbi->authority_q);
Packit Service ae04f2
		break;
Packit Service ae04f2
	case FINDZONE:
Packit Service ae04f2
		querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
Packit Service ae04f2
		break;
Packit Service ae04f2
	case LOOKUP:
Packit Service ae04f2
		querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
Packit Service ae04f2
		break;
Packit Service ae04f2
	default:
Packit Service ae04f2
		/*
Packit Service ae04f2
		 * this should never happen.  If it does, the code is
Packit Service ae04f2
		 * screwed up!
Packit Service ae04f2
		 */
Packit Service ae04f2
		UNEXPECTED_ERROR(__FILE__, __LINE__,
Packit Service ae04f2
				 "Incorrect query flag passed to "
Packit Service ae04f2
				 "postgres_get_resultset");
Packit Service ae04f2
		result = ISC_R_UNEXPECTED;
Packit Service ae04f2
		goto cleanup;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d built query", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
Packit Service ae04f2
	if (querystring  == NULL) {
Packit Service ae04f2
		result = ISC_R_NOMEMORY;
Packit Service ae04f2
		goto cleanup;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d query is '%s'", dlz_thread_num, querystring);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * output the full query string during debug so we can see
Packit Service ae04f2
	 * what lame error the query has.
Packit Service ae04f2
	 */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
Packit Service ae04f2
		      "\nQuery String: %s\n", querystring);
Packit Service ae04f2
Packit Service ae04f2
	/* attempt query up to 3 times. */
Packit Service ae04f2
	for (j=0; j < 3; j++) {
Packit Service ae04f2
#if 0
Packit Service ae04f2
		/* temporary logging message */
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "%d executing query for %d time",
Packit Service ae04f2
			      dlz_thread_num, j);
Packit Service ae04f2
#endif
Packit Service ae04f2
		/* try to get result set */
Packit Service ae04f2
		*rs = PQexec((PGconn *)dbi->dbconn, querystring );
Packit Service ae04f2
		result = ISC_R_SUCCESS;
Packit Service ae04f2
		/*
Packit Service ae04f2
		 * if result set is null, reset DB connection, max 3
Packit Service ae04f2
		 * attempts.
Packit Service ae04f2
		 */
Packit Service ae04f2
		for (i=0; *rs == NULL && i < 3; i++) {
Packit Service ae04f2
#if 0
Packit Service ae04f2
			/* temporary logging message */
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "%d resetting connection",
Packit Service ae04f2
				      dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
			result = ISC_R_FAILURE;
Packit Service ae04f2
			PQreset((PGconn *) dbi->dbconn);
Packit Service ae04f2
			/* connection ok, break inner loop */
Packit Service ae04f2
			if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK)
Packit Service ae04f2
				break;
Packit Service ae04f2
		}
Packit Service ae04f2
		/* result set ok, break outer loop */
Packit Service ae04f2
		if (PQresultStatus(*rs) == PGRES_TUPLES_OK) {
Packit Service ae04f2
#if 0
Packit Service ae04f2
			/* temporary logging message */
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "%d rs ok", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
			break;
Packit Service ae04f2
		} else {
Packit Service ae04f2
			/* we got a result set object, but it's not right. */
Packit Service ae04f2
#if 0
Packit Service ae04f2
			/* temporary logging message */
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "%d clearing rs", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
			PQclear(*rs);	/* get rid of it */
Packit Service ae04f2
			/* in case this was the last attempt */
Packit Service ae04f2
			*rs = NULL;
Packit Service ae04f2
			result = ISC_R_FAILURE;
Packit Service ae04f2
		}
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
 cleanup:
Packit Service ae04f2
	/* it's always good to cleanup after yourself */
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d cleaning up", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/* if we couldn't even allocate DBI, just return NULL */
Packit Service ae04f2
	if (dbi == NULL)
Packit Service ae04f2
		return ISC_R_FAILURE;
Packit Service ae04f2
Packit Service ae04f2
	/* free dbi->zone string */
Packit Service ae04f2
	if (dbi->zone != NULL)
Packit Service ae04f2
		isc_mem_free(ns_g_mctx, dbi->zone);
Packit Service ae04f2
Packit Service ae04f2
	/* free dbi->record string */
Packit Service ae04f2
	if (dbi->record != NULL)
Packit Service ae04f2
		isc_mem_free(ns_g_mctx, dbi->record);
Packit Service ae04f2
Packit Service ae04f2
	/* free dbi->client string */
Packit Service ae04f2
	if (dbi->client != NULL)
Packit Service ae04f2
		isc_mem_free(ns_g_mctx, dbi->client);
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d unlocking mutex", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/* release the lock so another thread can use this dbi */
Packit Service ae04f2
	isc_mutex_unlock(&dbi->instance_lock);
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
	/* release query string */
Packit Service ae04f2
	if (querystring  != NULL)
Packit Service ae04f2
		isc_mem_free(ns_g_mctx, querystring );
Packit Service ae04f2
Packit Service ae04f2
#if 0
Packit Service ae04f2
	/* temporary logging message */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
		      "%d returning", dlz_thread_num);
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	/* return result */
Packit Service ae04f2
	return result;
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * The processing of result sets for lookup and authority are
Packit Service ae04f2
 * exactly the same.  So that functionality has been moved
Packit Service ae04f2
 * into this function to minimize code.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	unsigned int i;
Packit Service ae04f2
	unsigned int rows;
Packit Service ae04f2
	unsigned int fields;
Packit Service ae04f2
	unsigned int j;
Packit Service ae04f2
	unsigned int len;
Packit Service ae04f2
	char *tmpString;
Packit Service ae04f2
	char *endp;
Packit Service ae04f2
	int ttl;
Packit Service ae04f2
Packit Service ae04f2
	rows = PQntuples(rs);	/* how many rows in result set */
Packit Service ae04f2
	fields = PQnfields(rs);	/* how many columns in result set */
Packit Service ae04f2
	for (i=0; i < rows; i++) {
Packit Service ae04f2
		switch(fields) {
Packit Service ae04f2
		case 1:
Packit Service ae04f2
			/*
Packit Service ae04f2
			 * one column in rs, it's the data field.  use
Packit Service ae04f2
			 * default type of A record, and default TTL
Packit Service ae04f2
			 * of 86400
Packit Service ae04f2
			 */
Packit Service ae04f2
			result = dns_sdlz_putrr(lookup, "a", 86400,
Packit Service ae04f2
						PQgetvalue(rs, i, 0));
Packit Service ae04f2
			break;
Packit Service ae04f2
		case 2:
Packit Service ae04f2
			/* two columns, data field, and data type.
Packit Service ae04f2
			 * use default TTL of 86400.
Packit Service ae04f2
			 */
Packit Service ae04f2
			result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0),
Packit Service ae04f2
						86400, PQgetvalue(rs, i, 1));
Packit Service ae04f2
			break;
Packit Service ae04f2
		case 3:
Packit Service ae04f2
			/* three columns, all data no defaults.
Packit Service ae04f2
			 * convert text to int, make sure it worked
Packit Service ae04f2
			 * right.
Packit Service ae04f2
			 */
Packit Service ae04f2
			ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
Packit Service ae04f2
			if (*endp != '\0' || ttl < 0) {
Packit Service ae04f2
				isc_log_write(dns_lctx,
Packit Service ae04f2
					      DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
					      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
					      "Postgres driver ttl must be "
Packit Service ae04f2
					      "a positive number");
Packit Service ae04f2
			}
Packit Service ae04f2
			result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
Packit Service ae04f2
						ttl, PQgetvalue(rs, i, 2));
Packit Service ae04f2
			break;
Packit Service ae04f2
		default:
Packit Service ae04f2
		  	/*
Packit Service ae04f2
			 * more than 3 fields, concatenate the last
Packit Service ae04f2
			 * ones together.  figure out how long to make
Packit Service ae04f2
			 * string
Packit Service ae04f2
			 */
Packit Service ae04f2
			for (j=2, len=0; j < fields; j++) {
Packit Service ae04f2
				len += strlen(PQgetvalue(rs, i, j)) + 1;
Packit Service ae04f2
			}
Packit Service ae04f2
			/*
Packit Service ae04f2
			 * allocate string memory, allow for NULL to
Packit Service ae04f2
			 * term string
Packit Service ae04f2
			 */
Packit Service ae04f2
			tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
Packit Service ae04f2
			if (tmpString == NULL) {
Packit Service ae04f2
				/* major bummer, need more ram */
Packit Service ae04f2
				isc_log_write(dns_lctx,
Packit Service ae04f2
					      DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
					      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
					      "Postgres driver unable to "
Packit Service ae04f2
					      "allocate memory for "
Packit Service ae04f2
					      "temporary string");
Packit Service ae04f2
				PQclear(rs);
Packit Service ae04f2
				return (ISC_R_FAILURE);	/* Yeah, I'd say! */
Packit Service ae04f2
			}
Packit Service ae04f2
			/* copy field to tmpString */
Packit Service ae04f2
			strcpy(tmpString, PQgetvalue(rs, i, 2));
Packit Service ae04f2
			/*
Packit Service ae04f2
			 * concat the rest of fields together, space
Packit Service ae04f2
			 * between each one.
Packit Service ae04f2
			 */
Packit Service ae04f2
			for (j=3; j < fields; j++) {
Packit Service ae04f2
				strcat(tmpString, " ");
Packit Service ae04f2
				strcat(tmpString, PQgetvalue(rs, i, j));
Packit Service ae04f2
			}
Packit Service ae04f2
			/* convert text to int, make sure it worked right */
Packit Service ae04f2
			ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
Packit Service ae04f2
			if (*endp != '\0' || ttl < 0) {
Packit Service ae04f2
				isc_log_write(dns_lctx,
Packit Service ae04f2
					      DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
					      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
					      "Postgres driver ttl must be "
Packit Service ae04f2
					      "a positive number");
Packit Service ae04f2
			}
Packit Service ae04f2
			/* ok, now tell Bind about it. */
Packit Service ae04f2
			result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
Packit Service ae04f2
						ttl, tmpString);
Packit Service ae04f2
			/* done, get rid of this thing. */
Packit Service ae04f2
			isc_mem_free(ns_g_mctx, tmpString);
Packit Service ae04f2
		}
Packit Service ae04f2
		/* I sure hope we were successful */
Packit Service ae04f2
		if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
			/* nope, get rid of the Result set, and log a msg */
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "dns_sdlz_putrr returned error. "
Packit Service ae04f2
				      "Error code was: %s",
Packit Service ae04f2
				      isc_result_totext(result));
Packit Service ae04f2
			return (ISC_R_FAILURE);
Packit Service ae04f2
		}
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* free result set memory */
Packit Service ae04f2
	PQclear(rs);
Packit Service ae04f2
Packit Service ae04f2
	/* if we did return results, we are successful */
Packit Service ae04f2
	if (rows > 0)
Packit Service ae04f2
		return (ISC_R_SUCCESS);
Packit Service ae04f2
Packit Service ae04f2
	/* empty result set, no data found */
Packit Service ae04f2
	return (ISC_R_NOTFOUND);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*
Packit Service ae04f2
 * SDLZ interface methods
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
/*% determine if the zone is supported by (in) the database */
Packit Service ae04f2
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_findzone(void *driverarg, void *dbdata, const char *name,
Packit Service ae04f2
		  dns_clientinfomethods_t *methods,
Packit Service ae04f2
		  dns_clientinfo_t *clientinfo)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	PGresult *rs = NULL;
Packit Service ae04f2
	unsigned int rows;
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
	UNUSED(methods);
Packit Service ae04f2
	UNUSED(clientinfo);
Packit Service ae04f2
Packit Service ae04f2
	/* run the query and get the result set from the database. */
Packit Service ae04f2
	result = postgres_get_resultset(name, NULL, NULL,
Packit Service ae04f2
					FINDZONE, dbdata, &rs);
Packit Service ae04f2
	/* if we didn't get a result set, log an err msg. */
Packit Service ae04f2
	if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
		if (rs != NULL)
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver unable to return "
Packit Service ae04f2
			      "result set for findzone query");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
	/* count how many rows in result set */
Packit Service ae04f2
	rows = PQntuples(rs);
Packit Service ae04f2
	/* get rid of result set, we are done with it. */
Packit Service ae04f2
	PQclear(rs);
Packit Service ae04f2
Packit Service ae04f2
	/* if we returned any rows, zone is supported. */
Packit Service ae04f2
	if (rows > 0)
Packit Service ae04f2
		return (ISC_R_SUCCESS);
Packit Service ae04f2
Packit Service ae04f2
	/* no rows returned, zone is not supported. */
Packit Service ae04f2
	return (ISC_R_NOTFOUND);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*% Determine if the client is allowed to perform a zone transfer */
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name,
Packit Service ae04f2
		      const char *client)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	PGresult *rs = NULL;
Packit Service ae04f2
	unsigned int rows;
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
Packit Service ae04f2
	/* first check if the zone is supported by the database. */
Packit Service ae04f2
	result = postgres_findzone(driverarg, dbdata, name, NULL, NULL);
Packit Service ae04f2
	if (result != ISC_R_SUCCESS)
Packit Service ae04f2
		return (ISC_R_NOTFOUND);
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * if we get to this point we know the zone is supported by
Packit Service ae04f2
	 * the database the only questions now are is the zone
Packit Service ae04f2
	 * transfer is allowed for this client and did the config file
Packit Service ae04f2
	 * have an allow zone xfr query.
Packit Service ae04f2
	 *
Packit Service ae04f2
	 * Run our query, and get a result set from the database.
Packit Service ae04f2
	 */
Packit Service ae04f2
	result = postgres_get_resultset(name, NULL, client,
Packit Service ae04f2
					ALLOWXFR, dbdata, &rs);
Packit Service ae04f2
	/* if we get "not implemented", send it along. */
Packit Service ae04f2
	if (result == ISC_R_NOTIMPLEMENTED)
Packit Service ae04f2
		return result;
Packit Service ae04f2
	/* if we didn't get a result set, log an err msg. */
Packit Service ae04f2
	if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
		if (rs != NULL)
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver unable to return "
Packit Service ae04f2
			      "result set for allow xfr query");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
	/* count how many rows in result set */
Packit Service ae04f2
	rows = PQntuples(rs);
Packit Service ae04f2
	/* get rid of result set, we are done with it. */
Packit Service ae04f2
	PQclear(rs);
Packit Service ae04f2
Packit Service ae04f2
	/* if we returned any rows, zone xfr is allowed. */
Packit Service ae04f2
	if (rows > 0)
Packit Service ae04f2
		return (ISC_R_SUCCESS);
Packit Service ae04f2
Packit Service ae04f2
	/* no rows returned, zone xfr not allowed */
Packit Service ae04f2
	return (ISC_R_NOPERM);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * If the client is allowed to perform a zone transfer, the next order of
Packit Service ae04f2
 * business is to get all the nodes in the zone, so bind can respond to the
Packit Service ae04f2
 * query.
Packit Service ae04f2
 */
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_allnodes(const char *zone, void *driverarg, void *dbdata,
Packit Service ae04f2
		  dns_sdlzallnodes_t *allnodes)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	PGresult *rs = NULL;
Packit Service ae04f2
	unsigned int i;
Packit Service ae04f2
	unsigned int rows;
Packit Service ae04f2
	unsigned int fields;
Packit Service ae04f2
	unsigned int j;
Packit Service ae04f2
	unsigned int len;
Packit Service ae04f2
	char *tmpString;
Packit Service ae04f2
	char *endp;
Packit Service ae04f2
	int ttl;
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
Packit Service ae04f2
	/* run the query and get the result set from the database. */
Packit Service ae04f2
	result = postgres_get_resultset(zone, NULL, NULL,
Packit Service ae04f2
					ALLNODES, dbdata, &rs);
Packit Service ae04f2
	/* if we get "not implemented", send it along */
Packit Service ae04f2
	if (result == ISC_R_NOTIMPLEMENTED)
Packit Service ae04f2
		return result;
Packit Service ae04f2
	/* if we didn't get a result set, log an err msg. */
Packit Service ae04f2
	if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
		if (rs != NULL)
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver unable to return "
Packit Service ae04f2
			      "result set for all nodes query");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	rows = PQntuples(rs);	/* how many rows in result set */
Packit Service ae04f2
	fields = PQnfields(rs);	/* how many columns in result set */
Packit Service ae04f2
	for (i=0; i < rows; i++) {
Packit Service ae04f2
		if (fields < 4) {	/* gotta have at least 4 columns */
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "Postgres driver too few fields "
Packit Service ae04f2
				      "returned by all nodes query");
Packit Service ae04f2
		}
Packit Service ae04f2
		/* convert text to int, make sure it worked right  */
Packit Service ae04f2
		ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
Packit Service ae04f2
		if (*endp != '\0' || ttl < 0) {
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "Postgres driver ttl must be "
Packit Service ae04f2
				      "a positive number");
Packit Service ae04f2
		}
Packit Service ae04f2
		if (fields == 4) {
Packit Service ae04f2
			/* tell Bind about it. */
Packit Service ae04f2
			result = dns_sdlz_putnamedrr(allnodes,
Packit Service ae04f2
						     PQgetvalue(rs, i, 2),
Packit Service ae04f2
						     PQgetvalue(rs, i, 1),
Packit Service ae04f2
						     ttl,
Packit Service ae04f2
						     PQgetvalue(rs, i, 3));
Packit Service ae04f2
		} else {
Packit Service ae04f2
			/*
Packit Service ae04f2
			 * more than 4 fields, concatonat the last
Packit Service ae04f2
			 * ones together.  figure out how long to make
Packit Service ae04f2
			 * string
Packit Service ae04f2
			 */
Packit Service ae04f2
			for (j=3, len=0; j < fields; j++) {
Packit Service ae04f2
				len += strlen(PQgetvalue(rs, i, j)) + 1;
Packit Service ae04f2
			}
Packit Service ae04f2
			/* allocate memory, allow for NULL to term string */
Packit Service ae04f2
			tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
Packit Service ae04f2
			if (tmpString == NULL) {	/* we need more ram. */
Packit Service ae04f2
				isc_log_write(dns_lctx,
Packit Service ae04f2
					      DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
					      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
					      "Postgres driver unable to "
Packit Service ae04f2
					      "allocate memory for "
Packit Service ae04f2
					      "temporary string");
Packit Service ae04f2
				PQclear(rs);
Packit Service ae04f2
				return (ISC_R_FAILURE);
Packit Service ae04f2
			}
Packit Service ae04f2
			/* copy this field to tmpString */
Packit Service ae04f2
			strcpy(tmpString, PQgetvalue(rs, i, 3));
Packit Service ae04f2
			/* concatenate the rest, with spaces between */
Packit Service ae04f2
			for (j=4; j < fields; j++) {
Packit Service ae04f2
				strcat(tmpString, " ");
Packit Service ae04f2
				strcat(tmpString, PQgetvalue(rs, i, j));
Packit Service ae04f2
			}
Packit Service ae04f2
			/* tell Bind about it. */
Packit Service ae04f2
			result = dns_sdlz_putnamedrr(allnodes,
Packit Service ae04f2
						     PQgetvalue(rs, i, 2),
Packit Service ae04f2
						     PQgetvalue(rs, i, 1),
Packit Service ae04f2
						     ttl, tmpString);
Packit Service ae04f2
			isc_mem_free(ns_g_mctx, tmpString);
Packit Service ae04f2
		}
Packit Service ae04f2
		/* if we weren't successful, log err msg */
Packit Service ae04f2
		if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "dns_sdlz_putnamedrr returned error. "
Packit Service ae04f2
				      "Error code was: %s",
Packit Service ae04f2
				      isc_result_totext(result));
Packit Service ae04f2
			return (ISC_R_FAILURE);
Packit Service ae04f2
		}
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* free result set memory */
Packit Service ae04f2
	PQclear(rs);
Packit Service ae04f2
Packit Service ae04f2
	/* if we did return results, we are successful */
Packit Service ae04f2
	if (rows > 0)
Packit Service ae04f2
		return (ISC_R_SUCCESS);
Packit Service ae04f2
Packit Service ae04f2
	/* empty result set, no data found */
Packit Service ae04f2
	return (ISC_R_NOTFOUND);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * if the lookup function does not return SOA or NS records for the zone,
Packit Service ae04f2
 * use this function to get that information for Bind.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_authority(const char *zone, void *driverarg, void *dbdata,
Packit Service ae04f2
		   dns_sdlzlookup_t *lookup)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	PGresult *rs = NULL;
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
Packit Service ae04f2
	/* run the query and get the result set from the database. */
Packit Service ae04f2
	result = postgres_get_resultset(zone, NULL, NULL,
Packit Service ae04f2
					AUTHORITY, dbdata, &rs);
Packit Service ae04f2
	/* if we get "not implemented", send it along */
Packit Service ae04f2
	if (result == ISC_R_NOTIMPLEMENTED)
Packit Service ae04f2
		return result;
Packit Service ae04f2
	/* if we didn't get a result set, log an err msg. */
Packit Service ae04f2
	if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
		if (rs != NULL)
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver unable to return "
Packit Service ae04f2
			      "result set for authority query");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * lookup and authority result sets are processed in the same
Packit Service ae04f2
	 * manner postgres_process_rs does the job for both
Packit Service ae04f2
	 * functions.
Packit Service ae04f2
	 */
Packit Service ae04f2
	return postgres_process_rs(lookup, rs);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*% if zone is supported, lookup up a (or multiple) record(s) in it */
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_lookup(const char *zone, const char *name, void *driverarg,
Packit Service ae04f2
		void *dbdata, dns_sdlzlookup_t *lookup,
Packit Service ae04f2
		dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	PGresult *rs = NULL;
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
	UNUSED(methods);
Packit Service ae04f2
	UNUSED(clientinfo);
Packit Service ae04f2
Packit Service ae04f2
	/* run the query and get the result set from the database. */
Packit Service ae04f2
	result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
Packit Service ae04f2
	/* if we didn't get a result set, log an err msg. */
Packit Service ae04f2
	if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
		if (rs != NULL)
Packit Service ae04f2
			PQclear(rs);
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver unable to "
Packit Service ae04f2
			      "return result set for lookup query");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * lookup and authority result sets are processed in the same
Packit Service ae04f2
	 * manner postgres_process_rs does the job for both functions.
Packit Service ae04f2
	 */
Packit Service ae04f2
	return postgres_process_rs(lookup, rs);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * create an instance of the driver.  Remember, only 1 copy of the driver's
Packit Service ae04f2
 * code is ever loaded, the driver has to remember which context it's
Packit Service ae04f2
 * operating in.  This is done via use of the dbdata argument which is
Packit Service ae04f2
 * passed into all query functions.
Packit Service ae04f2
 */
Packit Service ae04f2
static isc_result_t
Packit Service ae04f2
postgres_create(const char *dlzname, unsigned int argc, char *argv[],
Packit Service ae04f2
		void *driverarg, void **dbdata)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
	dbinstance_t *dbi = NULL;
Packit Service ae04f2
	unsigned int j;
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
	/* if multi-threaded, we need a few extra variables. */
Packit Service ae04f2
	int dbcount;
Packit Service ae04f2
	db_list_t *dblist = NULL;
Packit Service ae04f2
	int i;
Packit Service ae04f2
	char *endp;
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
	UNUSED(dlzname);
Packit Service ae04f2
Packit Service ae04f2
/* seed random # generator */
Packit Service ae04f2
	srand( (unsigned)time( NULL ) );
Packit Service ae04f2
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
	/* if debugging, let user know we are multithreaded. */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
Packit Service ae04f2
		      "Postgres driver running multithreaded");
Packit Service ae04f2
#else /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
	/* if debugging, let user know we are single threaded. */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
Packit Service ae04f2
		      "Postgres driver running single threaded");
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
	/* verify we have at least 5 arg's passed to the driver */
Packit Service ae04f2
	if (argc < 5) {
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver requires at least "
Packit Service ae04f2
			      "4 command line args.");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* no more than 8 arg's should be passed to the driver */
Packit Service ae04f2
	if (argc > 8) {
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver cannot accept more than "
Packit Service ae04f2
			      "7 command line args.");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* multithreaded build can have multiple DB connections */
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
	/* check how many db connections we should create */
Packit Service ae04f2
	dbcount = strtol(argv[1], &endp, 10);
Packit Service ae04f2
	if (*endp != '\0' || dbcount < 0) {
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver database connection count "
Packit Service ae04f2
			      "must be positive.");
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* allocate memory for database connection list */
Packit Service ae04f2
	dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
Packit Service ae04f2
	if (dblist == NULL)
Packit Service ae04f2
		return (ISC_R_NOMEMORY);
Packit Service ae04f2
Packit Service ae04f2
	/* initialize DB connection list */
Packit Service ae04f2
	ISC_LIST_INIT(*dblist);
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * create the appropriate number of database instances (DBI)
Packit Service ae04f2
	 * append each new DBI to the end of the list
Packit Service ae04f2
	 */
Packit Service ae04f2
	for (i=0; i < dbcount; i++) {
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
		/* how many queries were passed in from config file? */
Packit Service ae04f2
		switch(argc) {
Packit Service ae04f2
		case 5:
Packit Service ae04f2
			result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
Packit Service ae04f2
						     NULL, argv[3], argv[4],
Packit Service ae04f2
						     NULL, &dbi;;
Packit Service ae04f2
			break;
Packit Service ae04f2
		case 6:
Packit Service ae04f2
			result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
Packit Service ae04f2
						     argv[5], argv[3], argv[4],
Packit Service ae04f2
						     NULL, &dbi;;
Packit Service ae04f2
			break;
Packit Service ae04f2
		case 7:
Packit Service ae04f2
			result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
Packit Service ae04f2
						     argv[5], argv[3], argv[4],
Packit Service ae04f2
						     NULL, &dbi;;
Packit Service ae04f2
			break;
Packit Service ae04f2
		case 8:
Packit Service ae04f2
			result = build_sqldbinstance(ns_g_mctx, argv[6],
Packit Service ae04f2
						     argv[7], argv[5], argv[3],
Packit Service ae04f2
						     argv[4], NULL, &dbi;;
Packit Service ae04f2
			break;
Packit Service ae04f2
		default:
Packit Service ae04f2
			/* not really needed, should shut up compiler. */
Packit Service ae04f2
			result = ISC_R_FAILURE;
Packit Service ae04f2
		}
Packit Service ae04f2
Packit Service ae04f2
Packit Service ae04f2
		if (result == ISC_R_SUCCESS) {
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
Packit Service ae04f2
				      "Postgres driver created database "
Packit Service ae04f2
				      "instance object.");
Packit Service ae04f2
		} else { /* unsuccessful?, log err msg and cleanup. */
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "Postgres driver could not create "
Packit Service ae04f2
				      "database instance object.");
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
		/* when multithreaded, build a list of DBI's */
Packit Service ae04f2
		ISC_LINK_INIT(dbi, link);
Packit Service ae04f2
		ISC_LIST_APPEND(*dblist, dbi, link);
Packit Service ae04f2
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
		/* create and set db connection */
Packit Service ae04f2
		dbi->dbconn = PQconnectdb(argv[2]);
Packit Service ae04f2
		/*
Packit Service ae04f2
		 * if db connection cannot be created, log err msg and
Packit Service ae04f2
		 * cleanup.
Packit Service ae04f2
		 */
Packit Service ae04f2
		if (dbi->dbconn == NULL) {
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "Postgres driver could not allocate "
Packit Service ae04f2
				      "memory for database connection");
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
Packit Service ae04f2
		/* if we cannot connect the first time, try 3 more times. */
Packit Service ae04f2
		for (j = 0;
Packit Service ae04f2
		     PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK &&
Packit Service ae04f2
			     j < 3;
Packit Service ae04f2
		     j++)
Packit Service ae04f2
			PQreset((PGconn *) dbi->dbconn);
Packit Service ae04f2
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
		/*
Packit Service ae04f2
		 * if multi threaded, let user know which connection
Packit Service ae04f2
		 * failed.  user could be attempting to create 10 db
Packit Service ae04f2
		 * connections and for some reason the db backend only
Packit Service ae04f2
		 * allows 9
Packit Service ae04f2
		 */
Packit Service ae04f2
		if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
Packit Service ae04f2
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
				      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
				      "Postgres driver failed to create "
Packit Service ae04f2
				      "database connection number %u "
Packit Service ae04f2
				      "after 4 attempts",
Packit Service ae04f2
				      i + 1);
Packit Service ae04f2
			goto cleanup;
Packit Service ae04f2
		}
Packit Service ae04f2
Packit Service ae04f2
		/* set DBI = null for next loop through. */
Packit Service ae04f2
		dbi = NULL;
Packit Service ae04f2
	}	/* end for loop */
Packit Service ae04f2
Packit Service ae04f2
		/* set dbdata to the list we created. */
Packit Service ae04f2
	*dbdata = dblist;
Packit Service ae04f2
Packit Service ae04f2
#else /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
	/* if single threaded, just let user know we couldn't connect. */
Packit Service ae04f2
	if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
Packit Service ae04f2
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
Packit Service ae04f2
			      "Postgres driver failed to create database "
Packit Service ae04f2
			      "connection after 4 attempts");
Packit Service ae04f2
		goto cleanup;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * single threaded build can only use 1 db connection, return
Packit Service ae04f2
	 * it via dbdata
Packit Service ae04f2
	 */
Packit Service ae04f2
	*dbdata = dbi;
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
	/* hey, we got through all of that ok, return success. */
Packit Service ae04f2
	return(ISC_R_SUCCESS);
Packit Service ae04f2
Packit Service ae04f2
 cleanup:
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * if multithreaded, we could fail because only 1 connection
Packit Service ae04f2
	 * couldn't be made.  We should cleanup the other successful
Packit Service ae04f2
	 * connections properly.
Packit Service ae04f2
	 */
Packit Service ae04f2
	postgres_destroy_dblist(dblist);
Packit Service ae04f2
Packit Service ae04f2
#else /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
	if (dbi != NULL)
Packit Service ae04f2
		destroy_sqldbinstance(dbi);
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
	return(ISC_R_FAILURE);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * destroy an instance of the driver.  Remember, only 1 copy of the driver's
Packit Service ae04f2
 * code is ever loaded, the driver has to remember which context it's
Packit Service ae04f2
 * operating in.  This is done via use of the dbdata argument.
Packit Service ae04f2
 * so we really only need to clean it up since we are not using driverarg.
Packit Service ae04f2
 */
Packit Service ae04f2
static void
Packit Service ae04f2
postgres_destroy(void *driverarg, void *dbdata)
Packit Service ae04f2
{
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USETHREADS
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
	/* cleanup the list of DBI's */
Packit Service ae04f2
	postgres_destroy_dblist((db_list_t *) dbdata);
Packit Service ae04f2
Packit Service ae04f2
#else /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
Packit Service ae04f2
	dbinstance_t *dbi;
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(driverarg);
Packit Service ae04f2
Packit Service ae04f2
	dbi = (dbinstance_t *) dbdata;
Packit Service ae04f2
Packit Service ae04f2
	/* release DB connection */
Packit Service ae04f2
	if (dbi->dbconn != NULL)
Packit Service ae04f2
		PQfinish((PGconn *) dbi->dbconn);
Packit Service ae04f2
Packit Service ae04f2
	/* destroy single DB instance */
Packit Service ae04f2
	destroy_sqldbinstance(dbi);
Packit Service ae04f2
Packit Service ae04f2
#endif /* ISC_PLATFORM_USETHREADS */
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/* pointers to all our runtime methods. */
Packit Service ae04f2
/* this is used during driver registration */
Packit Service ae04f2
/* i.e. in dlz_postgres_init below. */
Packit Service ae04f2
static dns_sdlzmethods_t dlz_postgres_methods = {
Packit Service ae04f2
	postgres_create,
Packit Service ae04f2
	postgres_destroy,
Packit Service ae04f2
	postgres_findzone,
Packit Service ae04f2
	postgres_lookup,
Packit Service ae04f2
	postgres_authority,
Packit Service ae04f2
	postgres_allnodes,
Packit Service ae04f2
	postgres_allowzonexfr,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
	NULL,
Packit Service ae04f2
};
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * Wrapper around dns_sdlzregister().
Packit Service ae04f2
 */
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
dlz_postgres_init(void) {
Packit Service ae04f2
	isc_result_t result;
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Write debugging message to log
Packit Service ae04f2
	 */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
Packit Service ae04f2
		      "Registering DLZ postgres driver.");
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Driver is always threadsafe.  When multithreaded all
Packit Service ae04f2
	 * functions use multithreaded code.  When not multithreaded,
Packit Service ae04f2
	 * all functions can only be entered once, but only 1 thread
Packit Service ae04f2
	 * of operation is available in Bind.  So everything is still
Packit Service ae04f2
	 * threadsafe.
Packit Service ae04f2
	 */
Packit Service ae04f2
	result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL,
Packit Service ae04f2
				  DNS_SDLZFLAG_RELATIVEOWNER |
Packit Service ae04f2
				  DNS_SDLZFLAG_RELATIVERDATA |
Packit Service ae04f2
				  DNS_SDLZFLAG_THREADSAFE,
Packit Service ae04f2
				  ns_g_mctx, &dlz_postgres);
Packit Service ae04f2
	/* if we can't register the driver, there are big problems. */
Packit Service ae04f2
	if (result != ISC_R_SUCCESS) {
Packit Service ae04f2
		UNEXPECTED_ERROR(__FILE__, __LINE__,
Packit Service ae04f2
				 "dns_sdlzregister() failed: %s",
Packit Service ae04f2
				 isc_result_totext(result));
Packit Service ae04f2
		result = ISC_R_UNEXPECTED;
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
Packit Service ae04f2
	return result;
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
/*%
Packit Service ae04f2
 * Wrapper around dns_sdlzunregister().
Packit Service ae04f2
 */
Packit Service ae04f2
void
Packit Service ae04f2
dlz_postgres_clear(void) {
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Write debugging message to log
Packit Service ae04f2
	 */
Packit Service ae04f2
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
Packit Service ae04f2
		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
Packit Service ae04f2
		      "Unregistering DLZ postgres driver.");
Packit Service ae04f2
Packit Service ae04f2
	/* unregister the driver. */
Packit Service ae04f2
	if (dlz_postgres != NULL)
Packit Service ae04f2
		dns_sdlzunregister(&dlz_postgres);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
#endif