aboutsummaryrefslogtreecommitdiffstats
path: root/cdr
diff options
context:
space:
mode:
authorseanbright <seanbright@f38db490-d61c-443f-a65b-d21fe96a405b>2008-07-02 14:29:18 +0000
committerseanbright <seanbright@f38db490-d61c-443f-a65b-d21fe96a405b>2008-07-02 14:29:18 +0000
commit5ef82c39cc5d6fa4ef4ba38ea6579bb9501f4e9d (patch)
tree55e28879fe206ceeae989659ff739a7b63216472 /cdr
parent8a056288871c69090e70433c14b2ca507be459a7 (diff)
Merged revisions 126226,126513 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ........ r126226 | seanbright | 2008-06-28 17:28:16 -0400 (Sat, 28 Jun 2008) | 8 lines Merge in changes from my cdr-tds-conversion branch. This changes the internal implementation from using the volatile libtds, to using the db-lib front end. The unintended side effect of this is that we support (at least) versions 0.62 through 0.82 of the FreeTDS distribution without any #ifdef ugliness. (closes issue #12844) Reported by: jcollie ........ r126513 | seanbright | 2008-06-30 07:57:42 -0400 (Mon, 30 Jun 2008) | 4 lines Cast a few more strings to char *, so that we can compile cleanly against FreeTDS 0.60. Update the docs to reflect that we can now compile and run against all modern releases of FreeTDS (0.60 through 0.82) ........ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.0@127397 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'cdr')
-rw-r--r--cdr/cdr_tds.c464
1 files changed, 230 insertions, 234 deletions
diff --git a/cdr/cdr_tds.c b/cdr/cdr_tds.c
index 9d322fd27..9688c167c 100644
--- a/cdr/cdr_tds.c
+++ b/cdr/cdr_tds.c
@@ -66,72 +66,76 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <time.h>
#include <math.h>
-#include <tds.h>
-#include <tdsconvert.h>
-#include <ctype.h>
-
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
-#ifdef FREETDS_PRE_0_62
-#warning "You have older TDS, you should upgrade!"
-#endif
+#include <sqlfront.h>
+#include <sybdb.h>
#define DATE_FORMAT "%Y/%m/%d %T"
-static char *name = "mssql";
+static char *name = "FreeTDS (MSSQL)";
static char *config = "cdr_tds.conf";
-static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *charset = NULL, *language = NULL;
-static char *table = NULL;
-
-static int connected = 0;
+struct cdr_tds_config {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(hostname);
+ AST_STRING_FIELD(database);
+ AST_STRING_FIELD(username);
+ AST_STRING_FIELD(password);
+ AST_STRING_FIELD(table);
+ AST_STRING_FIELD(charset);
+ AST_STRING_FIELD(language);
+ );
+ DBPROCESS *dbproc;
+ unsigned int connected:1;
+};
AST_MUTEX_DEFINE_STATIC(tds_lock);
-static TDSSOCKET *tds;
-static TDSLOGIN *login;
-static TDSCONTEXT *context;
+static struct cdr_tds_config *settings;
static char *anti_injection(const char *, int);
-static void get_date(char *, struct timeval);
+static void get_date(char *, size_t len, struct timeval);
static int mssql_connect(void);
static int mssql_disconnect(void);
static int tds_log(struct ast_cdr *cdr)
{
- char sqlcmd[2048], start[80], answer[80], end[80];
+ char start[80], answer[80], end[80];
char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid;
- int res = 0;
- int retried = 0;
-#ifdef FREETDS_PRE_0_62
- TDS_INT result_type;
-#endif
+ RETCODE erc;
+ int res = -1;
+
+ accountcode = anti_injection(cdr->accountcode, 20);
+ src = anti_injection(cdr->src, 80);
+ dst = anti_injection(cdr->dst, 80);
+ dcontext = anti_injection(cdr->dcontext, 80);
+ clid = anti_injection(cdr->clid, 80);
+ channel = anti_injection(cdr->channel, 80);
+ dstchannel = anti_injection(cdr->dstchannel, 80);
+ lastapp = anti_injection(cdr->lastapp, 80);
+ lastdata = anti_injection(cdr->lastdata, 80);
+ uniqueid = anti_injection(cdr->uniqueid, 32);
+
+ get_date(start, sizeof(start), cdr->start);
+ get_date(answer, sizeof(answer), cdr->answer);
+ get_date(end, sizeof(end), cdr->end);
ast_mutex_lock(&tds_lock);
- memset(sqlcmd, 0, 2048);
+ /* Ensure that we are connected */
+ if (!settings->connected) {
+ if (mssql_connect()) {
+ /* Connect failed */
+ goto done;
+ }
+ }
- accountcode = anti_injection(cdr->accountcode, 20);
- src = anti_injection(cdr->src, 80);
- dst = anti_injection(cdr->dst, 80);
- dcontext = anti_injection(cdr->dcontext, 80);
- clid = anti_injection(cdr->clid, 80);
- channel = anti_injection(cdr->channel, 80);
- dstchannel = anti_injection(cdr->dstchannel, 80);
- lastapp = anti_injection(cdr->lastapp, 80);
- lastdata = anti_injection(cdr->lastdata, 80);
- uniqueid = anti_injection(cdr->uniqueid, 32);
-
- get_date(start, cdr->start);
- get_date(answer, cdr->answer);
- get_date(end, cdr->end);
-
- sprintf(
- sqlcmd,
+ erc = dbfcmd(settings->dbproc,
"INSERT INTO %s "
"("
"accountcode, "
@@ -172,7 +176,7 @@ static int tds_log(struct ast_cdr *cdr)
"'%s', " /* amaflags */
"'%s'" /* uniqueid */
")",
- table,
+ settings->table,
accountcode,
src,
dst,
@@ -192,27 +196,26 @@ static int tds_log(struct ast_cdr *cdr)
uniqueid
);
- do {
- if (!connected) {
- if (mssql_connect())
- ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n");
- else
- ast_log(LOG_WARNING, "Reconnected to SQL database.\n");
+ if (erc == FAIL) {
+ ast_log(LOG_ERROR, "Failed to build INSERT statement, no CDR was logged.\n");
+ goto done;
+ }
- retried = 1; /* note that we have now tried */
- }
+ if (dbsqlexec(settings->dbproc) == FAIL) {
+ ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CDR was logged.\n");
+ goto done;
+ }
-#ifdef FREETDS_PRE_0_62
- if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
-#else
- if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
-#endif
- {
- ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
+ /* Consume any results we might get back (this is more of a sanity check than
+ * anything else, since an INSERT shouldn't return results). */
+ while (dbresults(settings->dbproc) != NO_MORE_RESULTS) {
+ while (dbnextrow(settings->dbproc) != NO_MORE_ROWS);
+ }
- mssql_disconnect(); /* this is ok even if we are already disconnected */
- }
- } while (!connected && !retried);
+ res = 0;
+
+done:
+ ast_mutex_unlock(&tds_lock);
ast_free(accountcode);
ast_free(src);
@@ -225,285 +228,252 @@ static int tds_log(struct ast_cdr *cdr)
ast_free(lastdata);
ast_free(uniqueid);
- ast_mutex_unlock(&tds_lock);
-
- return res;
+ return 0;
}
static char *anti_injection(const char *str, int len)
{
/* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
-
char *buf;
char *buf_ptr, *srh_ptr;
char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
int idx;
- if ((buf = ast_malloc(len + 1)) == NULL)
- {
- ast_log(LOG_ERROR, "cdr_tds: Out of memory error\n");
+ if (!(buf = ast_calloc(1, len + 1))) {
+ ast_log(LOG_ERROR, "Out of memory\n");
return NULL;
}
- memset(buf, 0, len);
buf_ptr = buf;
/* Escape single quotes */
- for (; *str && strlen(buf) < len; str++)
- {
- if (*str == '\'')
+ for (; *str && strlen(buf) < len; str++) {
+ if (*str == '\'') {
*buf_ptr++ = '\'';
+ }
*buf_ptr++ = *str;
}
*buf_ptr = '\0';
/* Erase known bad input */
- for (idx=0; *known_bad[idx]; idx++)
- {
- while((srh_ptr = strcasestr(buf, known_bad[idx])))
- {
- memmove(srh_ptr, srh_ptr+strlen(known_bad[idx]), strlen(srh_ptr+strlen(known_bad[idx]))+1);
+ for (idx = 0; *known_bad[idx]; idx++) {
+ while ((srh_ptr = strcasestr(buf, known_bad[idx]))) {
+ memmove(srh_ptr, srh_ptr + strlen(known_bad[idx]), strlen(srh_ptr + strlen(known_bad[idx])) + 1);
}
}
return buf;
}
-static void get_date(char *dateField, struct timeval tv)
+static void get_date(char *dateField, size_t len, struct timeval tv)
{
- struct ast_tm tm;
- char buf[80];
-
/* To make sure we have date variable if not insert null to SQL */
- if (!ast_tvzero(tv))
- {
+ if (!ast_tvzero(tv)) {
+ struct ast_tm tm;
ast_localtime(&tv, &tm, NULL);
- ast_strftime(buf, 80, DATE_FORMAT, &tm);
- sprintf(dateField, "'%s'", buf);
- }
- else
- {
- strcpy(dateField, "null");
+ ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm);
+ } else {
+ ast_copy_string(dateField, "null", len);
}
}
static int mssql_disconnect(void)
{
- if (tds) {
- tds_free_socket(tds);
- tds = NULL;
- }
-
- if (context) {
- tds_free_context(context);
- context = NULL;
+ if (settings->dbproc) {
+ dbclose(settings->dbproc);
+ settings->dbproc = NULL;
}
- if (login) {
- tds_free_login(login);
- login = NULL;
- }
-
- connected = 0;
+ settings->connected = 0;
return 0;
}
static int mssql_connect(void)
{
-#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
- TDSCONNECTION *connection = NULL;
-#else
- TDSCONNECTINFO *connection = NULL;
-#endif
- char query[128];
-
- /* Connect to M$SQL Server */
- if (!(login = tds_alloc_login()))
- {
- ast_log(LOG_ERROR, "tds_alloc_login() failed.\n");
+ LOGINREC *login;
+
+ if ((login = dblogin()) == NULL) {
+ ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n");
return -1;
}
-
- tds_set_server(login, hostname);
- tds_set_user(login, dbuser);
- tds_set_passwd(login, password);
- tds_set_app(login, "TSQL");
- tds_set_library(login, "TDS-Library");
-#ifndef FREETDS_PRE_0_62
- tds_set_client_charset(login, charset);
-#endif
- tds_set_language(login, language);
- tds_set_packet(login, 512);
- tds_set_version(login, 7, 0);
-
-#ifdef FREETDS_0_64
- if (!(context = tds_alloc_context(NULL)))
-#else
- if (!(context = tds_alloc_context()))
-#endif
- {
- ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
- goto connect_fail;
+
+ DBSETLAPP(login, "TSQL");
+ DBSETLUSER(login, (char *) settings->username);
+ DBSETLPWD(login, (char *) settings->password);
+ DBSETLCHARSET(login, (char *) settings->charset);
+ DBSETLNATLANG(login, (char *) settings->language);
+
+ if ((settings->dbproc = dbopen(login, (char *) settings->hostname)) == NULL) {
+ ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->hostname);
+ dbloginfree(login);
+ return -1;
}
- if (!(tds = tds_alloc_socket(context, 512))) {
- ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
- goto connect_fail;
+ dbloginfree(login);
+
+ if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) {
+ ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database);
+ goto failed;
}
- tds_set_parent(tds, NULL);
- connection = tds_read_config_info(tds, login, context->locale);
- if (!connection)
- {
- ast_log(LOG_ERROR, "tds_read_config() failed.\n");
- goto connect_fail;
+ if (dbfcmd(settings->dbproc, "SELECT 1 FROM [%s]", settings->table) == FAIL) {
+ ast_log(LOG_ERROR, "Unable to build query while verifying the existence of table '%s'\n", settings->table);
+ goto failed;
}
- if (tds_connect(tds, connection) == TDS_FAIL)
- {
- ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n");
- tds = NULL; /* freed by tds_connect() on error */
-#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
- tds_free_connection(connection);
-#else
- tds_free_connect(connection);
-#endif
- connection = NULL;
- goto connect_fail;
+ if (dbsqlexec(settings->dbproc) == FAIL) {
+ ast_log(LOG_ERROR, "Unable to verify existence of table '%s'\n", settings->table);
+ goto failed;
}
-#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
- tds_free_connection(connection);
-#else
- tds_free_connect(connection);
-#endif
- connection = NULL;
-
- sprintf(query, "USE %s", dbname);
-#ifdef FREETDS_PRE_0_62
- if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
-#else
- if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
-#endif
- {
- ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname);
- goto connect_fail;
+
+ /* Consume the result set (we don't really care about the result, though) */
+ while (dbresults(settings->dbproc) != NO_MORE_RESULTS) {
+ while (dbnextrow(settings->dbproc) != NO_MORE_ROWS);
}
- connected = 1;
+ settings->connected = 1;
+
return 0;
-connect_fail:
- mssql_disconnect();
+failed:
+ dbclose(settings->dbproc);
+ settings->dbproc = NULL;
return -1;
}
static int tds_unload_module(void)
{
- mssql_disconnect();
+ if (settings) {
+ ast_mutex_lock(&tds_lock);
+ mssql_disconnect();
+ ast_mutex_unlock(&tds_lock);
+
+ ast_string_field_free_memory(settings);
+ ast_free(settings);
+ }
ast_cdr_unregister(name);
- if (hostname) ast_free(hostname);
- if (dbname) ast_free(dbname);
- if (dbuser) ast_free(dbuser);
- if (password) ast_free(password);
- if (charset) ast_free(charset);
- if (language) ast_free(language);
- if (table) ast_free(table);
+ dbexit();
+
+ return 0;
+}
+
+static int tds_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
+{
+ ast_log(LOG_ERROR, "%s (%d)\n", dberrstr, dberr);
+
+ if (oserr != DBNOERR) {
+ ast_log(LOG_ERROR, "%s (%d)\n", oserrstr, oserr);
+ }
+
+ return INT_CANCEL;
+}
+
+static int tds_message_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
+{
+ ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line);
+ ast_log(LOG_NOTICE, "%s\n", msgtext);
return 0;
}
static int tds_load_module(int reload)
{
- int res = 0;
struct ast_config *cfg;
- struct ast_variable *var;
const char *ptr = NULL;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-#ifdef FREETDS_PRE_0_62
- TDS_INT result_type;
-#endif
cfg = ast_config_load(config, config_flags);
if (!cfg) {
- ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDR's: %s\n", config);
+ ast_log(LOG_NOTICE, "Unable to load TDS config for CDRs: %s\n", config);
return 0;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
return 0;
- var = ast_variable_browse(cfg, "global");
- if (!var) /* nothing configured */ {
+ if (!ast_variable_browse(cfg, "global")) {
+ /* nothing configured */
ast_config_destroy(cfg);
return 0;
}
-
+
+ ast_mutex_lock(&tds_lock);
+
+ /* Clear out any existing settings */
+ ast_string_field_init(settings, 0);
+
ptr = ast_variable_retrieve(cfg, "global", "hostname");
if (ptr) {
- if (hostname)
- ast_free(hostname);
- hostname = ast_strdup(ptr);
- } else
- ast_log(LOG_ERROR, "Database server hostname not specified.\n");
+ ast_string_field_set(settings, hostname, ptr);
+ } else {
+ ast_log(LOG_ERROR, "Failed to connect: Database server hostname not specified.\n");
+ goto failed;
+ }
ptr = ast_variable_retrieve(cfg, "global", "dbname");
if (ptr) {
- if (dbname)
- ast_free(dbname);
- dbname = ast_strdup(ptr);
- } else
- ast_log(LOG_ERROR, "Database dbname not specified.\n");
+ ast_string_field_set(settings, database, ptr);
+ } else {
+ ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n");
+ goto failed;
+ }
ptr = ast_variable_retrieve(cfg, "global", "user");
if (ptr) {
- if (dbuser)
- ast_free(dbuser);
- dbuser = ast_strdup(ptr);
- } else
- ast_log(LOG_ERROR, "Database dbuser not specified.\n");
+ ast_string_field_set(settings, username, ptr);
+ } else {
+ ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n");
+ goto failed;
+ }
ptr = ast_variable_retrieve(cfg, "global", "password");
if (ptr) {
- if (password)
- ast_free(password);
- password = ast_strdup(ptr);
- } else
- ast_log(LOG_ERROR,"Database password not specified.\n");
+ ast_string_field_set(settings, password, ptr);
+ } else {
+ ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n");
+ goto failed;
+ }
ptr = ast_variable_retrieve(cfg, "global", "charset");
- if (charset)
- ast_free(charset);
- if (ptr)
- charset = ast_strdup(ptr);
- else
- charset = ast_strdup("iso_1");
-
- if (language)
- ast_free(language);
+ if (ptr) {
+ ast_string_field_set(settings, charset, ptr);
+ } else {
+ ast_string_field_set(settings, charset, "iso_1");
+ }
+
ptr = ast_variable_retrieve(cfg, "global", "language");
- if (ptr)
- language = ast_strdup(ptr);
- else
- language = ast_strdup("us_english");
+ if (ptr) {
+ ast_string_field_set(settings, language, ptr);
+ } else {
+ ast_string_field_set(settings, language, "us_english");
+ }
ptr = ast_variable_retrieve(cfg, "global", "table");
- if (ptr == NULL) {
- ast_debug(1, "cdr_tds: table not specified. Assuming cdr\n");
- ptr = "cdr";
+ if (ptr) {
+ ast_string_field_set(settings, table, ptr);
+ } else {
+ ast_log(LOG_NOTICE, "Table name not specified, using 'cdr' by default.\n");
+ ast_string_field_set(settings, table, "cdr");
+ }
+
+ mssql_disconnect();
+
+ if (mssql_connect()) {
+ /* We failed to connect (mssql_connect takes care of logging it) */
+ goto failed;
}
- if (table)
- ast_free(table);
- table = ast_strdup(ptr);
+ ast_mutex_unlock(&tds_lock);
ast_config_destroy(cfg);
- ast_mutex_lock(&tds_lock);
- mssql_disconnect();
- mssql_connect();
+ return 1;
+
+failed:
ast_mutex_unlock(&tds_lock);
+ ast_config_destroy(cfg);
- return res;
+ return 0;
}
static int reload(void)
@@ -513,9 +483,35 @@ static int reload(void)
static int load_module(void)
{
- if (!tds_load_module(0))
+ if (dbinit() == FAIL) {
+ ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n");
return AST_MODULE_LOAD_DECLINE;
+ }
+
+ dberrhandle(tds_error_handler);
+ dbmsghandle(tds_message_handler);
+
+ settings = ast_calloc(1, sizeof(*settings));
+
+ if (!settings || ast_string_field_init(settings, 256)) {
+ if (settings) {
+ ast_free(settings);
+ settings = NULL;
+ }
+ dbexit();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!tds_load_module(0)) {
+ ast_string_field_free_memory(settings);
+ ast_free(settings);
+ settings = NULL;
+ dbexit();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
ast_cdr_register(name, ast_module_info->description, tds_log);
+
return AST_MODULE_LOAD_SUCCESS;
}
@@ -524,7 +520,7 @@ static int unload_module(void)
return tds_unload_module();
}
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MSSQL CDR Backend",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "FreeTDS CDR Backend",
.load = load_module,
.unload = unload_module,
.reload = reload,