#ifdef DLZ_ODBC
#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_odbc_driver.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
static dns_sdlzimplementation_t *dlz_odbc = NULL;
#define dbc_search_limit 30
#define ALLNODES 1
#define ALLOWXFR 2
#define AUTHORITY 3
#define FINDZONE 4
#define LOOKUP 5
#define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0)
typedef struct{
SQLHDBC dbc;
SQLHSTMT stmnt;
} odbc_db_t;
typedef struct {
#ifdef ISC_PLATFORM_USETHREADS
db_list_t *db;
#else
dbinstance_t *db;
#endif
SQLHENV sql_env;
SQLCHAR *dsn;
SQLCHAR *user;
SQLCHAR *pass;
} odbc_instance_t;
static size_t
odbc_makesafe(char *to, const char *from, size_t length);
static SQLSMALLINT
safeLen(void *a) {
if (a == NULL)
return 0;
return strlen((char *) a);
}
static void
destroy_odbc_instance(odbc_instance_t *odbc_inst) {
#ifdef ISC_PLATFORM_USETHREADS
dbinstance_t *ndbi = NULL;
dbinstance_t *dbi = NULL;
ndbi = ISC_LIST_HEAD(*odbc_inst->db);
while (ndbi != NULL) {
dbi = ndbi;
ndbi = ISC_LIST_NEXT(dbi, link);
if (dbi->dbconn != NULL) {
if (((odbc_db_t *) (dbi->dbconn))->stmnt != NULL) {
SQLFreeHandle(SQL_HANDLE_STMT,
((odbc_db_t *)
(dbi->dbconn))->stmnt);
((odbc_db_t *) (dbi->dbconn))->stmnt = NULL;
}
if (((odbc_db_t *) (dbi->dbconn))->dbc != NULL) {
SQLDisconnect(((odbc_db_t *)
dbi->dbconn)->dbc);
SQLFreeHandle(SQL_HANDLE_DBC,
((odbc_db_t *)
(dbi->dbconn))->dbc);
((odbc_db_t *) (dbi->dbconn))->dbc = NULL;
}
isc_mem_free(ns_g_mctx, dbi->dbconn);
}
destroy_sqldbinstance(dbi);
}
isc_mem_put(ns_g_mctx, odbc_inst->db, sizeof(db_list_t));
#else
if (((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt != NULL) {
SQLFreeHandle(SQL_HANDLE_STMT,
((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt);
((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt = NULL;
}
if (((odbc_db_t *) (odbc_inst->db->dbconn))->dbc != NULL) {
SQLDisconnect(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc);
SQLFreeHandle(SQL_HANDLE_DBC,
((odbc_db_t *) (odbc_inst->db->dbconn))->dbc);
((odbc_db_t *) (odbc_inst->db->dbconn))->dbc = NULL;
}
if (((odbc_db_t *) odbc_inst->db->dbconn) != NULL) {
isc_mem_free(ns_g_mctx, odbc_inst->db->dbconn);
odbc_inst->db->dbconn = NULL;
}
if (odbc_inst->db != NULL)
destroy_sqldbinstance(odbc_inst->db);
#endif
if (odbc_inst->sql_env != NULL)
SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env);
if (odbc_inst->dsn != NULL)
isc_mem_free(ns_g_mctx, odbc_inst->dsn);
if (odbc_inst->pass != NULL)
isc_mem_free(ns_g_mctx, odbc_inst->pass);
if (odbc_inst->user != NULL)
isc_mem_free(ns_g_mctx, odbc_inst->user);
if (odbc_inst != NULL)
isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t));
}
static isc_result_t
odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc) {
odbc_db_t *ndb = *dbc;
SQLRETURN sqlRes;
isc_result_t result = ISC_R_SUCCESS;
if (ndb != NULL) {
if (ndb->stmnt != NULL) {
SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt);
ndb->stmnt = NULL;
}
if (ndb->dbc != NULL) {
SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc);
ndb->dbc = NULL;
}
} else {
ndb = isc_mem_allocate(ns_g_mctx, sizeof(odbc_db_t));
if (ndb == NULL) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to allocate memory");
return ISC_R_NOMEMORY;
}
memset(ndb, 0, sizeof(odbc_db_t));
}
sqlRes = SQLAllocHandle(SQL_HANDLE_DBC, dbi->sql_env, &(ndb->dbc));
if (!sqlOK(sqlRes)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to allocate memory");
result = ISC_R_NOMEMORY;
goto cleanup;
}
sqlRes = SQLConnect(ndb->dbc, dbi->dsn, safeLen(dbi->dsn), dbi->user,
safeLen(dbi->user), dbi->pass, safeLen(dbi->pass));
if (!sqlOK(sqlRes)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to connect");
result = ISC_R_FAILURE;
goto cleanup;
}
sqlRes = SQLAllocHandle(SQL_HANDLE_STMT, ndb->dbc, &(ndb->stmnt));
if (!sqlOK(sqlRes)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to allocate memory");
result = ISC_R_NOMEMORY;
goto cleanup;
}
*dbc = ndb;
return ISC_R_SUCCESS;
cleanup:
if (ndb != NULL) {
if (ndb->stmnt != NULL) {
SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt);
ndb->stmnt = NULL;
}
if (ndb->dbc != NULL) {
SQLDisconnect(ndb->dbc);
SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc);
ndb->dbc = NULL;
}
isc_mem_free(ns_g_mctx, ndb);
}
return result;
}
#ifdef ISC_PLATFORM_USETHREADS
static dbinstance_t *
odbc_find_avail_conn(db_list_t *dblist)
{
dbinstance_t *dbi = NULL;
dbinstance_t *head;
int count = 0;
head = dbi = ISC_LIST_HEAD(*dblist);
while (count < dbc_search_limit) {
if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
return dbi;
dbi = ISC_LIST_NEXT(dbi, link);
if (dbi == NULL) {
count++;
dbi = head;
}
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
"Odbc driver unable to find available "
"connection after searching %d times",
count);
return NULL;
}
#endif
static char *
odbc_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;
odbc_makesafe(outstr, instr, len);
return outstr;
}
static size_t
odbc_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 = '\\';
break;
case '\'':
*target = '\'';
target++;
*target = '\'';
break;
default:
*target = *source;
}
source++;
target++;
remaining--;
}
*target = '\0';
return target - to;
}
static isc_result_t
odbc_get_resultset(const char *zone, const char *record,
const char *client, unsigned int query,
void *dbdata, dbinstance_t **r_dbi)
{
isc_result_t result;
dbinstance_t *dbi = NULL;
char *querystring = NULL;
unsigned int j = 0;
SQLRETURN sqlRes;
REQUIRE(*r_dbi == NULL);
#ifdef ISC_PLATFORM_USETHREADS
dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db);
#else
dbi = (dbinstance_t *) ((odbc_instance_t *) dbdata)->db;
#endif
if (dbi == NULL) {
result = ISC_R_FAILURE;
goto cleanup;
}
switch(query) {
case ALLNODES:
if (dbi->allnodes_q == NULL) {
result = ISC_R_NOTIMPLEMENTED;
goto cleanup;
}
break;
case ALLOWXFR:
if (dbi->allowxfr_q == NULL) {
result = ISC_R_NOTIMPLEMENTED;
goto cleanup;
}
break;
case AUTHORITY:
if (dbi->authority_q == NULL) {
result = ISC_R_NOTIMPLEMENTED;
goto cleanup;
}
break;
case FINDZONE:
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:
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:
UNEXPECTED_ERROR(__FILE__, __LINE__,
"Incorrect query flag passed to "
"odbc_get_resultset");
result = ISC_R_UNEXPECTED;
goto cleanup;
}
if (zone != NULL) {
dbi->zone = odbc_escape_string(zone);
if (dbi->zone == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
} else {
dbi->zone = NULL;
}
if (record != NULL) {
dbi->record = odbc_escape_string(record);
if (dbi->record == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
} else {
dbi->record = NULL;
}
if (client != NULL) {
dbi->client = odbc_escape_string(client);
if (dbi->client == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
} else {
dbi->client = NULL;
}
switch(query) {
case ALLNODES:
querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
break;
case ALLOWXFR:
querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
break;
case AUTHORITY:
querystring = build_querystring(ns_g_mctx, dbi->authority_q);
break;
case FINDZONE:
querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
break;
case LOOKUP:
querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
break;
default:
UNEXPECTED_ERROR(__FILE__, __LINE__,
"Incorrect query flag passed to "
"odbc_get_resultset");
result = ISC_R_UNEXPECTED;
goto cleanup;
}
if (querystring == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
"\nQuery String: %s\n", querystring);
for (j=0; j < 3; j++) {
sqlRes = SQLExecDirect(((odbc_db_t *) dbi->dbconn)->stmnt,
(SQLCHAR *) querystring,
(SQLINTEGER) strlen(querystring));
if (!sqlOK(sqlRes)) {
SQLCloseCursor(((odbc_db_t *) dbi->dbconn)->stmnt);
result = odbc_connect((odbc_instance_t *) dbdata,
(odbc_db_t **) &(dbi->dbconn));
if (result != ISC_R_SUCCESS)
break;
result = ISC_R_FAILURE;
} else {
result = ISC_R_SUCCESS;
*r_dbi = dbi;
break;
}
}
cleanup:
if (dbi == NULL)
return ISC_R_FAILURE;
if (dbi->zone != NULL)
isc_mem_free(ns_g_mctx, dbi->zone);
if (dbi->record != NULL)
isc_mem_free(ns_g_mctx, dbi->record);
if (dbi->client != NULL)
isc_mem_free(ns_g_mctx, dbi->client);
#ifdef ISC_PLATFORM_USETHREADS
if (result != ISC_R_SUCCESS)
isc_mutex_unlock(&dbi->instance_lock);
#endif
if (querystring != NULL)
isc_mem_free(ns_g_mctx, querystring );
return result;
}
static isc_result_t
odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data) {
SQLLEN size;
REQUIRE(data != NULL && *data == NULL);
if (sqlOK(SQLColAttribute(stmnt, field, SQL_DESC_DISPLAY_SIZE,
NULL, 0, NULL, &size)) && size > 0) {
*data = isc_mem_allocate(ns_g_mctx, size + 1);
if (data != NULL) {
if (sqlOK(SQLGetData(stmnt, field, SQL_C_CHAR,
*data, size + 1,&size)))
return ISC_R_SUCCESS;
isc_mem_free(ns_g_mctx, *data);
}
}
return ISC_R_FAILURE;
}
static isc_result_t
odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField,
SQLSMALLINT endField, char **retData) {
isc_result_t result;
SQLLEN size;
int totSize = 0;
SQLSMALLINT i;
int j = 0;
char *data;
REQUIRE(retData != NULL && *retData == NULL);
REQUIRE(startField > 0 && startField <= endField);
for (i=startField; i <= endField; i++)
if (sqlOK(SQLColAttribute(stmnt, i, SQL_DESC_DISPLAY_SIZE,
NULL, 0, NULL, &size)) && size > 0) {
totSize += (size + 1);
}
if (totSize < 1)
return ISC_R_FAILURE;
data = isc_mem_allocate(ns_g_mctx, ++totSize);
if (data == NULL)
return ISC_R_NOMEMORY;
result = ISC_R_FAILURE;
for (i=startField; i <= endField; i++) {
if (sqlOK(SQLGetData(stmnt, i, SQL_C_CHAR, &(data[j]),
totSize - j, &size))) {
if (size > 0) {
j += size;
data[j++] = ' ';
data[j] = '\0';
result = ISC_R_SUCCESS;
}
} else {
isc_mem_free(ns_g_mctx, data);
return ISC_R_FAILURE;
}
}
if (result != ISC_R_SUCCESS) {
isc_mem_free(ns_g_mctx, data);
return result;
}
*retData = data;
return ISC_R_SUCCESS;
}
static isc_result_t
odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *dbi)
{
isc_result_t result;
SQLSMALLINT fields;
SQLHSTMT *stmnt;
char *ttl_s;
char *type;
char *data;
char *endp;
int ttl;
REQUIRE(dbi != NULL);
stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt;
if (!sqlOK(SQLNumResultCols(stmnt, &fields))) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to process result set");
result = ISC_R_FAILURE;
goto process_rs_cleanup;
}
result = ISC_R_FAILURE;
while (sqlOK(SQLFetch(stmnt))) {
data = type = ttl_s = NULL;
switch(fields) {
case 1:
if ((result = odbc_getField(stmnt, 1,
&data)) == ISC_R_SUCCESS) {
result = dns_sdlz_putrr(lookup, "a",
86400, data);
}
break;
case 2:
if ((result = odbc_getField(stmnt, 1,
&type)) == ISC_R_SUCCESS &&
(result = odbc_getField(stmnt, 2,
&data)) == ISC_R_SUCCESS) {
result = dns_sdlz_putrr(lookup, type,
86400, data);
}
break;
default:
if ((result = odbc_getField(stmnt, 1, &ttl_s))
== ISC_R_SUCCESS &&
(result = odbc_getField(stmnt, 2, &type))
== ISC_R_SUCCESS &&
(result = odbc_getManyFields(stmnt, 3,
fields, &data))
== ISC_R_SUCCESS) {
ttl = strtol(ttl_s, &endp, 10);
if (*endp != '\0' || ttl < 0) {
isc_log_write(dns_lctx,
DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ,
ISC_LOG_ERROR,
"Odbc driver ttl must "
"be a postive number");
result = ISC_R_FAILURE;
} else {
result = dns_sdlz_putrr(lookup, type,
ttl, data);
}
}
}
if (ttl_s != NULL)
isc_mem_free(ns_g_mctx, ttl_s);
if (type != NULL)
isc_mem_free(ns_g_mctx, type);
if (data != NULL)
isc_mem_free(ns_g_mctx, data);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"dns_sdlz_putrr returned error. "
"Error code was: %s",
isc_result_totext(result));
result = ISC_R_FAILURE;
goto process_rs_cleanup;
}
}
process_rs_cleanup:
SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
#ifdef ISC_PLATFORM_USETHREADS
isc_mutex_unlock(&dbi->instance_lock);
#endif
return result;
}
static isc_result_t
odbc_findzone(void *driverarg, void *dbdata, const char *name,
dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
{
isc_result_t result;
dbinstance_t *dbi = NULL;
UNUSED(driverarg);
UNUSED(methods);
UNUSED(clientinfo);
result = odbc_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &dbi);
if (result == ISC_R_SUCCESS &&
!sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) {
result = ISC_R_NOTFOUND;
}
if (dbi != NULL) {
SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
#ifdef ISC_PLATFORM_USETHREADS
isc_mutex_unlock(&dbi->instance_lock);
#endif
}
return result;
}
static isc_result_t
odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name,
const char *client)
{
isc_result_t result;
dbinstance_t *dbi = NULL;
UNUSED(driverarg);
result = odbc_findzone(driverarg, dbdata, name, NULL, NULL);
if (result != ISC_R_SUCCESS)
return (ISC_R_NOTFOUND);
result = odbc_get_resultset(name, NULL, client, ALLOWXFR,
dbdata, &dbi);
if (result == ISC_R_NOTIMPLEMENTED)
return result;
if (result == ISC_R_SUCCESS &&
!sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) {
result = ISC_R_NOPERM;
}
if (dbi != NULL) {
SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
#ifdef ISC_PLATFORM_USETHREADS
isc_mutex_unlock(&dbi->instance_lock);
#endif
}
return result;
}
static isc_result_t
odbc_allnodes(const char *zone, void *driverarg, void *dbdata,
dns_sdlzallnodes_t *allnodes)
{
isc_result_t result;
dbinstance_t *dbi = NULL;
SQLHSTMT *stmnt;
SQLSMALLINT fields;
char *data;
char *type;
char *ttl_s;
int ttl;
char *host;
char *endp;
UNUSED(driverarg);
result = odbc_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &dbi);
if (result == ISC_R_NOTIMPLEMENTED)
return result;
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to return "
"result set for all nodes query");
return (ISC_R_FAILURE);
}
stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt;
if (!sqlOK(SQLNumResultCols(stmnt, &fields))) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to process result set");
result = ISC_R_FAILURE;
goto allnodes_cleanup;
}
if (fields < 4) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver too few fields returned by "
"all nodes query");
result = ISC_R_FAILURE;
goto allnodes_cleanup;
}
result = ISC_R_FAILURE;
while (sqlOK(SQLFetch(stmnt))) {
data = host = type = ttl_s = NULL;
if ((result = odbc_getField(stmnt, 1,
&ttl_s)) == ISC_R_SUCCESS &&
(result = odbc_getField(stmnt, 2,
&type)) == ISC_R_SUCCESS &&
(result = odbc_getField(stmnt, 3,
&host)) == ISC_R_SUCCESS &&
(result = odbc_getManyFields(stmnt, 4, fields,
&data)) == ISC_R_SUCCESS) {
ttl = strtol(ttl_s, &endp, 10);
if (*endp != '\0' || ttl < 0) {
isc_log_write(dns_lctx,
DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver ttl must be "
"a postive number");
result = ISC_R_FAILURE;
} else {
result = dns_sdlz_putnamedrr(allnodes, host,
type, ttl, data);
}
}
if (ttl_s != NULL)
isc_mem_free(ns_g_mctx, ttl_s);
if (type != NULL)
isc_mem_free(ns_g_mctx, type);
if (host != NULL)
isc_mem_free(ns_g_mctx, host);
if (data != NULL)
isc_mem_free(ns_g_mctx, data);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"dns_sdlz_putnamedrr returned error. "
"Error code was: %s",
isc_result_totext(result));
result = ISC_R_FAILURE;
goto allnodes_cleanup;
}
}
allnodes_cleanup:
SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
#ifdef ISC_PLATFORM_USETHREADS
isc_mutex_unlock(&dbi->instance_lock);
#endif
return result;
}
static isc_result_t
odbc_authority(const char *zone, void *driverarg, void *dbdata,
dns_sdlzlookup_t *lookup)
{
isc_result_t result;
dbinstance_t *dbi = NULL;
UNUSED(driverarg);
result = odbc_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &dbi);
if (result == ISC_R_NOTIMPLEMENTED)
return result;
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to return "
"result set for authority query");
return (ISC_R_FAILURE);
}
return odbc_process_rs(lookup, dbi);
}
static isc_result_t
odbc_lookup(const char *zone, const char *name, void *driverarg,
void *dbdata, dns_sdlzlookup_t *lookup,
dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo)
{
isc_result_t result;
dbinstance_t *dbi = NULL;
UNUSED(driverarg);
UNUSED(methods);
UNUSED(clientinfo);
result = odbc_get_resultset(zone, name, NULL, LOOKUP, dbdata, &dbi);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver unable to return "
"result set for lookup query");
return (ISC_R_FAILURE);
}
return odbc_process_rs(lookup, dbi);
}
static isc_result_t
odbc_create(const char *dlzname, unsigned int argc, char *argv[],
void *driverarg, void **dbdata)
{
isc_result_t result;
odbc_instance_t *odbc_inst = NULL;
dbinstance_t *db = NULL;
SQLRETURN sqlRes;
#ifdef ISC_PLATFORM_USETHREADS
int dbcount;
int i;
char *endp;
#endif
UNUSED(dlzname);
UNUSED(driverarg);
#ifdef ISC_PLATFORM_USETHREADS
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
"Odbc driver running multithreaded");
#else
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
"Odbc driver running single threaded");
#endif
if (argc < 5) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver requires at least "
"4 command line args.");
return (ISC_R_FAILURE);
}
if (argc > 8) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver cannot accept more than "
"7 command line args.");
return (ISC_R_FAILURE);
}
#ifdef ISC_PLATFORM_USETHREADS
dbcount = strtol(argv[1], &endp, 10);
if (*endp != '\0' || dbcount < 0) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver database connection count "
"must be positive.");
return (ISC_R_FAILURE);
}
#endif
odbc_inst = isc_mem_get(ns_g_mctx, sizeof(odbc_instance_t));
if (odbc_inst == NULL)
return (ISC_R_NOMEMORY);
memset(odbc_inst, 0, sizeof(odbc_instance_t));
odbc_inst->dsn = (SQLCHAR *) getParameterValue(argv[2],
"dsn=");
if (odbc_inst->dsn == NULL) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"odbc driver requires a dns parameter.");
result = ISC_R_FAILURE;
goto cleanup;
}
odbc_inst->user = (SQLCHAR *) getParameterValue(argv[2],
"user=");
odbc_inst->pass = (SQLCHAR *) getParameterValue(argv[2], "pass=");
if (odbc_inst->sql_env == NULL) {
sqlRes = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,
&(odbc_inst->sql_env));
if (!sqlOK(sqlRes)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
"Odbc driver unable to allocate memory");
result = ISC_R_NOMEMORY;
goto cleanup;
}
sqlRes = SQLSetEnvAttr(odbc_inst->sql_env,
SQL_ATTR_ODBC_VERSION,
(void *) SQL_OV_ODBC3, 0);
if (!sqlOK(sqlRes)) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
"Unable to configure ODBC environment");
result = ISC_R_NOMEMORY;
goto cleanup;
}
}
#ifdef ISC_PLATFORM_USETHREADS
odbc_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
if (odbc_inst->db == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
ISC_LIST_INIT(*odbc_inst->db);
for (i=0; i < dbcount; i++) {
#endif
switch(argc) {
case 5:
result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
NULL, argv[3], argv[4],
NULL, &db);
break;
case 6:
result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
argv[5], argv[3], argv[4],
NULL, &db);
break;
case 7:
result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
argv[5], argv[3], argv[4],
NULL, &db);
break;
case 8:
result = build_sqldbinstance(ns_g_mctx, argv[6],
argv[7], argv[5], argv[3],
argv[4], NULL, &db);
break;
default:
result = ISC_R_FAILURE;
}
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver could not create "
"database instance object.");
goto cleanup;
}
#ifdef ISC_PLATFORM_USETHREADS
ISC_LINK_INIT(db, link);
ISC_LIST_APPEND(*odbc_inst->db, db, link);
#endif
result = odbc_connect(odbc_inst, (odbc_db_t **) &(db->dbconn));
if (result != ISC_R_SUCCESS) {
#ifdef ISC_PLATFORM_USETHREADS
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver failed to create database "
"connection number %u after 3 attempts",
i+1);
#else
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
"Odbc driver failed to create database "
"connection after 3 attempts");
#endif
goto cleanup;
}
#ifdef ISC_PLATFORM_USETHREADS
db = NULL;
}
#else
odbc_inst->db = db;
#endif
*dbdata = odbc_inst;
return(ISC_R_SUCCESS);
cleanup:
destroy_odbc_instance(odbc_inst);
return result;
}
static void
odbc_destroy(void *driverarg, void *dbdata)
{
UNUSED(driverarg);
destroy_odbc_instance((odbc_instance_t *) dbdata);
}
static dns_sdlzmethods_t dlz_odbc_methods = {
odbc_create,
odbc_destroy,
odbc_findzone,
odbc_lookup,
odbc_authority,
odbc_allnodes,
odbc_allowzonexfr,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
isc_result_t
dlz_odbc_init(void) {
isc_result_t result;
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Registering DLZ odbc driver.");
result = dns_sdlzregister("odbc", &dlz_odbc_methods, NULL,
DNS_SDLZFLAG_RELATIVEOWNER |
DNS_SDLZFLAG_RELATIVERDATA |
DNS_SDLZFLAG_THREADSAFE,
ns_g_mctx, &dlz_odbc);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"dns_sdlzregister() failed: %s",
isc_result_totext(result));
result = ISC_R_UNEXPECTED;
}
return result;
}
void
dlz_odbc_clear(void) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Unregistering DLZ odbc driver.");
if (dlz_odbc != NULL)
dns_sdlzunregister(&dlz_odbc);
}
#endif