|
|
|
@ -84,16 +84,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
|
static char *name = "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;
|
|
|
|
|
|
|
|
|
|
AST_MUTEX_DEFINE_STATIC(tds_lock);
|
|
|
|
|
struct cdr_tds_config {
|
|
|
|
|
AST_DECLARE_STRING_FIELDS(
|
|
|
|
|
AST_STRING_FIELD(hostname);
|
|
|
|
|
AST_STRING_FIELD(dbname);
|
|
|
|
|
AST_STRING_FIELD(dbuser);
|
|
|
|
|
AST_STRING_FIELD(password);
|
|
|
|
|
AST_STRING_FIELD(table);
|
|
|
|
|
AST_STRING_FIELD(charset);
|
|
|
|
|
AST_STRING_FIELD(language);
|
|
|
|
|
);
|
|
|
|
|
TDSSOCKET *tds;
|
|
|
|
|
TDSLOGIN *login;
|
|
|
|
|
TDSCONTEXT *context;
|
|
|
|
|
unsigned int connected:1;
|
|
|
|
|
ast_mutex_t 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);
|
|
|
|
@ -111,7 +119,7 @@ static int tds_log(struct ast_cdr *cdr)
|
|
|
|
|
TDS_INT result_type;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
ast_mutex_lock(&tds_lock);
|
|
|
|
|
ast_mutex_lock(&settings->lock);
|
|
|
|
|
|
|
|
|
|
memset(sqlcmd, 0, 2048);
|
|
|
|
|
|
|
|
|
@ -172,7 +180,7 @@ static int tds_log(struct ast_cdr *cdr)
|
|
|
|
|
"'%s', " /* amaflags */
|
|
|
|
|
"'%s'" /* uniqueid */
|
|
|
|
|
")",
|
|
|
|
|
table,
|
|
|
|
|
settings->table,
|
|
|
|
|
accountcode,
|
|
|
|
|
src,
|
|
|
|
|
dst,
|
|
|
|
@ -193,7 +201,7 @@ static int tds_log(struct ast_cdr *cdr)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if (!connected) {
|
|
|
|
|
if (!settings->connected) {
|
|
|
|
|
if (mssql_connect())
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n");
|
|
|
|
|
else
|
|
|
|
@ -203,16 +211,16 @@ static int tds_log(struct ast_cdr *cdr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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))
|
|
|
|
|
if (!settings->connected || (tds_submit_query(settings->tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(settings->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))
|
|
|
|
|
if (!settings->connected || (tds_submit_query(settings->tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(settings->tds) != TDS_SUCCEED))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
|
|
|
|
|
|
|
|
|
|
mssql_disconnect(); /* this is ok even if we are already disconnected */
|
|
|
|
|
}
|
|
|
|
|
} while (!connected && !retried);
|
|
|
|
|
} while (!settings->connected && !retried);
|
|
|
|
|
|
|
|
|
|
ast_free(accountcode);
|
|
|
|
|
ast_free(src);
|
|
|
|
@ -225,7 +233,7 @@ static int tds_log(struct ast_cdr *cdr)
|
|
|
|
|
ast_free(lastdata);
|
|
|
|
|
ast_free(uniqueid);
|
|
|
|
|
|
|
|
|
|
ast_mutex_unlock(&tds_lock);
|
|
|
|
|
ast_mutex_unlock(&settings->lock);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
@ -233,14 +241,13 @@ static int tds_log(struct ast_cdr *cdr)
|
|
|
|
|
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_calloc(1, len + 1))) {
|
|
|
|
|
ast_log(LOG_ERROR, "cdr_tds: Out of memory error\n");
|
|
|
|
|
ast_log(LOG_ERROR, "Out of memory\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -282,22 +289,22 @@ static void get_date(char *dateField, struct timeval tv)
|
|
|
|
|
|
|
|
|
|
static int mssql_disconnect(void)
|
|
|
|
|
{
|
|
|
|
|
if (tds) {
|
|
|
|
|
tds_free_socket(tds);
|
|
|
|
|
tds = NULL;
|
|
|
|
|
if (settings->tds) {
|
|
|
|
|
tds_free_socket(settings->tds);
|
|
|
|
|
settings->tds = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context) {
|
|
|
|
|
tds_free_context(context);
|
|
|
|
|
context = NULL;
|
|
|
|
|
if (settings->context) {
|
|
|
|
|
tds_free_context(settings->context);
|
|
|
|
|
settings->context = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (login) {
|
|
|
|
|
tds_free_login(login);
|
|
|
|
|
login = NULL;
|
|
|
|
|
if (settings->login) {
|
|
|
|
|
tds_free_login(settings->login);
|
|
|
|
|
settings->login = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connected = 0;
|
|
|
|
|
settings->connected = 0;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -312,48 +319,48 @@ static int mssql_connect(void)
|
|
|
|
|
char query[128];
|
|
|
|
|
|
|
|
|
|
/* Connect to M$SQL Server */
|
|
|
|
|
if (!(login = tds_alloc_login())) {
|
|
|
|
|
if (!(settings->login = tds_alloc_login())) {
|
|
|
|
|
ast_log(LOG_ERROR, "tds_alloc_login() failed.\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");
|
|
|
|
|
tds_set_server(settings->login, settings->hostname);
|
|
|
|
|
tds_set_user(settings->login, settings->dbuser);
|
|
|
|
|
tds_set_passwd(settings->login, settings->password);
|
|
|
|
|
tds_set_app(settings->login, "TSQL");
|
|
|
|
|
tds_set_library(settings->login, "TDS-Library");
|
|
|
|
|
#ifndef FREETDS_PRE_0_62
|
|
|
|
|
tds_set_client_charset(login, charset);
|
|
|
|
|
tds_set_client_charset(settings->login, settings->charset);
|
|
|
|
|
#endif
|
|
|
|
|
tds_set_language(login, language);
|
|
|
|
|
tds_set_packet(login, 512);
|
|
|
|
|
tds_set_version(login, 7, 0);
|
|
|
|
|
tds_set_language(settings->login, settings->language);
|
|
|
|
|
tds_set_packet(settings->login, 512);
|
|
|
|
|
tds_set_version(settings->login, 7, 0);
|
|
|
|
|
|
|
|
|
|
#ifdef FREETDS_0_64
|
|
|
|
|
if (!(context = tds_alloc_context(NULL)))
|
|
|
|
|
if (!(settings->context = tds_alloc_context(NULL)))
|
|
|
|
|
#else
|
|
|
|
|
if (!(context = tds_alloc_context()))
|
|
|
|
|
if (!(settings->context = tds_alloc_context()))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
|
|
|
|
|
goto connect_fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(tds = tds_alloc_socket(context, 512))) {
|
|
|
|
|
if (!(settings->tds = tds_alloc_socket(settings->context, 512))) {
|
|
|
|
|
ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
|
|
|
|
|
goto connect_fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tds_set_parent(tds, NULL);
|
|
|
|
|
connection = tds_read_config_info(tds, login, context->locale);
|
|
|
|
|
tds_set_parent(settings->tds, NULL);
|
|
|
|
|
connection = tds_read_config_info(settings->tds, settings->login, settings->context->locale);
|
|
|
|
|
if (!connection) {
|
|
|
|
|
ast_log(LOG_ERROR, "tds_read_config() failed.\n");
|
|
|
|
|
goto connect_fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tds_connect(tds, connection) == TDS_FAIL) {
|
|
|
|
|
if (tds_connect(settings->tds, connection) == TDS_FAIL) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n");
|
|
|
|
|
tds = NULL; /* freed by tds_connect() on error */
|
|
|
|
|
settings->tds = NULL; /* freed by tds_connect() on error */
|
|
|
|
|
#if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
|
|
|
|
|
tds_free_connection(connection);
|
|
|
|
|
#else
|
|
|
|
@ -369,18 +376,18 @@ static int mssql_connect(void)
|
|
|
|
|
#endif
|
|
|
|
|
connection = NULL;
|
|
|
|
|
|
|
|
|
|
sprintf(query, "USE %s", dbname);
|
|
|
|
|
sprintf(query, "USE %s", settings->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))
|
|
|
|
|
if ((tds_submit_query(settings->tds, query) != TDS_SUCCEED) || (tds_process_simple_query(settings->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))
|
|
|
|
|
if ((tds_submit_query(settings->tds, query) != TDS_SUCCEED) || (tds_process_simple_query(settings->tds) != TDS_SUCCEED))
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname);
|
|
|
|
|
ast_log(LOG_ERROR, "Could not change database (%s)\n", settings->dbname);
|
|
|
|
|
goto connect_fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connected = 1;
|
|
|
|
|
settings->connected = 1;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
connect_fail:
|
|
|
|
@ -394,13 +401,9 @@ static int tds_unload_module(void)
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
ast_mutex_destroy(&settings->lock);
|
|
|
|
|
ast_string_field_free_memory(settings);
|
|
|
|
|
ast_free(settings);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -425,69 +428,79 @@ static int tds_load_module(int reload)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reload) {
|
|
|
|
|
ast_string_field_init(settings, 0);
|
|
|
|
|
} else {
|
|
|
|
|
settings = ast_calloc(1, sizeof(*settings));
|
|
|
|
|
|
|
|
|
|
if (!settings || ast_string_field_init(settings, 256)) {
|
|
|
|
|
if (settings) {
|
|
|
|
|
ast_free(settings);
|
|
|
|
|
settings = NULL;
|
|
|
|
|
}
|
|
|
|
|
ast_config_destroy(cfg);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_mutex_init(&settings->lock);
|
|
|
|
|
|
|
|
|
|
ptr = ast_variable_retrieve(cfg, "global", "hostname");
|
|
|
|
|
if (ptr) {
|
|
|
|
|
if (hostname)
|
|
|
|
|
ast_free(hostname);
|
|
|
|
|
hostname = ast_strdup(ptr);
|
|
|
|
|
} else
|
|
|
|
|
ast_string_field_set(settings, hostname, ptr);
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_ERROR, "Database server hostname not specified.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr = ast_variable_retrieve(cfg, "global", "dbname");
|
|
|
|
|
if (ptr) {
|
|
|
|
|
if (dbname)
|
|
|
|
|
ast_free(dbname);
|
|
|
|
|
dbname = ast_strdup(ptr);
|
|
|
|
|
} else
|
|
|
|
|
ast_string_field_set(settings, dbname, ptr);
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_ERROR, "Database dbname not specified.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr = ast_variable_retrieve(cfg, "global", "user");
|
|
|
|
|
if (ptr) {
|
|
|
|
|
if (dbuser)
|
|
|
|
|
ast_free(dbuser);
|
|
|
|
|
dbuser = ast_strdup(ptr);
|
|
|
|
|
} else
|
|
|
|
|
ast_string_field_set(settings, dbuser, ptr);
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_ERROR, "Database dbuser not specified.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr = ast_variable_retrieve(cfg, "global", "password");
|
|
|
|
|
if (ptr) {
|
|
|
|
|
if (password)
|
|
|
|
|
ast_free(password);
|
|
|
|
|
password = ast_strdup(ptr);
|
|
|
|
|
} else
|
|
|
|
|
ast_string_field_set(settings, password, ptr);
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_ERROR,"Database password not specified.\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_debug(1, "Table not specified. Assuming 'cdr'\n");
|
|
|
|
|
ast_string_field_set(settings, table, "cdr");
|
|
|
|
|
}
|
|
|
|
|
if (table)
|
|
|
|
|
ast_free(table);
|
|
|
|
|
table = ast_strdup(ptr);
|
|
|
|
|
|
|
|
|
|
ast_config_destroy(cfg);
|
|
|
|
|
|
|
|
|
|
ast_mutex_lock(&tds_lock);
|
|
|
|
|
ast_mutex_lock(&settings->lock);
|
|
|
|
|
mssql_disconnect();
|
|
|
|
|
mssql_connect();
|
|
|
|
|
ast_mutex_unlock(&tds_lock);
|
|
|
|
|
ast_mutex_unlock(&settings->lock);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|