00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062 #include "asterisk.h"
00063
00064 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00065
00066 #include <sys/types.h>
00067 #include <stdio.h>
00068 #include <string.h>
00069 #include <stdlib.h>
00070 #include <unistd.h>
00071 #include <time.h>
00072 #include <math.h>
00073
00074 #include <tds.h>
00075 #include <tdsconvert.h>
00076 #include <ctype.h>
00077
00078 #include "asterisk/config.h"
00079 #include "asterisk/options.h"
00080 #include "asterisk/channel.h"
00081 #include "asterisk/cdr.h"
00082 #include "asterisk/module.h"
00083 #include "asterisk/logger.h"
00084
00085 #ifdef FREETDS_PRE_0_62
00086 #warning "You have older TDS, you should upgrade!"
00087 #endif
00088
00089 #define DATE_FORMAT "%Y/%m/%d %T"
00090
00091 static char *name = "mssql";
00092 static char *config = "cdr_tds.conf";
00093
00094 static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *charset = NULL, *language = NULL;
00095 static char *table = NULL;
00096
00097 static int connected = 0;
00098
00099 AST_MUTEX_DEFINE_STATIC(tds_lock);
00100
00101 static TDSSOCKET *tds;
00102 static TDSLOGIN *login;
00103 static TDSCONTEXT *context;
00104
00105 static char *anti_injection(const char *, int);
00106 static void get_date(char *, struct timeval);
00107
00108 static int mssql_connect(void);
00109 static int mssql_disconnect(void);
00110
00111 static int tds_log(struct ast_cdr *cdr)
00112 {
00113 char sqlcmd[2048], start[80], answer[80], end[80];
00114 char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid;
00115 int res = 0;
00116 int retried = 0;
00117 #ifdef FREETDS_PRE_0_62
00118 TDS_INT result_type;
00119 #endif
00120
00121 ast_mutex_lock(&tds_lock);
00122
00123 memset(sqlcmd, 0, 2048);
00124
00125 accountcode = anti_injection(cdr->accountcode, 20);
00126 src = anti_injection(cdr->src, 80);
00127 dst = anti_injection(cdr->dst, 80);
00128 dcontext = anti_injection(cdr->dcontext, 80);
00129 clid = anti_injection(cdr->clid, 80);
00130 channel = anti_injection(cdr->channel, 80);
00131 dstchannel = anti_injection(cdr->dstchannel, 80);
00132 lastapp = anti_injection(cdr->lastapp, 80);
00133 lastdata = anti_injection(cdr->lastdata, 80);
00134 uniqueid = anti_injection(cdr->uniqueid, 32);
00135
00136 get_date(start, cdr->start);
00137 get_date(answer, cdr->answer);
00138 get_date(end, cdr->end);
00139
00140 sprintf(
00141 sqlcmd,
00142 "INSERT INTO %s "
00143 "("
00144 "accountcode, "
00145 "src, "
00146 "dst, "
00147 "dcontext, "
00148 "clid, "
00149 "channel, "
00150 "dstchannel, "
00151 "lastapp, "
00152 "lastdata, "
00153 "start, "
00154 "answer, "
00155 "[end], "
00156 "duration, "
00157 "billsec, "
00158 "disposition, "
00159 "amaflags, "
00160 "uniqueid"
00161 ") "
00162 "VALUES "
00163 "("
00164 "'%s', "
00165 "'%s', "
00166 "'%s', "
00167 "'%s', "
00168 "'%s', "
00169 "'%s', "
00170 "'%s', "
00171 "'%s', "
00172 "'%s', "
00173 "%s, "
00174 "%s, "
00175 "%s, "
00176 "%ld, "
00177 "%ld, "
00178 "'%s', "
00179 "'%s', "
00180 "'%s'"
00181 ")",
00182 table,
00183 accountcode,
00184 src,
00185 dst,
00186 dcontext,
00187 clid,
00188 channel,
00189 dstchannel,
00190 lastapp,
00191 lastdata,
00192 start,
00193 answer,
00194 end,
00195 cdr->duration,
00196 cdr->billsec,
00197 ast_cdr_disp2str(cdr->disposition),
00198 ast_cdr_flags2str(cdr->amaflags),
00199 uniqueid
00200 );
00201
00202 do {
00203 if (!connected) {
00204 if (mssql_connect())
00205 ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n");
00206 else
00207 ast_log(LOG_WARNING, "Reconnected to SQL database.\n");
00208
00209 retried = 1;
00210 }
00211
00212 #ifdef FREETDS_PRE_0_62
00213 if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
00214 #else
00215 if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
00216 #endif
00217 {
00218 ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
00219
00220 mssql_disconnect();
00221 }
00222 } while (!connected && !retried);
00223
00224 free(accountcode);
00225 free(src);
00226 free(dst);
00227 free(dcontext);
00228 free(clid);
00229 free(channel);
00230 free(dstchannel);
00231 free(lastapp);
00232 free(lastdata);
00233 free(uniqueid);
00234
00235 ast_mutex_unlock(&tds_lock);
00236
00237 return res;
00238 }
00239
00240 static char *anti_injection(const char *str, int len)
00241 {
00242
00243
00244 char *buf;
00245 char *buf_ptr, *srh_ptr;
00246 char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
00247 int idx;
00248
00249 if ((buf = malloc(len + 1)) == NULL)
00250 {
00251 ast_log(LOG_ERROR, "cdr_tds: Out of memory error\n");
00252 return NULL;
00253 }
00254 memset(buf, 0, len);
00255
00256 buf_ptr = buf;
00257
00258
00259 for (; *str && strlen(buf) < len; str++)
00260 {
00261 if (*str == '\'')
00262 *buf_ptr++ = '\'';
00263 *buf_ptr++ = *str;
00264 }
00265 *buf_ptr = '\0';
00266
00267
00268 for (idx=0; *known_bad[idx]; idx++)
00269 {
00270 while((srh_ptr = strcasestr(buf, known_bad[idx])))
00271 {
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 }
00278
00279 static void get_date(char *dateField, struct timeval tv)
00280 {
00281 struct tm tm;
00282 time_t t;
00283 char buf[80];
00284
00285
00286 if (!ast_tvzero(tv))
00287 {
00288 t = tv.tv_sec;
00289 localtime_r(&t, &tm);
00290 strftime(buf, 80, DATE_FORMAT, &tm);
00291 sprintf(dateField, "'%s'", buf);
00292 }
00293 else
00294 {
00295 strcpy(dateField, "null");
00296 }
00297 }
00298
00299 static int mssql_disconnect(void)
00300 {
00301 if (tds) {
00302 tds_free_socket(tds);
00303 tds = NULL;
00304 }
00305
00306 if (context) {
00307 tds_free_context(context);
00308 context = NULL;
00309 }
00310
00311 if (login) {
00312 tds_free_login(login);
00313 login = NULL;
00314 }
00315
00316 connected = 0;
00317
00318 return 0;
00319 }
00320
00321 static int mssql_connect(void)
00322 {
00323 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
00324 TDSCONNECTION *connection = NULL;
00325 #else
00326 TDSCONNECTINFO *connection = NULL;
00327 #endif
00328 char query[128];
00329
00330
00331 if (!(login = tds_alloc_login()))
00332 {
00333 ast_log(LOG_ERROR, "tds_alloc_login() failed.\n");
00334 return -1;
00335 }
00336
00337 tds_set_server(login, hostname);
00338 tds_set_user(login, dbuser);
00339 tds_set_passwd(login, password);
00340 tds_set_app(login, "TSQL");
00341 tds_set_library(login, "TDS-Library");
00342 #ifndef FREETDS_PRE_0_62
00343 tds_set_client_charset(login, charset);
00344 #endif
00345 tds_set_language(login, language);
00346 tds_set_packet(login, 512);
00347 tds_set_version(login, 7, 0);
00348
00349 #ifdef FREETDS_0_64
00350 if (!(context = tds_alloc_context(NULL)))
00351 #else
00352 if (!(context = tds_alloc_context()))
00353 #endif
00354 {
00355 ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
00356 goto connect_fail;
00357 }
00358
00359 if (!(tds = tds_alloc_socket(context, 512))) {
00360 ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
00361 goto connect_fail;
00362 }
00363
00364 tds_set_parent(tds, NULL);
00365 connection = tds_read_config_info(tds, login, context->locale);
00366 if (!connection)
00367 {
00368 ast_log(LOG_ERROR, "tds_read_config() failed.\n");
00369 goto connect_fail;
00370 }
00371
00372 if (tds_connect(tds, connection) == TDS_FAIL)
00373 {
00374 ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n");
00375 tds = NULL;
00376 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
00377 tds_free_connection(connection);
00378 #else
00379 tds_free_connect(connection);
00380 #endif
00381 connection = NULL;
00382 goto connect_fail;
00383 }
00384 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
00385 tds_free_connection(connection);
00386 #else
00387 tds_free_connect(connection);
00388 #endif
00389 connection = NULL;
00390
00391 sprintf(query, "USE %s", dbname);
00392 #ifdef FREETDS_PRE_0_62
00393 if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
00394 #else
00395 if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
00396 #endif
00397 {
00398 ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname);
00399 goto connect_fail;
00400 }
00401
00402 connected = 1;
00403 return 0;
00404
00405 connect_fail:
00406 mssql_disconnect();
00407 return -1;
00408 }
00409
00410 static int tds_unload_module(void)
00411 {
00412 mssql_disconnect();
00413
00414 ast_cdr_unregister(name);
00415
00416 if (hostname) free(hostname);
00417 if (dbname) free(dbname);
00418 if (dbuser) free(dbuser);
00419 if (password) free(password);
00420 if (charset) free(charset);
00421 if (language) free(language);
00422 if (table) free(table);
00423
00424 return 0;
00425 }
00426
00427 static int tds_load_module(void)
00428 {
00429 int res = 0;
00430 struct ast_config *cfg;
00431 struct ast_variable *var;
00432 const char *ptr = NULL;
00433 #ifdef FREETDS_PRE_0_62
00434 TDS_INT result_type;
00435 #endif
00436
00437 cfg = ast_config_load(config);
00438 if (!cfg) {
00439 ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDR's: %s\n", config);
00440 return 0;
00441 }
00442
00443 var = ast_variable_browse(cfg, "global");
00444 if (!var)
00445 return 0;
00446
00447 ptr = ast_variable_retrieve(cfg, "global", "hostname");
00448 if (ptr)
00449 hostname = strdup(ptr);
00450 else
00451 ast_log(LOG_ERROR,"Database server hostname not specified.\n");
00452
00453 ptr = ast_variable_retrieve(cfg, "global", "dbname");
00454 if (ptr)
00455 dbname = strdup(ptr);
00456 else
00457 ast_log(LOG_ERROR,"Database dbname not specified.\n");
00458
00459 ptr = ast_variable_retrieve(cfg, "global", "user");
00460 if (ptr)
00461 dbuser = strdup(ptr);
00462 else
00463 ast_log(LOG_ERROR,"Database dbuser not specified.\n");
00464
00465 ptr = ast_variable_retrieve(cfg, "global", "password");
00466 if (ptr)
00467 password = strdup(ptr);
00468 else
00469 ast_log(LOG_ERROR,"Database password not specified.\n");
00470
00471 ptr = ast_variable_retrieve(cfg, "global", "charset");
00472 if (ptr)
00473 charset = strdup(ptr);
00474 else
00475 charset = strdup("iso_1");
00476
00477 ptr = ast_variable_retrieve(cfg, "global", "language");
00478 if (ptr)
00479 language = strdup(ptr);
00480 else
00481 language = strdup("us_english");
00482
00483 ptr = ast_variable_retrieve(cfg,"global","table");
00484 if (ptr == NULL) {
00485 ast_log(LOG_DEBUG,"cdr_tds: table not specified. Assuming cdr\n");
00486 ptr = "cdr";
00487 }
00488 table = strdup(ptr);
00489
00490 ast_config_destroy(cfg);
00491
00492 mssql_connect();
00493
00494
00495 res = ast_cdr_register(name, ast_module_info->description, tds_log);
00496 if (res)
00497 {
00498 ast_log(LOG_ERROR, "Unable to register MSSQL CDR handling\n");
00499 }
00500
00501 return res;
00502 }
00503
00504 static int reload(void)
00505 {
00506 tds_unload_module();
00507 return tds_load_module();
00508 }
00509
00510 static int load_module(void)
00511 {
00512 if(!tds_load_module())
00513 return AST_MODULE_LOAD_DECLINE;
00514 else
00515 return AST_MODULE_LOAD_SUCCESS;
00516 }
00517
00518 static int unload_module(void)
00519 {
00520 return tds_unload_module();
00521 }
00522
00523 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MSSQL CDR Backend",
00524 .load = load_module,
00525 .unload = unload_module,
00526 .reload = reload,
00527 );