cdr_tds.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * FreeTDS CDR logger
  5. *
  6. * This program is free software, distributed under the terms of
  7. * the GNU General Public License.
  8. *
  9. *
  10. * Table Structure for `cdr`
  11. *
  12. * Created on: 05/20/2004 16:16
  13. * Last changed on: 07/27/2004 20:01
  14. CREATE TABLE [dbo].[cdr] (
  15. [accountcode] [varchar] (20) NULL ,
  16. [src] [varchar] (80) NULL ,
  17. [dst] [varchar] (80) NULL ,
  18. [dcontext] [varchar] (80) NULL ,
  19. [clid] [varchar] (80) NULL ,
  20. [channel] [varchar] (80) NULL ,
  21. [dstchannel] [varchar] (80) NULL ,
  22. [lastapp] [varchar] (80) NULL ,
  23. [lastdata] [varchar] (80) NULL ,
  24. [start] [datetime] NULL ,
  25. [answer] [datetime] NULL ,
  26. [end] [datetime] NULL ,
  27. [duration] [int] NULL ,
  28. [billsec] [int] NULL ,
  29. [disposition] [varchar] (20) NULL ,
  30. [amaflags] [varchar] (16) NULL ,
  31. [uniqueid] [varchar] (32) NULL
  32. ) ON [PRIMARY]
  33. */
  34. #include <sys/types.h>
  35. #include <asterisk/config.h>
  36. #include <asterisk/options.h>
  37. #include <asterisk/channel.h>
  38. #include <asterisk/cdr.h>
  39. #include <asterisk/module.h>
  40. #include <asterisk/logger.h>
  41. #include "../asterisk.h"
  42. #include <stdio.h>
  43. #include <string.h>
  44. #include <stdlib.h>
  45. #include <unistd.h>
  46. #include <time.h>
  47. #include <math.h>
  48. #include <tds.h>
  49. #include <tdsconvert.h>
  50. #include <ctype.h>
  51. #if !defined(TDS_INT_EXIT)
  52. #define TDS_PRE_0_62
  53. #warning "You have older TDS, you should upgrade!"
  54. #endif
  55. #define DATE_FORMAT "%Y/%m/%d %T"
  56. static char *desc = "MSSQL CDR Backend";
  57. static char *name = "mssql";
  58. static char *config = "cdr_tds.conf";
  59. static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *charset = NULL, *language = NULL;
  60. static int connected = 0;
  61. static time_t connect_time = 0;
  62. AST_MUTEX_DEFINE_STATIC(tds_lock);
  63. static TDSSOCKET *tds;
  64. static TDSLOGIN *login;
  65. static TDSCONTEXT *context;
  66. static char *stristr(const char*, const char*);
  67. static char *anti_injection(const char *, int);
  68. static void get_date(char *, struct timeval);
  69. static int mssql_connect(void);
  70. static int mssql_disconnect(void);
  71. static int tds_log(struct ast_cdr *cdr)
  72. {
  73. char sqlcmd[2048], start[80], answer[80], end[80];
  74. char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid;
  75. int res = 0;
  76. int retried = 0;
  77. #ifdef TDS_PRE_0_62
  78. TDS_INT result_type;
  79. #endif
  80. ast_mutex_lock(&tds_lock);
  81. memset(sqlcmd, 0, 2048);
  82. accountcode = anti_injection(cdr->accountcode, 20);
  83. src = anti_injection(cdr->src, 80);
  84. dst = anti_injection(cdr->dst, 80);
  85. dcontext = anti_injection(cdr->dcontext, 80);
  86. clid = anti_injection(cdr->clid, 80);
  87. channel = anti_injection(cdr->channel, 80);
  88. dstchannel = anti_injection(cdr->dstchannel, 80);
  89. lastapp = anti_injection(cdr->lastapp, 80);
  90. lastdata = anti_injection(cdr->lastdata, 80);
  91. uniqueid = anti_injection(cdr->uniqueid, 32);
  92. get_date(start, cdr->start);
  93. get_date(answer, cdr->answer);
  94. get_date(end, cdr->end);
  95. sprintf(
  96. sqlcmd,
  97. "INSERT INTO cdr "
  98. "("
  99. "accountcode, "
  100. "src, "
  101. "dst, "
  102. "dcontext, "
  103. "clid, "
  104. "channel, "
  105. "dstchannel, "
  106. "lastapp, "
  107. "lastdata, "
  108. "start, "
  109. "answer, "
  110. "[end], "
  111. "duration, "
  112. "billsec, "
  113. "disposition, "
  114. "amaflags, "
  115. "uniqueid"
  116. ") "
  117. "VALUES "
  118. "("
  119. "'%s', " /* accountcode */
  120. "'%s', " /* src */
  121. "'%s', " /* dst */
  122. "'%s', " /* dcontext */
  123. "'%s', " /* clid */
  124. "'%s', " /* channel */
  125. "'%s', " /* dstchannel */
  126. "'%s', " /* lastapp */
  127. "'%s', " /* lastdata */
  128. "%s, " /* start */
  129. "%s, " /* answer */
  130. "%s, " /* end */
  131. "%i, " /* duration */
  132. "%i, " /* billsec */
  133. "'%s', " /* disposition */
  134. "'%s', " /* amaflags */
  135. "'%s'" /* uniqueid */
  136. ")",
  137. accountcode,
  138. src,
  139. dst,
  140. dcontext,
  141. clid,
  142. channel,
  143. dstchannel,
  144. lastapp,
  145. lastdata,
  146. start,
  147. answer,
  148. end,
  149. cdr->duration,
  150. cdr->billsec,
  151. ast_cdr_disp2str(cdr->disposition),
  152. ast_cdr_flags2str(cdr->amaflags),
  153. uniqueid
  154. );
  155. do {
  156. if (!connected) {
  157. if (mssql_connect())
  158. ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n");
  159. else
  160. ast_log(LOG_WARNING, "Reconnected to SQL database.\n");
  161. retried = 1; /* note that we have now tried */
  162. }
  163. #ifdef TDS_PRE_0_62
  164. if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
  165. #else
  166. if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
  167. #endif
  168. {
  169. ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
  170. mssql_disconnect(); /* this is ok even if we are already disconnected */
  171. }
  172. } while (!connected && !retried);
  173. free(accountcode);
  174. free(src);
  175. free(dst);
  176. free(dcontext);
  177. free(clid);
  178. free(channel);
  179. free(dstchannel);
  180. free(lastapp);
  181. free(lastdata);
  182. free(uniqueid);
  183. ast_mutex_unlock(&tds_lock);
  184. return res;
  185. }
  186. /* Return the offset of one string within another.
  187. Copyright (C) 1994, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
  188. This file is part of the GNU C Library.
  189. The GNU C Library is free software; you can redistribute it and/or
  190. modify it under the terms of the GNU Lesser General Public
  191. License as published by the Free Software Foundation; either
  192. version 2.1 of the License, or (at your option) any later version.
  193. The GNU C Library is distributed in the hope that it will be useful,
  194. but WITHOUT ANY WARRANTY; without even the implied warranty of
  195. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  196. Lesser General Public License for more details.
  197. You should have received a copy of the GNU Lesser General Public
  198. License along with the GNU C Library; if not, write to the Free
  199. Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  200. 02111-1307 USA. */
  201. /*
  202. * My personal strstr() implementation that beats most other algorithms.
  203. * Until someone tells me otherwise, I assume that this is the
  204. * fastest implementation of strstr() in C.
  205. * I deliberately chose not to comment it. You should have at least
  206. * as much fun trying to understand it, as I had to write it :-).
  207. *
  208. * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */
  209. static char *
  210. stristr (phaystack, pneedle)
  211. const char *phaystack;
  212. const char *pneedle;
  213. {
  214. typedef unsigned chartype;
  215. const unsigned char *haystack, *needle;
  216. chartype b;
  217. const unsigned char *rneedle;
  218. haystack = (const unsigned char *) phaystack;
  219. if ((b = toupper(*(needle = (const unsigned char *) pneedle))))
  220. {
  221. chartype c;
  222. haystack--; /* possible ANSI violation */
  223. {
  224. chartype a;
  225. do
  226. if (!(a = toupper(*++haystack)))
  227. goto ret0;
  228. while (a != b);
  229. }
  230. if (!(c = toupper(*++needle)))
  231. goto foundneedle;
  232. ++needle;
  233. goto jin;
  234. for (;;)
  235. {
  236. {
  237. chartype a;
  238. if (0)
  239. jin:{
  240. if ((a = toupper(*++haystack)) == c)
  241. goto crest;
  242. }
  243. else
  244. a = toupper(*++haystack);
  245. do
  246. {
  247. for (; a != b; a = toupper(*++haystack))
  248. {
  249. if (!a)
  250. goto ret0;
  251. if ((a = toupper(*++haystack)) == b)
  252. break;
  253. if (!a)
  254. goto ret0;
  255. }
  256. }
  257. while ((a = toupper(*++haystack)) != c);
  258. }
  259. crest:
  260. {
  261. chartype a;
  262. {
  263. const unsigned char *rhaystack;
  264. if (toupper(*(rhaystack = haystack-- + 1)) == (a = toupper(*(rneedle = needle))))
  265. do
  266. {
  267. if (!a)
  268. goto foundneedle;
  269. if (toupper(*++rhaystack) != (a = toupper(*++needle)))
  270. break;
  271. if (!a)
  272. goto foundneedle;
  273. }
  274. while (toupper(*++rhaystack) == (a = toupper(*++needle)));
  275. needle = rneedle; /* took the register-poor aproach */
  276. }
  277. if (!a)
  278. break;
  279. }
  280. }
  281. }
  282. foundneedle:
  283. return (char *) haystack;
  284. ret0:
  285. return 0;
  286. }
  287. static char *anti_injection(const char *str, int len)
  288. {
  289. /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
  290. char *buf;
  291. char *buf_ptr, *srh_ptr;
  292. char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
  293. int idx;
  294. if ((buf = malloc(len + 1)) == NULL)
  295. {
  296. ast_log(LOG_ERROR, "cdr_tds: Out of memory error\n");
  297. return NULL;
  298. }
  299. memset(buf, 0, len);
  300. buf_ptr = buf;
  301. /* Escape single quotes */
  302. for (; *str && strlen(buf) < len; str++)
  303. {
  304. if (*str == '\'')
  305. *buf_ptr++ = '\'';
  306. *buf_ptr++ = *str;
  307. }
  308. *buf_ptr = '\0';
  309. /* Erase known bad input */
  310. for (idx=0; *known_bad[idx]; idx++)
  311. {
  312. while((srh_ptr = stristr(buf, known_bad[idx]))) /* fix me! */
  313. {
  314. memmove(srh_ptr, srh_ptr+strlen(known_bad[idx]), strlen(srh_ptr+strlen(known_bad[idx]))+1);
  315. }
  316. }
  317. return buf;
  318. }
  319. static void get_date(char *dateField, struct timeval tv)
  320. {
  321. struct tm tm;
  322. time_t t;
  323. char buf[80];
  324. /* To make sure we have date variable if not insert null to SQL */
  325. if (tv.tv_sec && tv.tv_usec)
  326. {
  327. t = tv.tv_sec;
  328. localtime_r(&t, &tm);
  329. strftime(buf, 80, DATE_FORMAT, &tm);
  330. sprintf(dateField, "'%s'", buf);
  331. }
  332. else
  333. {
  334. strcpy(dateField, "null");
  335. }
  336. }
  337. char *description(void)
  338. {
  339. return desc;
  340. }
  341. static int mssql_disconnect(void)
  342. {
  343. if (tds) {
  344. tds_free_socket(tds);
  345. tds = NULL;
  346. }
  347. if (context) {
  348. tds_free_context(context);
  349. context = NULL;
  350. }
  351. if (login) {
  352. tds_free_login(login);
  353. login = NULL;
  354. }
  355. connected = 0;
  356. return 0;
  357. }
  358. static int mssql_connect(void)
  359. {
  360. TDSCONNECTINFO *connection = NULL;
  361. char query[128];
  362. /* Connect to M$SQL Server */
  363. if (!(login = tds_alloc_login()))
  364. {
  365. ast_log(LOG_ERROR, "tds_alloc_login() failed.\n");
  366. return -1;
  367. }
  368. tds_set_server(login, hostname);
  369. tds_set_user(login, dbuser);
  370. tds_set_passwd(login, password);
  371. tds_set_app(login, "TSQL");
  372. tds_set_library(login, "TDS-Library");
  373. #ifndef TDS_PRE_0_62
  374. tds_set_client_charset(login, charset);
  375. #endif
  376. tds_set_language(login, language);
  377. tds_set_packet(login, 512);
  378. tds_set_version(login, 7, 0);
  379. if (!(context = tds_alloc_context()))
  380. {
  381. ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
  382. goto connect_fail;
  383. }
  384. if (!(tds = tds_alloc_socket(context, 512))) {
  385. ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
  386. goto connect_fail;
  387. }
  388. tds_set_parent(tds, NULL);
  389. connection = tds_read_config_info(tds, login, context->locale);
  390. if (!connection)
  391. {
  392. ast_log(LOG_ERROR, "tds_read_config() failed.\n");
  393. goto connect_fail;
  394. }
  395. if (tds_connect(tds, connection) == TDS_FAIL)
  396. {
  397. ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n");
  398. tds = NULL; /* freed by tds_connect() on error */
  399. tds_free_connect(connection);
  400. connection = NULL;
  401. goto connect_fail;
  402. }
  403. tds_free_connect(connection);
  404. connection = NULL;
  405. sprintf(query, "USE %s", dbname);
  406. #ifdef TDS_PRE_0_62
  407. if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
  408. #else
  409. if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
  410. #endif
  411. {
  412. ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname);
  413. goto connect_fail;
  414. }
  415. connected = 1;
  416. return 0;
  417. connect_fail:
  418. mssql_disconnect();
  419. return -1;
  420. }
  421. int unload_module(void)
  422. {
  423. mssql_disconnect();
  424. ast_cdr_unregister(name);
  425. if (hostname) free(hostname);
  426. if (dbname) free(dbname);
  427. if (dbuser) free(dbuser);
  428. if (password) free(password);
  429. if (charset) free(charset);
  430. if (language) free(language);
  431. return 0;
  432. }
  433. int load_module(void)
  434. {
  435. int res = 0;
  436. struct ast_config *cfg;
  437. struct ast_variable *var;
  438. char *ptr = NULL;
  439. #ifdef TDS_PRE_0_62
  440. TDS_INT result_type;
  441. #endif
  442. cfg = ast_load(config);
  443. if (!cfg) {
  444. ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDR's: %s\n", config);
  445. return 0;
  446. }
  447. var = ast_variable_browse(cfg, "global");
  448. if (!var) /* nothing configured */
  449. return 0;
  450. ptr = ast_variable_retrieve(cfg, "global", "hostname");
  451. if (ptr)
  452. hostname = strdup(ptr);
  453. else
  454. ast_log(LOG_ERROR,"Database server hostname not specified.\n");
  455. ptr = ast_variable_retrieve(cfg, "global", "dbname");
  456. if (ptr)
  457. dbname = strdup(ptr);
  458. else
  459. ast_log(LOG_ERROR,"Database dbname not specified.\n");
  460. ptr = ast_variable_retrieve(cfg, "global", "user");
  461. if (ptr)
  462. dbuser = strdup(ptr);
  463. else
  464. ast_log(LOG_ERROR,"Database dbuser not specified.\n");
  465. ptr = ast_variable_retrieve(cfg, "global", "password");
  466. if (ptr)
  467. password = strdup(ptr);
  468. else
  469. ast_log(LOG_ERROR,"Database password not specified.\n");
  470. ptr = ast_variable_retrieve(cfg, "global", "charset");
  471. if (ptr)
  472. charset = strdup(ptr);
  473. else
  474. charset = strdup("iso_1");
  475. ptr = ast_variable_retrieve(cfg, "global", "language");
  476. if (ptr)
  477. language = strdup(ptr);
  478. else
  479. language = strdup("us_english");
  480. ast_destroy(cfg);
  481. mssql_connect();
  482. /* Register MSSQL CDR handler */
  483. res = ast_cdr_register(name, desc, tds_log);
  484. if (res)
  485. {
  486. ast_log(LOG_ERROR, "Unable to register MSSQL CDR handling\n");
  487. }
  488. return res;
  489. }
  490. int reload(void)
  491. {
  492. unload_module();
  493. return load_module();
  494. }
  495. int usecount(void)
  496. {
  497. /* Simplistic use count */
  498. if (ast_mutex_trylock(&tds_lock)) {
  499. return 1;
  500. } else {
  501. ast_mutex_unlock(&tds_lock);
  502. return 0;
  503. }
  504. }
  505. char *key()
  506. {
  507. return ASTERISK_GPL_KEY;
  508. }