dlz_postgres_driver.c

来自「非常好的dns解析软件」· C语言 代码 · 共 1,343 行 · 第 1/3 页

C
1,343
字号
/* * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was * conceived and contributed by Rob Butler. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. *//* * Copyright (C) 1999-2001  Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */#ifdef DLZ_POSTGRES#include <config.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <dns/log.h>#include <dns/sdlz.h>#include <dns/result.h>#include <isc/mem.h>#include <isc/platform.h>#include <isc/print.h>#include <isc/result.h>#include <isc/string.h>#include <isc/util.h>#include <named/globals.h>#include <dlz/sdlz_helper.h>#include <dlz/dlz_postgres_driver.h>/* temporarily include time. */#include <time.h>#include <libpq-fe.h>static dns_sdlzimplementation_t *dlz_postgres = NULL;#define dbc_search_limit 30#define ALLNODES 1#define ALLOWXFR 2#define AUTHORITY 3#define FINDZONE 4#define LOOKUP 5/* * Private methods *//* --------------- * Escaping arbitrary strings to get valid SQL strings/identifiers. * * Replaces "\\" with "\\\\" and "'" with "''". * length is the length of the buffer pointed to by * from.  The buffer at to must be at least 2*length + 1 characters * long.  A terminating NUL character is written. * * NOTICE!!! * This function was borrowed directly from PostgreSQL's libpq. * The function was originally called PQescapeString and renamed * to postgres_makesafe to avoid a naming collision. * PQescapeString is a new function made available in Postgres 7.2. * For some reason the function is not properly exported on Win32 * builds making the function unavailable on Windows.  Also, since * this function is new it would require building this driver with * the libpq 7.2.  By borrowing this function the Windows problem * is solved, and the dependence on libpq 7.2 is removed.  Libpq is * still required of course, but an older version should work now too. * * The copyright statements from the original file containing this * function are included below: * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * --------------- */static size_tpostgres_makesafe(char *to, const char *from, size_t length){	const char *source = from;	char	   *target = to;	unsigned int remaining = length;	while (remaining > 0)	{		switch (*source)		{		case '\\':			*target = '\\';			target++;			*target = '\\';			/* target and remaining are updated below. */			break;		case '\'':			*target = '\'';			target++;			*target = '\'';			/* target and remaining are updated below. */			break;		default:			*target = *source;			/* target and remaining are updated below. */		}		source++;		target++;		remaining--;	}	/* Write the terminating NUL character. */	*target = '\0';	return target - to;}#ifdef ISC_PLATFORM_USETHREADS/*% * Properly cleans up a list of database instances. * This function is only used when the driver is compiled for * multithreaded operation. */static voidpostgres_destroy_dblist(db_list_t *dblist){	dbinstance_t *ndbi = NULL;	dbinstance_t *dbi = NULL;	/* get the first DBI in the list */	ndbi = ISC_LIST_HEAD(*dblist);	/* loop through the list */	while (ndbi != NULL) {		dbi = ndbi;		/* get the next DBI in the list */		ndbi = ISC_LIST_NEXT(dbi, link);		/* release DB connection */		if (dbi->dbconn != NULL)			PQfinish((PGconn *) dbi->dbconn);		/* release all memory that comprised a DBI */		destroy_sqldbinstance(dbi);	}	/* release memory for the list structure */	isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));}/*% * Loops through the list of DB instances, attempting to lock * on the mutex.  If successful, the DBI is reserved for use * and the thread can perform queries against the database. * If the lock fails, the next one in the list is tried. * looping continues until a lock is obtained, or until * the list has been searched dbc_search_limit times. * This function is only used when the driver is compiled for * multithreaded operation. */static dbinstance_t *postgres_find_avail_conn(db_list_t *dblist){	dbinstance_t *dbi = NULL;	dbinstance_t *head;	int count = 0;	/* get top of list */	head = dbi = ISC_LIST_HEAD(*dblist);	/* loop through list */	while (count < dbc_search_limit) {		/* try to lock on the mutex */		if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)			return dbi; /* success, return the DBI for use. */		/* not successful, keep trying */		dbi = ISC_LIST_NEXT(dbi, link);		/* check to see if we have gone to the top of the list. */		if (dbi == NULL) {			count++;			dbi = head;		}	}	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,		      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,		      "Postgres driver unable to find available connection "		      "after searching %d times",		      count);	return NULL;}#endif /* ISC_PLATFORM_USETHREADS *//*% * Allocates memory for a new string, and then constructs the new * string by "escaping" the input string.  The new string is * safe to be used in queries.  This is necessary because we cannot * be sure of what types of strings are passed to us, and we don't * want special characters in the string causing problems. */static char *postgres_escape_string(const char *instr) {	char *outstr;	unsigned int len;	if (instr == NULL)		return NULL;	len = strlen(instr);	outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);	if (outstr == NULL)		return NULL;	postgres_makesafe(outstr, instr, len);	/* PQescapeString(outstr, instr, len); */	return outstr;}/*% * This function is the real core of the driver.   Zone, record * and client strings are passed in (or NULL is passed if the * string is not available).  The type of query we want to run * is indicated by the query flag, and the dbdata object is passed * passed in to.  dbdata really holds either: *		1) a list of database instances (in multithreaded mode) OR *		2) a single database instance (in single threaded mode) * The function will construct the query and obtain an available * database instance (DBI).  It will then run the query and hopefully * obtain a result set.  Postgres is nice, in that once the result * set is returned, we can make the db connection available for another * thread to use, while this thread continues on.  So, the DBI is made * available ASAP by unlocking the instance_lock after we have cleaned * it up properly. */static isc_result_tpostgres_get_resultset(const char *zone, const char *record,		       const char *client, unsigned int query,		       void *dbdata, PGresult **rs){	isc_result_t result;	dbinstance_t *dbi = NULL;	char *querystring = NULL;	unsigned int i = 0;	unsigned int j = 0;	/* temporarily get a unique thread # */	unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));	REQUIRE(*rs == NULL);	/* temporary logging message */	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,		      "%d Getting DBI", dlz_thread_num);	/* get db instance / connection */#ifdef ISC_PLATFORM_USETHREADS	/* find an available DBI from the list */	dbi = postgres_find_avail_conn((db_list_t *) dbdata);#else /* ISC_PLATFORM_USETHREADS */	/*	 * only 1 DBI - no need to lock instance lock either	 * only 1 thread in the whole process, no possible contention.	 */	dbi =  (dbinstance_t *) dbdata;#endif /* ISC_PLATFORM_USETHREADS */	/* temporary logging message */	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,		      "%d Got DBI - checking query", dlz_thread_num);	/* if DBI is null, can't do anything else */	if (dbi == NULL) {		result = ISC_R_FAILURE;		goto cleanup;	}	/* what type of query are we going to run? */	switch(query) {	case ALLNODES:		/*		 * if the query was not passed in from the config file		 * then we can't run it.  return not_implemented, so		 * it's like the code for that operation was never		 * built into the driver.... AHHH flexibility!!!		 */		if (dbi->allnodes_q == NULL) {			result = ISC_R_NOTIMPLEMENTED;			goto cleanup;		}		break;	case ALLOWXFR:		/* same as comments as ALLNODES */		if (dbi->allowxfr_q == NULL) {			result = ISC_R_NOTIMPLEMENTED;			goto cleanup;		}		break;	case AUTHORITY:		/* same as comments as ALLNODES */		if (dbi->authority_q == NULL) {			result = ISC_R_NOTIMPLEMENTED;			goto cleanup;		}		break;	case FINDZONE:		/* this is required.  It's the whole point of DLZ! */		if (dbi->findzone_q == NULL) {			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),				      "No query specified for findzone.  "				      "Findzone requires a query");			result = ISC_R_FAILURE;			goto cleanup;		}		break;	case LOOKUP:		/* this is required.  It's also a major point of DLZ! */		if (dbi->lookup_q == NULL) {			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,				      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),				      "No query specified for lookup.  "				      "Lookup requires a query");			result = ISC_R_FAILURE;			goto cleanup;		}		break;	default:		/*		 * this should never happen.  If it does, the code is		 * screwed up!		 */		UNEXPECTED_ERROR(__FILE__, __LINE__,				 "Incorrect query flag passed to "				 "postgres_get_resultset");		result = ISC_R_UNEXPECTED;		goto cleanup;	}	/* temporary logging message */	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,		      "%d checked query", dlz_thread_num);	/*	 * was a zone string passed?  If so, make it safe for use in	 * queries.	 */	if (zone != NULL) {		dbi->zone = postgres_escape_string(zone);		if (dbi->zone == NULL) {			result = ISC_R_NOMEMORY;			goto cleanup;		}	} else {	/* no string passed, set the string pointer to NULL */		dbi->zone = NULL;	}	/* temporary logging message */	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,		      "%d did zone", dlz_thread_num);	/*	 * was a record string passed?  If so, make it safe for use in	 * queries.	 */	if (record != NULL) {		dbi->record = postgres_escape_string(record);		if (dbi->record == NULL) {			result = ISC_R_NOMEMORY;			goto cleanup;		}	} else {	/* no string passed, set the string pointer to NULL */		dbi->record = NULL;	}	/* temporary logging message */	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,		      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,		      "%d did record", dlz_thread_num);	/*	 * was a client string passed?  If so, make it safe for use in	 * queries.	 */	if (client != NULL) {		dbi->client = postgres_escape_string(client);		if (dbi->client == NULL) {			result = ISC_R_NOMEMORY;			goto cleanup;		}	} else {	/* no string passed, set the string pointer to NULL */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?