cdr_tds.c 14 KB

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