dlz_mysql_driver.c revision abff0f462a758383d012887d3a97da4dac0c5a94
/*
* 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_MYSQL
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <isc/platform.h>
#include <dlz/sdlz_helper.h>
#include <dlz/dlz_mysql_driver.h>
#include <mysql.h>
#define dbc_search_limit 30
#define ALLNODES 1
#define ALLOWXFR 2
#define AUTHORITY 3
#define FINDZONE 4
#define COUNTZONE 5
#define LOOKUP 6
/*
* Private methods
*/
/*%
* 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 *
char *outstr;
unsigned int len;
return NULL;
return NULL;
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 a single database instance.
* The function will construct and run the query, hopefully getting
* a result set.
*/
static isc_result_t
{
char *querystring = NULL;
unsigned int i = 0;
unsigned int j = 0;
int qres = 0;
else
/* get db instance / connection */
/* if DBI is null, can't do anything else */
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!!!
*/
goto cleanup;
}
break;
case ALLOWXFR:
/* same as comments as ALLNODES */
goto cleanup;
}
break;
case AUTHORITY:
/* same as comments as ALLNODES */
goto cleanup;
}
break;
case FINDZONE:
/* this is required. It's the whole point of DLZ! */
"No query specified for findzone. "
"Findzone requires a query");
goto cleanup;
}
break;
case COUNTZONE:
/* same as comments as ALLNODES */
goto cleanup;
}
break;
case LOOKUP:
/* this is required. It's also a major point of DLZ! */
"No query specified for lookup. "
"Lookup requires a query");
goto cleanup;
}
break;
default:
/*
* this should never happen. If it does, the code is
* screwed up!
*/
"Incorrect query flag passed to "
"mysql_get_resultset");
goto cleanup;
}
/*
* was a zone string passed? If so, make it safe for use in
* queries.
*/
zone);
goto cleanup;
}
} else { /* no string passed, set the string pointer to NULL */
}
/*
* was a record string passed? If so, make it safe for use in
* queries.
*/
record);
goto cleanup;
}
} else { /* no string passed, set the string pointer to NULL */
}
/*
* was a client string passed? If so, make it safe for use in
* queries.
*/
client);
goto cleanup;
}
} else { /* no string passed, set the string pointer to NULL */
}
/*
* what type of query are we going to run? this time we build
* the actual query to run.
*/
switch(query) {
case ALLNODES:
break;
case ALLOWXFR:
break;
case AUTHORITY:
break;
case FINDZONE:
break;
case COUNTZONE:
break;
case LOOKUP:
break;
default:
/*
* this should never happen. If it does, the code is
* screwed up!
*/
"Incorrect query flag passed to "
"mysql_get_resultset");
goto cleanup;
}
/* if the querystring is null, Bummer, outta RAM. UPGRADE TIME!!! */
if (querystring == NULL) {
goto cleanup;
}
/*
* output the full query string during debug so we can see
* what lame error the query has.
*/
"\nQuery String: %s\n", querystring);
/* attempt query up to 3 times. */
for (i=0; i < 3; i++) {
if (qres == 0)
break;
;
}
if (qres == 0) {
}
} else {
}
/* it's always good to cleanup after yourself */
/* if we couldn't even get DBI, just return NULL */
return ISC_R_FAILURE;
/* free dbi->zone string */
/* free dbi->record string */
/* free dbi->client string */
/* release query string */
if (querystring != NULL)
/* return result */
return result;
}
/*%
* The processing of result sets for lookup and authority are
* exactly the same. So that functionality has been moved
* into this function to minimize code.
*/
static isc_result_t
{
unsigned int fields;
unsigned int j;
unsigned int len;
char *tmpString;
char *endp;
int ttl;
switch(fields) {
case 1:
/*
* one column in rs, it's the data field. use
* default type of A record, and default TTL
* of 86400
*/
break;
case 2:
/*
* two columns, data field, and data type.
* use default TTL of 86400.
*/
break;
case 3:
/*
* three columns, all data no defaults.
* convert text to int, make sure it worked
* right.
*/
"mysql driver ttl must be "
"a postive number");
}
break;
default:
/*
* more than 3 fields, concatenate the last
* ones together. figure out how long to make
* string.
*/
}
/*
* allocate string memory, allow for NULL to
* term string
*/
/* major bummer, need more ram */
"mysql driver unable "
"to allocate memory for "
"temporary string");
return (ISC_R_FAILURE); /* Yeah, I'd say! */
}
/* copy field to tmpString */
/*
* concat the rest of fields together, space
* between each one.
*/
for (j=3; j < fields; j++) {
}
/* convert text to int, make sure it worked right */
"mysql driver ttl must be "
"a postive number");
}
/* ok, now tell Bind about it. */
/* done, get rid of this thing. */
}
/* I sure hope we were successful */
if (result != ISC_R_SUCCESS) {
/* nope, get rid of the Result set, and log a msg */
"dns_sdlz_putrr returned error. "
"Error code was: %s",
return (ISC_R_FAILURE);
}
}
/* free result set memory */
/* return result code */
return result;
}
/*
* SDLZ interface methods
*/
/*% determine if the zone is supported by (in) the database */
static isc_result_t
{
/* run the query and get the result set from the database. */
/* if we didn't get a result set, log an err msg. */
"mysql driver unable to return "
"result set for findzone query");
return (ISC_R_FAILURE);
}
/* count how many rows in result set */
/* get rid of result set, we are done with it. */
/* if we returned any rows, zone is supported. */
if (rows > 0) {
return (ISC_R_SUCCESS);
}
/* no rows returned, zone is not supported. */
return (ISC_R_NOTFOUND);
}
/*% Determine if the client is allowed to perform a zone transfer */
static isc_result_t
const char *client)
{
/* first check if the zone is supported by the database. */
if (result != ISC_R_SUCCESS)
return (ISC_R_NOTFOUND);
/*
* if we get to this point we know the zone is supported by
* the database the only questions now are is the zone
* transfer is allowed for this client and did the config file
* have an allow zone xfr query.
*
* Run our query, and get a result set from the database.
*/
/* if we get "not implemented", send it along. */
if (result == ISC_R_NOTIMPLEMENTED)
return result;
/* if we didn't get a result set, log an err msg. */
"mysql driver unable to return "
"result set for allow xfr query");
return (ISC_R_FAILURE);
}
/* count how many rows in result set */
/* get rid of result set, we are done with it. */
/* if we returned any rows, zone xfr is allowed. */
if (rows > 0)
return (ISC_R_SUCCESS);
/* no rows returned, zone xfr not allowed */
return (ISC_R_NOPERM);
}
/*%
* If the client is allowed to perform a zone transfer, the next order of
* business is to get all the nodes in the zone, so bind can respond to the
* query.
*/
static isc_result_t
{
unsigned int fields;
unsigned int j;
unsigned int len;
char *tmpString;
char *endp;
int ttl;
/* run the query and get the result set from the database. */
/* if we get "not implemented", send it along */
if (result == ISC_R_NOTIMPLEMENTED)
return result;
/* if we didn't get a result set, log an err msg. */
if (result != ISC_R_SUCCESS) {
"mysql driver unable to return "
"result set for all nodes query");
return (ISC_R_FAILURE);
}
"mysql driver too few fields returned "
"by all nodes query");
}
/* convert text to int, make sure it worked right */
"mysql driver ttl must be "
"a postive number");
}
if (fields == 4) {
/* tell Bind about it. */
} else {
/*
* more than 4 fields, concatenate the last
* ones together. figure out how long to make
* string.
*/
}
/* allocate memory, allow for NULL to term string */
"mysql driver unable "
"to allocate memory for "
"temporary string");
return (ISC_R_FAILURE);
}
/* copy this field to tmpString */
/* concatonate the rest, with spaces between */
for (j=4; j < fields; j++) {
}
/* tell Bind about it. */
}
/* if we weren't successful, log err msg */
if (result != ISC_R_SUCCESS) {
"dns_sdlz_putnamedrr returned error. "
"Error code was: %s",
break;
}
/* get next row from the result set */
}
/* free result set memory */
return result;
}
/*% if the lookup function does not return SOA or NS records for the zone,
* use this function to get that information for Bind.
*/
static isc_result_t
{
/* run the query and get the result set from the database. */
/* if we get "not implemented", send it along */
if (result == ISC_R_NOTIMPLEMENTED)
return result;
/* if we didn't get a result set, log an err msg. */
if (result != ISC_R_SUCCESS) {
"mysql driver unable to return "
"result set for authority query");
return (ISC_R_FAILURE);
}
/*
* lookup and authority result sets are processed in the same
* manner mysql_process_rs does the job for both functions.
*/
}
/*% if zone is supported, lookup up a (or multiple) record(s) in it */
static isc_result_t
{
/* run the query and get the result set from the database. */
/* if we didn't get a result set, log an err msg. */
if (result != ISC_R_SUCCESS) {
"mysql driver unable to return "
"result set for lookup query");
return (ISC_R_FAILURE);
}
/*
* lookup and authority result sets are processed in the same manner
* mysql_process_rs does the job for both functions.
*/
}
/*%
* create an instance of the driver. Remember, only 1 copy of the driver's
* code is ever loaded, the driver has to remember which context it's
* operating in. This is done via use of the dbdata argument which is
* passed into all query functions.
*/
static isc_result_t
{
int port;
char *endp;
int j;
unsigned int flags = 0;
#if MYSQL_VERSION_ID >= 50000
#endif
/* verify we have at least 4 arg's passed to the driver */
if (argc < 4) {
"mysql driver requires "
"at least 4 command line args.");
return (ISC_R_FAILURE);
}
/* no more than 8 arg's should be passed to the driver */
if (argc > 8) {
"mysql driver cannot accept "
"more than 7 command line args.");
return (ISC_R_FAILURE);
}
/* parse connection string and get paramters. */
/* get db name - required */
"mysql driver requires a dbname parameter.");
goto full_cleanup;
}
/* get db port. Not required, but must be > 0 if specified */
port = 0;
} else {
"Mysql driver port "
"must be a positive number.");
goto full_cleanup;
}
}
/* how many queries were passed in from config file? */
switch(argc) {
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
default:
/* not really needed, should shut up compiler. */
}
/* unsuccessful?, log err msg and cleanup. */
if (result != ISC_R_SUCCESS) {
"mysql driver could not create "
"database instance object.");
goto cleanup;
}
/* create and set db connection */
/* if db connection cannot be created, log err msg and cleanup. */
"mysql driver could not allocate "
"memory for database connection");
goto full_cleanup;
}
}
}
}
#if MYSQL_VERSION_ID >= 50000
/* enable automatic reconnection. */
&auto_reconnect) != 0) {
"mysql driver failed to set "
"MYSQL_OPT_RECONNECT option, continuing");
}
#endif
flags);
/* let user know if we couldn't connect. */
"mysql driver failed to create "
"database connection after 4 attempts");
goto full_cleanup;
}
/* return db connection via dbdata */
goto cleanup;
return result;
}
/*%
* destroy the driver. Remember, only 1 copy of the driver's
* code is ever loaded, the driver has to remember which context it's
* operating in. This is done via use of the dbdata argument.
* so we really only need to clean it up since we are not using driverarg.
*/
static void
{
/* release DB connection */
/* destroy DB instance */
}
/* pointers to all our runtime methods. */
/* this is used during driver registration */
/* i.e. in dlz_mysql_init below. */
static dns_sdlzmethods_t dlz_mysql_methods = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
/*%
* Wrapper around dns_sdlzregister().
*/
dlz_mysql_init(void) {
/*
* Write debugging message to log
*/
"Registering DLZ mysql driver.");
/* Driver is always threadsafe. Because of the way MySQL handles
* threads the MySQL driver can only be used when bind is run single
* threaded. Using MySQL with Bind running multi-threaded is not
* allowed. When using the MySQL driver "-n1" should always be
* passed to Bind to guarantee single threaded operation.
*/
/* if we can't register the driver, there are big problems. */
if (result != ISC_R_SUCCESS) {
"dns_sdlzregister() failed: %s",
}
return result;
}
/*%
* Wrapper around dns_sdlzunregister().
*/
void
dlz_mysql_clear(void) {
/*
* Write debugging message to log
*/
"Unregistering DLZ mysql driver.");
/* unregister the driver. */
}
#endif