/*
* 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, 2013, 2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* This provides the externally loadable MySQL DLZ module, without
* update support
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <dlz_minimal.h>
#include <dlz_list.h>
#include <dlz_dbi.h>
#include <dlz_pthread.h>
/*%
* Structure to hold everthing needed by this "instance" of the MySQL
* module remember, the module code is only loaded once, but may have
* many separate instances.
*/
typedef struct {
#if PTHREADS
int dbcount;
#else
#endif
unsigned int flags;
char *dbname;
char *host;
char *user;
char *pass;
char *socket;
int port;
/* Helper functions from the dlz_dlopen driver */
/* forward references */
void
dlz_destroy(void *dbdata);
static void
/*
* Private methods
*/
void
/* release DB connection */
/* destroy DB instance */
}
#if PTHREADS
/*%
* Properly cleans up a list of database instances.
* This function is only used when the module is compiled for
* multithreaded operation.
*/
static void
}
/* release memory for the list structure */
}
/*%
* 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 module is compiled for
* multithreaded operation.
*/
static dbinstance_t *
int count = 0;
/* get top of list */
/* loop through list */
while (count < dbc_search_limit) {
/* try to lock on the mutex */
return (dbi); /* success, return the DBI for use. */
/* not successful, keep trying */
/* check to see if we have gone to the top of the list. */
count++;
}
}
"MySQL module unable to find available connection "
"after searching %d times", count);
return (NULL);
}
#endif /* PTHREADS */
/*%
* 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 module. 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
{
unsigned int i = 0;
unsigned int j = 0;
int qres = 0;
#if PTHREADS
/* find an available DBI from the list */
#else /* PTHREADS */
/*
* only 1 DBI - no need to lock instance lock either
* only 1 thread in the whole process, no possible contention.
*/
#endif /* PTHREADS */
goto cleanup;
}
/* what type of query are we going to run? */
switch(query) {
case ALLNODES:
goto cleanup;
}
break;
case ALLOWXFR:
goto cleanup;
}
break;
case AUTHORITY:
goto cleanup;
}
break;
case FINDZONE:
"No query specified for findzone. "
"Findzone requires a query");
goto cleanup;
}
break;
case COUNTZONE:
goto cleanup;
}
break;
case LOOKUP:
"No query specified for lookup. "
"Lookup requires a query");
goto cleanup;
}
break;
default:
"Incorrect query flag passed to "
"mysql_get_resultset");
goto cleanup;
}
zone);
goto cleanup;
}
} else
record);
goto cleanup;
}
} else
client);
goto cleanup;
}
} else
/*
* 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:
"Incorrect query flag passed to "
"mysql_get_resultset");
}
if (querystring == NULL) {
goto cleanup;
}
/* output the full query string when debugging */
/* attempt query up to 3 times. */
for (i = 0; i < 3; i++) {
if (qres == 0)
break;
for (j = 0; j < 4; j++)
break;
}
if (qres == 0) {
}
} else
return (ISC_R_FAILURE);
}
}
}
/* release the lock so another thread can use this dbi */
if (querystring != NULL)
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;
char *tmpString;
char *endp;
int ttl;
unsigned int len = 0;
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 module ttl must be "
"a postive number");
return (ISC_R_FAILURE);
}
break;
default:
/*
* more than 3 fields, concatenate the last
* ones together. figure out how long to make
* string.
*/
for (j = 2; j < fields; j++)
/*
* allocate string memory, allow for NULL to
* term string
*/
"MySQL module unable to allocate "
"memory for temporary string");
return (ISC_R_FAILURE);
}
for (j = 3; j < fields; j++) {
}
"MySQL module ttl must be "
"a postive number");
return (ISC_R_FAILURE);
}
}
if (result != ISC_R_SUCCESS) {
"putrr returned error: %d", result);
return (ISC_R_FAILURE);
}
}
return (result);
}
/*
* DLZ methods
*/
/*% determine if the zone is supported by (in) the database */
{
"MySQL module unable to return "
"result set for findzone query");
return (ISC_R_FAILURE);
}
/*
* if we returned any rows, the zone is supported.
*/
if (rows > 0) {
return (ISC_R_SUCCESS);
}
return (ISC_R_NOTFOUND);
}
/*% Determine if the client is allowed to perform a zone transfer */
/* 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.
*/
if (result == ISC_R_NOTIMPLEMENTED)
return (result);
"MySQL module unable to return "
"result set for allow xfr query");
return (ISC_R_FAILURE);
}
/*
* count how many rows in result set; if we returned any,
* zone xfr is allowed.
*/
if (rows > 0)
return (ISC_R_SUCCESS);
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.
*/
unsigned int fields;
unsigned int j;
char *tmpString;
char *endp;
int ttl;
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 module unable to return "
"result set for all nodes query");
goto cleanup;
}
if (fields < 4) {
"MySQL module too few fields returned "
"by all nodes query");
goto cleanup;
}
"MySQL module ttl must be "
"a postive number");
goto cleanup;
}
if (fields == 4) {
} else {
unsigned int len = 0;
/*
* more than 4 fields, concatenate the last
* ones together.
*/
for (j = 3; j < fields; j++)
"MySQL module unable to allocate "
"memory for temporary string");
goto cleanup;
}
for (j = 4; j < fields; j++) {
}
}
if (result != ISC_R_SUCCESS) {
"putnamedrr returned error: %s", result);
break;
}
}
return (result);
}
/*%
* If the lookup function does not return SOA or NS records for the zone,
* use this function to get that information for named.
*/
if (result == ISC_R_NOTIMPLEMENTED)
return (result);
if (result != ISC_R_SUCCESS) {
"MySQL module 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 */
{
/* if we didn't get a result set, log an err msg. */
if (result != ISC_R_SUCCESS) {
"MySQL module 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 module.
*/
void **dbdata, ...)
{
char *endp;
int j;
const char *helper_name;
#if MYSQL_VERSION_ID >= 50000
#endif
#if PTHREADS
int dbcount;
int i;
#endif /* PTHREADS */
/* allocate memory for MySQL instance */
return (ISC_R_NOMEMORY);
/* Fill in the helper functions */
#if PTHREADS
/* if debugging, let user know we are multithreaded. */
#else /* PTHREADS */
/* if debugging, let user know we are single threaded. */
#endif /* PTHREADS */
/* verify we have at least 4 arg's passed to the module */
if (argc < 4) {
"MySQL module requires "
"at least 4 command line args.");
return (ISC_R_FAILURE);
}
/* no more than 8 arg's should be passed to the module */
if (argc > 8) {
"MySQL module cannot accept "
"more than 7 command line args.");
return (ISC_R_FAILURE);
}
/* get db name - required */
"MySQL module requires a dbname parameter.");
goto cleanup;
}
/* get db port. Not required, but must be > 0 if specified */
else {
"Mysql module: port "
"must be a positive number.");
goto cleanup;
}
}
}
}
}
#if PTHREADS
/* multithreaded build can have multiple DB connections */
dbcount = 1;
else {
"MySQL database connection count "
"must be positive.");
goto cleanup;
}
}
/* allocate memory for database connection list */
goto cleanup;
}
/* initialize DB connection list */
/*
* create the appropriate number of database instances (DBI)
* append each new DBI to the end of the list
*/
for (i = 0; i < dbcount; i++) {
#endif /* PTHREADS */
switch(argc) {
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
default:
}
if (result != ISC_R_SUCCESS) {
"MySQL module could not create "
"database instance object.");
goto cleanup;
}
#if PTHREADS
/* when multithreaded, build a list of DBI's */
#else
/*
* when single threaded, hold onto the one connection
* instance.
*/
#endif
/* create and set db connection */
"MySQL module could not allocate "
"memory for database connection");
goto cleanup;
}
#if MYSQL_VERSION_ID >= 50000
/* enable automatic reconnection. */
&auto_reconnect) != 0) {
"MySQL module failed to set "
"MYSQL_OPT_RECONNECT option, continuing");
}
#endif
"MySQL connection failed: %s",
}
"MySQL module failed to create "
"database connection after 4 attempts");
goto cleanup;
}
#if PTHREADS
/* set DBI = null for next loop through. */
}
#endif /* PTHREADS */
return (ISC_R_SUCCESS);
return (result);
}
/*%
* Destroy the module.
*/
void
#if PTHREADS
/* cleanup the list of DBI's */
#else /* PTHREADS */
#endif /* PTHREADS */
}
/*
* Return the version of the API
*/
int
*flags |= (DNS_SDLZFLAG_RELATIVEOWNER |
return (DLZ_DLOPEN_VERSION);
}
/*
* Register a helper function from the bind9 dlz_dlopen driver
*/
static void
}