/*
 * 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.
 */

SDLZ Helper

The SDLZ helper functions are meant to make development of flexible drivers for
SDLZ much easier.  This document gives a very brief and high-level overview of
how to use the SDLZ helper functions and structures.  Anyone who will build a
SDLZ driver is expected to become familiar with Bind's existing functions and
way of doing things.  Additionally, it is expected that you will have looked
over the existing SDLZ drivers in an effort to understand how they operate.
Once you have done that, this brief description of the SDLZ helper functions
should be all you need.

The SDLZ helper functions revolve around a data structure called dbinstance.
You should not directly use the dbinstance type.  Use dbinstance_t instead.

A dbinstance is meant to hold everything a driver would need for communicating
with a database.  It is meant to be generic enough so as to support SQL, LDAP,
and possibly other databases.  The dbinstance structure is listed below.

struct dbinstance {
	void				*dbconn;
	query_list_t			*allnodes_q;
	query_list_t			*allowxfr_q;
	query_list_t			*authority_q;
	query_list_t			*findzone_q;
	query_list_t			*lookup_q;
	query_list_t		        *countzone_q;
	char				*query_buf;
	char				*zone;
	char				*record;
	char				*client;
	isc_mem_t			*mctx;
	isc_mutex_t			instance_lock;
	ISC_LINK(dbinstance_t)	        link;

};


Three functions are made available by SDLZ helper.  The first we will look at
is:

isc_result_t
build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str, 
		const char *allowxfr_str, const char *authority_str,
		const char *findzone_str, const char *lookup_str,
		const char *countzone_str, dbinstance_t **dbi);
                                                                        
This function can be used to build a dbinstance_t structure. You pass in a Bind
memory context and several query strings, and it will pass back a dbinstance_t.
The most powerful feature of this function is it will parse each of the query
strings and build a query list from them.  Querylists are lists of strings or
pointers to a pointer to a string.  You needn't worry about how all this works.
It is all taken care of inside of the function for you.  The important thing to
know is that three tokens are recognized.  Those tokens are:

%zone%
%record%
%client%

Where those tokens appear in a string, they will be replaced by values when we
build a query string from the query list.

Notice the first element in the dbinstance_t structure is "void *dbconn".  This
element is meant to be used to hold a pointer to a database connection.  Use
whatever means your database system normally uses for establishing a database
connection, and then store a pointer to that in the dbinstance_t "dbconn".

Your dbinstance_t is now built.  Notice the last element in the dbinstance_t is
"ISC_LINK(dbinstance_t)	link".  This allows you to build a list of dbinstance_t
structures.  The idea is to have one dbinstance_t created for each connection
to your database system.  You should never try to use one dbinstance_t with
multiple database connections.  ALWAYS store your database connection in
"dbconn," and then build a new dbinstance_t for each new database connection.

Once you have built all the dbinstance_t's you need, they are ready for use.

Notice the "isc_mutex_t instance_lock".  Use Bind's "isc_mutex_trylock"
function.  The idea here is that you will loop through your list of
dbinstance_t's trying to lock on each in turn.  When you successfully lock
on one, the dbinstance_t is reserved for your use and so is the associated
"dbconn"!  Don't forget to use "isc_mutex_unlock" to unlock the mutex and
flag your dbinstance_t as available for the next thread to use when you are
done.

Once you have reserved the dbinstance_t, put it to work.  Notice the char *
zone, record, and client elements of dbinstance_t.  These pointers correspond
to the three tokens that are recognized.  Whatever string you set zone to
will replace all occurrences of the token %zone% in your strings.

To build a query string, call the function:

char * build_querystring(mctx, querylist);

You pass an memory context and a querylist to this function.  The query lists
are :

allnodes_q
allowxfr_q
authority_q
findzone_q
lookup_q
countzone_q

These are all part of the dbinstance_t structure that was created earlier.
The build_querystring() function will build a string where all the tokens have
been replaced by the strings you have set the dbinstance_t zone, record, and
client pointers to.

Do your query using the new string.  When you are done don't forget to properly
de-allocate the query string.  Also, for good measure, you should set the zone, 
record and client pointers in the dbinstance_t to NULL so everything will be
clean next time you use the structure.  After you are done performing your 
query, don't forget to unlock the instance_lock mutex.

The last function:

void destroy_sqldbinstance(dbi);

This function will properly clean up any memory that was allocated by
build_sqldbinstance().  You are responsible for freeing and de-allocating the
database connection stored in "dbconn" before destroying the dbinstance_t.

