dlz_odbc_driver.c
来自「非常好的dns解析软件」· C语言 代码 · 共 1,569 行 · 第 1/3 页
C
1,569 行
/* * 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_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)/* * Private Structures *//* * structure to hold ODBC connection & statement */typedef struct{ SQLHDBC dbc; SQLHSTMT stmnt;} odbc_db_t;/* * Structure to hold everthing needed by this "instance" of the odbc driver * remember, the driver code is only loaded once, but may have many separate * instances */typedef struct {#ifdef ISC_PLATFORM_USETHREADS db_list_t *db; /* handle to a list of DB */#else dbinstance_t *db; /* handle to db */#endif SQLHENV sql_env; /* handle to SQL environment */ SQLCHAR *dsn; SQLCHAR *user; SQLCHAR *pass;} odbc_instance_t;/* forward reference */static size_todbc_makesafe(char *to, const char *from, size_t length);/* * Private methods */static SQLSMALLINTsafeLen(void *a) { if (a == NULL) return 0; return strlen((char *) a);}/*% propertly cleans up an odbc_instance_t */static voiddestroy_odbc_instance(odbc_instance_t *odbc_inst) {#ifdef ISC_PLATFORM_USETHREADS dbinstance_t *ndbi = NULL; dbinstance_t *dbi = NULL; /* get the first DBI in the list */ ndbi = ISC_LIST_HEAD(*odbc_inst->db); /* loop through the list */ while (ndbi != NULL) { dbi = ndbi; /* get the next DBI in the list */ ndbi = ISC_LIST_NEXT(dbi, link); /* if we have a connection / statement object in memory */ if (dbi->dbconn != NULL) { /* free statement handle */ 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; } /* disconnect from database & free connection handle */ 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; } /* free memory that held connection & statement. */ isc_mem_free(ns_g_mctx, dbi->dbconn); } /* release all memory that comprised a DBI */ destroy_sqldbinstance(dbi); } /* release memory for the list structure */ isc_mem_put(ns_g_mctx, odbc_inst->db, sizeof(db_list_t));#else /* ISC_PLATFORM_USETHREADS */ /* free statement handle */ 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; } /* disconnect from database, free connection handle */ 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; } /* free mem for the odbc_db_t structure held in db */ 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 /* ISC_PLATFORM_USETHREADS */ /* free sql environment */ if (odbc_inst->sql_env != NULL) SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env); /* free ODBC instance strings */ 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); /* free memory for odbc_inst */ if (odbc_inst != NULL) isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t));}/*% Connects to database, and creates ODBC statements */static isc_result_todbc_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 db != null, we have to do some cleanup * if statement handle != null free it */ if (ndb->stmnt != NULL) { SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); ndb->stmnt = NULL; } /* if connection handle != null free it */ 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 statement handle != null free it */ if (ndb->stmnt != NULL) { SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt); ndb->stmnt = NULL; } /* if connection handle != null free it */ if (ndb->dbc != NULL) { SQLDisconnect(ndb->dbc); SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc); ndb->dbc = NULL; } /* free memory holding ndb */ isc_mem_free(ns_g_mctx, ndb); } return result;}/*% * 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. */#ifdef ISC_PLATFORM_USETHREADSstatic dbinstance_t *odbc_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, "Odbc 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 *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;}/* --------------- * 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 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_todbc_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;}/*% * 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. The data base instance that is used is returned * to the caller so they can get the data from the result set from it. * If successfull, it will be the responsibility of the caller to close * the cursor, and unlock the mutex of the DBI when they are done with it. * If not successfull, this function will perform all the cleanup. */static isc_result_todbc_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); /* get db instance / connection */#ifdef ISC_PLATFORM_USETHREADS /* find an available DBI from the list */ dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db);#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 *) ((odbc_instance_t *) dbdata)->db;#endif /* ISC_PLATFORM_USETHREADS */ /* 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!!! */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?