#include "asterisk.h"
#include <time.h>
#include <math.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
#include <sqlfront.h>
#include <sybdb.h>
Go to the source code of this file.
Data Structures | |
struct | cdr_tds_config |
Defines | |
#define | DATE_FORMAT "%Y/%m/%d %T" |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static char * | anti_injection (const char *, int) |
static int | execute_and_consume (DBPROCESS *dbproc, const char *fmt,...) |
static void | get_date (char *, size_t len, struct timeval) |
static int | load_module (void) |
static int | mssql_connect (void) |
static int | mssql_disconnect (void) |
static int | reload (void) |
static int | tds_error_handler (DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) |
static int | tds_load_module (int reload) |
static int | tds_log (struct ast_cdr *cdr) |
static int | tds_message_handler (DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) |
static int | tds_unload_module (void) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "FreeTDS CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } |
static struct ast_module_info * | ast_module_info = &__mod_info |
static char * | config = "cdr_tds.conf" |
static char * | name = "FreeTDS (MSSQL)" |
static struct cdr_tds_config * | settings |
static ast_mutex_t | tds_lock = ((ast_mutex_t) PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP ) |
See also
Definition in file cdr_tds.c.
#define DATE_FORMAT "%Y/%m/%d %T" |
* * Table Structure for `cdr` * * Created on: 05/20/2004 16:16 * Last changed on: 07/27/2004 20:01 CREATE TABLE [dbo].[cdr] ( [accountcode] [varchar] (20) NULL , [src] [varchar] (80) NULL , [dst] [varchar] (80) NULL , [dcontext] [varchar] (80) NULL , [clid] [varchar] (80) NULL , [channel] [varchar] (80) NULL , [dstchannel] [varchar] (80) NULL , [lastapp] [varchar] (80) NULL , [lastdata] [varchar] (80) NULL , [start] [datetime] NULL , [answer] [datetime] NULL , [end] [datetime] NULL , [duration] [int] NULL , [billsec] [int] NULL , [disposition] [varchar] (20) NULL , [amaflags] [varchar] (16) NULL , [uniqueid] [varchar] (32) NULL , [userfield] [varchar] (256) NULL ) ON [PRIMARY]
static char * anti_injection | ( | const char * | , | |
int | ||||
) | [static] |
Definition at line 245 of file cdr_tds.c.
References ast_calloc, ast_log(), buf, LOG_ERROR, and strcasestr().
Referenced by tds_log().
00246 { 00247 /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */ 00248 char *buf; 00249 char *buf_ptr, *srh_ptr; 00250 char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"}; 00251 int idx; 00252 00253 if (!(buf = ast_calloc(1, len + 1))) { 00254 ast_log(LOG_ERROR, "Out of memory\n"); 00255 return NULL; 00256 } 00257 00258 buf_ptr = buf; 00259 00260 /* Escape single quotes */ 00261 for (; *str && strlen(buf) < len; str++) { 00262 if (*str == '\'') { 00263 *buf_ptr++ = '\''; 00264 } 00265 *buf_ptr++ = *str; 00266 } 00267 *buf_ptr = '\0'; 00268 00269 /* Erase known bad input */ 00270 for (idx = 0; *known_bad[idx]; idx++) { 00271 while ((srh_ptr = strcasestr(buf, known_bad[idx]))) { 00272 memmove(srh_ptr, srh_ptr + strlen(known_bad[idx]), strlen(srh_ptr + strlen(known_bad[idx])) + 1); 00273 } 00274 } 00275 00276 return buf; 00277 }
static int execute_and_consume | ( | DBPROCESS * | dbproc, | |
const char * | fmt, | |||
... | ||||
) | [static] |
Definition at line 291 of file cdr_tds.c.
References ast_vasprintf, and free.
Referenced by mssql_connect().
00292 { 00293 va_list ap; 00294 char *buffer; 00295 00296 va_start(ap, fmt); 00297 if (ast_vasprintf(&buffer, fmt, ap) < 0) { 00298 va_end(ap); 00299 return 1; 00300 } 00301 va_end(ap); 00302 00303 if (dbfcmd(dbproc, buffer) == FAIL) { 00304 free(buffer); 00305 return 1; 00306 } 00307 00308 free(buffer); 00309 00310 if (dbsqlexec(dbproc) == FAIL) { 00311 return 1; 00312 } 00313 00314 /* Consume the result set (we don't really care about the result, though) */ 00315 while (dbresults(dbproc) != NO_MORE_RESULTS) { 00316 while (dbnextrow(dbproc) != NO_MORE_ROWS); 00317 } 00318 00319 return 0; 00320 }
static void get_date | ( | char * | , | |
size_t | len, | |||
struct | timeval | |||
) | [static] |
Definition at line 279 of file cdr_tds.c.
References ast_copy_string(), ast_localtime(), ast_strftime(), ast_tvzero(), and DATE_FORMAT.
00280 { 00281 /* To make sure we have date variable if not insert null to SQL */ 00282 if (!ast_tvzero(when)) { 00283 struct ast_tm tm; 00284 ast_localtime(&when, &tm, NULL); 00285 ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm); 00286 } else { 00287 ast_copy_string(dateField, "null", len); 00288 } 00289 }
static int load_module | ( | void | ) | [static] |
Definition at line 524 of file cdr_tds.c.
References ast_calloc, ast_cdr_register(), ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_string_field_free_memory, ast_string_field_init, dbinit(), LOG_ERROR, settings, tds_error_handler(), tds_load_module(), tds_log(), and tds_message_handler().
00525 { 00526 if (dbinit() == FAIL) { 00527 ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n"); 00528 return AST_MODULE_LOAD_DECLINE; 00529 } 00530 00531 dberrhandle(tds_error_handler); 00532 dbmsghandle(tds_message_handler); 00533 00534 settings = ast_calloc(1, sizeof(*settings)); 00535 00536 if (!settings || ast_string_field_init(settings, 256)) { 00537 if (settings) { 00538 ast_free(settings); 00539 settings = NULL; 00540 } 00541 dbexit(); 00542 return AST_MODULE_LOAD_DECLINE; 00543 } 00544 00545 if (!tds_load_module(0)) { 00546 ast_string_field_free_memory(settings); 00547 ast_free(settings); 00548 settings = NULL; 00549 dbexit(); 00550 return AST_MODULE_LOAD_DECLINE; 00551 } 00552 00553 ast_cdr_register(name, ast_module_info->description, tds_log); 00554 00555 return AST_MODULE_LOAD_SUCCESS; 00556 }
static int mssql_connect | ( | void | ) | [static] |
Definition at line 334 of file cdr_tds.c.
References ast_log(), cdr_tds_config::charset, cdr_tds_config::connected, cdr_tds_config::database, cdr_tds_config::dbproc, execute_and_consume(), cdr_tds_config::has_userfield, cdr_tds_config::hostname, cdr_tds_config::language, LOG_ERROR, LOG_NOTICE, cdr_tds_config::password, settings, cdr_tds_config::table, and cdr_tds_config::username.
Referenced by tds_load_module(), and tds_log().
00335 { 00336 LOGINREC *login; 00337 00338 if ((login = dblogin()) == NULL) { 00339 ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n"); 00340 return -1; 00341 } 00342 00343 DBSETLAPP(login, "TSQL"); 00344 DBSETLUSER(login, (char *) settings->username); 00345 DBSETLPWD(login, (char *) settings->password); 00346 DBSETLCHARSET(login, (char *) settings->charset); 00347 DBSETLNATLANG(login, (char *) settings->language); 00348 00349 if ((settings->dbproc = dbopen(login, (char *) settings->hostname)) == NULL) { 00350 ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->hostname); 00351 dbloginfree(login); 00352 return -1; 00353 } 00354 00355 dbloginfree(login); 00356 00357 if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) { 00358 ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database); 00359 goto failed; 00360 } 00361 00362 if (execute_and_consume(settings->dbproc, "SELECT 1 FROM [%s]", settings->table)) { 00363 ast_log(LOG_ERROR, "Unable to find table '%s'\n", settings->table); 00364 goto failed; 00365 } 00366 00367 /* Check to see if we have a userfield column in the table */ 00368 if (execute_and_consume(settings->dbproc, "SELECT userfield FROM [%s] WHERE 1 = 0", settings->table)) { 00369 ast_log(LOG_NOTICE, "Unable to find 'userfield' column in table '%s'\n", settings->table); 00370 settings->has_userfield = 0; 00371 } else { 00372 settings->has_userfield = 1; 00373 } 00374 00375 settings->connected = 1; 00376 00377 return 0; 00378 00379 failed: 00380 dbclose(settings->dbproc); 00381 settings->dbproc = NULL; 00382 return -1; 00383 }
static int mssql_disconnect | ( | void | ) | [static] |
Definition at line 322 of file cdr_tds.c.
References cdr_tds_config::connected, cdr_tds_config::dbproc, and settings.
Referenced by tds_load_module(), tds_log(), and tds_unload_module().
00323 { 00324 if (settings->dbproc) { 00325 dbclose(settings->dbproc); 00326 settings->dbproc = NULL; 00327 } 00328 00329 settings->connected = 0; 00330 00331 return 0; 00332 }
static int reload | ( | void | ) | [static] |
Definition at line 519 of file cdr_tds.c.
References tds_load_module().
00520 { 00521 return tds_load_module(1); 00522 }
static int tds_error_handler | ( | DBPROCESS * | dbproc, | |
int | severity, | |||
int | dberr, | |||
int | oserr, | |||
char * | dberrstr, | |||
char * | oserrstr | |||
) | [static] |
Definition at line 403 of file cdr_tds.c.
References ast_log(), and LOG_ERROR.
Referenced by load_module().
00404 { 00405 ast_log(LOG_ERROR, "%s (%d)\n", dberrstr, dberr); 00406 00407 if (oserr != DBNOERR) { 00408 ast_log(LOG_ERROR, "%s (%d)\n", oserrstr, oserr); 00409 } 00410 00411 return INT_CANCEL; 00412 }
static int tds_load_module | ( | int | reload | ) | [static] |
Definition at line 422 of file cdr_tds.c.
References ast_config_destroy(), ast_config_load, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_string_field_init, ast_string_field_set, ast_variable_browse(), ast_variable_retrieve(), charset, CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEUNCHANGED, hostname, language, LOG_ERROR, LOG_NOTICE, mssql_connect(), mssql_disconnect(), settings, table, and tds_lock.
Referenced by load_module(), and reload().
00423 { 00424 struct ast_config *cfg; 00425 const char *ptr = NULL; 00426 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00427 00428 cfg = ast_config_load(config, config_flags); 00429 if (!cfg) { 00430 ast_log(LOG_NOTICE, "Unable to load TDS config for CDRs: %s\n", config); 00431 return 0; 00432 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) 00433 return 0; 00434 00435 if (!ast_variable_browse(cfg, "global")) { 00436 /* nothing configured */ 00437 ast_config_destroy(cfg); 00438 return 0; 00439 } 00440 00441 ast_mutex_lock(&tds_lock); 00442 00443 /* Clear out any existing settings */ 00444 ast_string_field_init(settings, 0); 00445 00446 ptr = ast_variable_retrieve(cfg, "global", "hostname"); 00447 if (ptr) { 00448 ast_string_field_set(settings, hostname, ptr); 00449 } else { 00450 ast_log(LOG_ERROR, "Failed to connect: Database server hostname not specified.\n"); 00451 goto failed; 00452 } 00453 00454 ptr = ast_variable_retrieve(cfg, "global", "dbname"); 00455 if (ptr) { 00456 ast_string_field_set(settings, database, ptr); 00457 } else { 00458 ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n"); 00459 goto failed; 00460 } 00461 00462 ptr = ast_variable_retrieve(cfg, "global", "user"); 00463 if (ptr) { 00464 ast_string_field_set(settings, username, ptr); 00465 } else { 00466 ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n"); 00467 goto failed; 00468 } 00469 00470 ptr = ast_variable_retrieve(cfg, "global", "password"); 00471 if (ptr) { 00472 ast_string_field_set(settings, password, ptr); 00473 } else { 00474 ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n"); 00475 goto failed; 00476 } 00477 00478 ptr = ast_variable_retrieve(cfg, "global", "charset"); 00479 if (ptr) { 00480 ast_string_field_set(settings, charset, ptr); 00481 } else { 00482 ast_string_field_set(settings, charset, "iso_1"); 00483 } 00484 00485 ptr = ast_variable_retrieve(cfg, "global", "language"); 00486 if (ptr) { 00487 ast_string_field_set(settings, language, ptr); 00488 } else { 00489 ast_string_field_set(settings, language, "us_english"); 00490 } 00491 00492 ptr = ast_variable_retrieve(cfg, "global", "table"); 00493 if (ptr) { 00494 ast_string_field_set(settings, table, ptr); 00495 } else { 00496 ast_log(LOG_NOTICE, "Table name not specified, using 'cdr' by default.\n"); 00497 ast_string_field_set(settings, table, "cdr"); 00498 } 00499 00500 mssql_disconnect(); 00501 00502 if (mssql_connect()) { 00503 /* We failed to connect (mssql_connect takes care of logging it) */ 00504 goto failed; 00505 } 00506 00507 ast_mutex_unlock(&tds_lock); 00508 ast_config_destroy(cfg); 00509 00510 return 1; 00511 00512 failed: 00513 ast_mutex_unlock(&tds_lock); 00514 ast_config_destroy(cfg); 00515 00516 return 0; 00517 }
static int tds_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 111 of file cdr_tds.c.
References accountcode, anti_injection(), ast_cdr_disp2str(), ast_cdr_flags2str(), ast_free, ast_log(), AST_MAX_USER_FIELD, ast_mutex_lock(), ast_mutex_unlock(), cdr_tds_config::connected, cdr_tds_config::dbproc, get_date(), cdr_tds_config::has_userfield, cdr_tds_config::hostname, LOG_ERROR, LOG_NOTICE, mssql_connect(), mssql_disconnect(), settings, cdr_tds_config::table, and tds_lock.
Referenced by load_module().
00112 { 00113 char start[80], answer[80], end[80]; 00114 char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid, *userfield = NULL; 00115 RETCODE erc; 00116 int res = -1; 00117 int attempt = 1; 00118 00119 accountcode = anti_injection(cdr->accountcode, 20); 00120 src = anti_injection(cdr->src, 80); 00121 dst = anti_injection(cdr->dst, 80); 00122 dcontext = anti_injection(cdr->dcontext, 80); 00123 clid = anti_injection(cdr->clid, 80); 00124 channel = anti_injection(cdr->channel, 80); 00125 dstchannel = anti_injection(cdr->dstchannel, 80); 00126 lastapp = anti_injection(cdr->lastapp, 80); 00127 lastdata = anti_injection(cdr->lastdata, 80); 00128 uniqueid = anti_injection(cdr->uniqueid, 32); 00129 00130 get_date(start, sizeof(start), cdr->start); 00131 get_date(answer, sizeof(answer), cdr->answer); 00132 get_date(end, sizeof(end), cdr->end); 00133 00134 ast_mutex_lock(&tds_lock); 00135 00136 if (settings->has_userfield) { 00137 userfield = anti_injection(cdr->userfield, AST_MAX_USER_FIELD); 00138 } 00139 00140 retry: 00141 /* Ensure that we are connected */ 00142 if (!settings->connected) { 00143 ast_log(LOG_NOTICE, "Attempting to reconnect to %s (Attempt %d)\n", settings->hostname, attempt); 00144 if (mssql_connect()) { 00145 /* Connect failed */ 00146 if (attempt++ < 3) { 00147 goto retry; 00148 } 00149 goto done; 00150 } 00151 } 00152 00153 if (settings->has_userfield) { 00154 erc = dbfcmd(settings->dbproc, 00155 "INSERT INTO %s " 00156 "(" 00157 "accountcode, src, dst, dcontext, clid, channel, " 00158 "dstchannel, lastapp, lastdata, start, answer, [end], duration, " 00159 "billsec, disposition, amaflags, uniqueid, userfield" 00160 ") " 00161 "VALUES " 00162 "(" 00163 "'%s', '%s', '%s', '%s', '%s', '%s', " 00164 "'%s', '%s', '%s', %s, %s, %s, %ld, " 00165 "%ld, '%s', '%s', '%s', '%s'" 00166 ")", 00167 settings->table, 00168 accountcode, src, dst, dcontext, clid, channel, 00169 dstchannel, lastapp, lastdata, start, answer, end, cdr->duration, 00170 cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid, 00171 userfield 00172 ); 00173 } else { 00174 erc = dbfcmd(settings->dbproc, 00175 "INSERT INTO %s " 00176 "(" 00177 "accountcode, src, dst, dcontext, clid, channel, " 00178 "dstchannel, lastapp, lastdata, start, answer, [end], duration, " 00179 "billsec, disposition, amaflags, uniqueid" 00180 ") " 00181 "VALUES " 00182 "(" 00183 "'%s', '%s', '%s', '%s', '%s', '%s', " 00184 "'%s', '%s', '%s', %s, %s, %s, %ld, " 00185 "%ld, '%s', '%s', '%s'" 00186 ")", 00187 settings->table, 00188 accountcode, src, dst, dcontext, clid, channel, 00189 dstchannel, lastapp, lastdata, start, answer, end, cdr->duration, 00190 cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid 00191 ); 00192 } 00193 00194 if (erc == FAIL) { 00195 if (attempt++ < 3) { 00196 ast_log(LOG_NOTICE, "Failed to build INSERT statement, retrying...\n"); 00197 mssql_disconnect(); 00198 goto retry; 00199 } else { 00200 ast_log(LOG_ERROR, "Failed to build INSERT statement, no CDR was logged.\n"); 00201 goto done; 00202 } 00203 } 00204 00205 if (dbsqlexec(settings->dbproc) == FAIL) { 00206 if (attempt++ < 3) { 00207 ast_log(LOG_NOTICE, "Failed to execute INSERT statement, retrying...\n"); 00208 mssql_disconnect(); 00209 goto retry; 00210 } else { 00211 ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CDR was logged.\n"); 00212 goto done; 00213 } 00214 } 00215 00216 /* Consume any results we might get back (this is more of a sanity check than 00217 * anything else, since an INSERT shouldn't return results). */ 00218 while (dbresults(settings->dbproc) != NO_MORE_RESULTS) { 00219 while (dbnextrow(settings->dbproc) != NO_MORE_ROWS); 00220 } 00221 00222 res = 0; 00223 00224 done: 00225 ast_mutex_unlock(&tds_lock); 00226 00227 ast_free(accountcode); 00228 ast_free(src); 00229 ast_free(dst); 00230 ast_free(dcontext); 00231 ast_free(clid); 00232 ast_free(channel); 00233 ast_free(dstchannel); 00234 ast_free(lastapp); 00235 ast_free(lastdata); 00236 ast_free(uniqueid); 00237 00238 if (userfield) { 00239 ast_free(userfield); 00240 } 00241 00242 return res; 00243 }
static int tds_message_handler | ( | DBPROCESS * | dbproc, | |
DBINT | msgno, | |||
int | msgstate, | |||
int | severity, | |||
char * | msgtext, | |||
char * | srvname, | |||
char * | procname, | |||
int | line | |||
) | [static] |
Definition at line 414 of file cdr_tds.c.
References ast_debug, ast_log(), and LOG_NOTICE.
Referenced by load_module().
00415 { 00416 ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line); 00417 ast_log(LOG_NOTICE, "%s\n", msgtext); 00418 00419 return 0; 00420 }
static int tds_unload_module | ( | void | ) | [static] |
Definition at line 385 of file cdr_tds.c.
References ast_cdr_unregister(), ast_free, ast_mutex_lock(), ast_mutex_unlock(), ast_string_field_free_memory, mssql_disconnect(), settings, and tds_lock.
Referenced by unload_module().
00386 { 00387 if (settings) { 00388 ast_mutex_lock(&tds_lock); 00389 mssql_disconnect(); 00390 ast_mutex_unlock(&tds_lock); 00391 00392 ast_string_field_free_memory(settings); 00393 ast_free(settings); 00394 } 00395 00396 ast_cdr_unregister(name); 00397 00398 dbexit(); 00399 00400 return 0; 00401 }
static int unload_module | ( | void | ) | [static] |
Definition at line 558 of file cdr_tds.c.
References tds_unload_module().
00559 { 00560 return tds_unload_module(); 00561 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "FreeTDS CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
struct ast_module_info* ast_module_info = &__mod_info [static] |
struct cdr_tds_config* settings [static] |
Definition at line 100 of file cdr_tds.c.
Referenced by load_module(), mssql_connect(), mssql_disconnect(), tds_load_module(), tds_log(), and tds_unload_module().
ast_mutex_t tds_lock = ((ast_mutex_t) PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP ) [static] |
Definition at line 98 of file cdr_tds.c.
Referenced by tds_load_module(), tds_log(), and tds_unload_module().