2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2004 - 2006, Digium, Inc.
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.
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.
19 * \brief FreeTDS CDR logger
22 * \arg \ref Config_cdr
23 * \arg http://www.freetds.org/
24 * \ingroup cdr_drivers
29 * Table Structure for `cdr`
31 * Created on: 05/20/2004 16:16
32 * Last changed on: 07/27/2004 20:01
34 CREATE TABLE [dbo].[cdr] (
35 [accountcode] [varchar] (20) NULL ,
36 [src] [varchar] (80) NULL ,
37 [dst] [varchar] (80) NULL ,
38 [dcontext] [varchar] (80) NULL ,
39 [clid] [varchar] (80) NULL ,
40 [channel] [varchar] (80) NULL ,
41 [dstchannel] [varchar] (80) NULL ,
42 [lastapp] [varchar] (80) NULL ,
43 [lastdata] [varchar] (80) NULL ,
44 [start] [datetime] NULL ,
45 [answer] [datetime] NULL ,
46 [end] [datetime] NULL ,
47 [duration] [int] NULL ,
48 [billsec] [int] NULL ,
49 [disposition] [varchar] (20) NULL ,
50 [amaflags] [varchar] (16) NULL ,
51 [uniqueid] [varchar] (32) NULL ,
52 [userfield] [varchar] (256) NULL
60 <depend>freetds</depend>
65 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
67 #include <sys/types.h>
76 #include <tdsconvert.h>
79 #include "asterisk/config.h"
80 #include "asterisk/options.h"
81 #include "asterisk/channel.h"
82 #include "asterisk/cdr.h"
83 #include "asterisk/module.h"
84 #include "asterisk/logger.h"
86 #ifdef FREETDS_PRE_0_62
87 #warning "You have older TDS, you should upgrade!"
90 #define DATE_FORMAT "%Y/%m/%d %T"
92 static char *name
= "mssql";
93 static char *config
= "cdr_tds.conf";
95 static char *hostname
= NULL
, *dbname
= NULL
, *dbuser
= NULL
, *password
= NULL
, *charset
= NULL
, *language
= NULL
;
96 static char *table
= NULL
;
98 static int connected
= 0;
99 static int has_userfield
= 0;
101 AST_MUTEX_DEFINE_STATIC(tds_lock
);
103 static TDSSOCKET
*tds
;
104 static TDSLOGIN
*login
;
105 static TDSCONTEXT
*context
;
107 static char *anti_injection(const char *, int);
108 static void get_date(char *, size_t, struct timeval
);
110 static int mssql_connect(void);
111 static int mssql_disconnect(void);
113 static int tds_log(struct ast_cdr
*cdr
)
115 char sqlcmd
[2048], start
[80], answer
[80], end
[80];
116 char *accountcode
, *src
, *dst
, *dcontext
, *clid
, *channel
, *dstchannel
, *lastapp
, *lastdata
, *uniqueid
, *userfield
= NULL
;
119 #ifdef FREETDS_PRE_0_62
123 ast_mutex_lock(&tds_lock
);
125 memset(sqlcmd
, 0, 2048);
127 accountcode
= anti_injection(cdr
->accountcode
, 20);
128 src
= anti_injection(cdr
->src
, 80);
129 dst
= anti_injection(cdr
->dst
, 80);
130 dcontext
= anti_injection(cdr
->dcontext
, 80);
131 clid
= anti_injection(cdr
->clid
, 80);
132 channel
= anti_injection(cdr
->channel
, 80);
133 dstchannel
= anti_injection(cdr
->dstchannel
, 80);
134 lastapp
= anti_injection(cdr
->lastapp
, 80);
135 lastdata
= anti_injection(cdr
->lastdata
, 80);
136 uniqueid
= anti_injection(cdr
->uniqueid
, 32);
139 userfield
= anti_injection(cdr
->userfield
, AST_MAX_USER_FIELD
);
142 get_date(start
, sizeof(start
), cdr
->start
);
143 get_date(answer
, sizeof(answer
), cdr
->answer
);
144 get_date(end
, sizeof(end
), cdr
->end
);
173 "'%s', " /* accountcode */
176 "'%s', " /* dcontext */
178 "'%s', " /* channel */
179 "'%s', " /* dstchannel */
180 "'%s', " /* lastapp */
181 "'%s', " /* lastdata */
185 "%ld, " /* duration */
186 "%ld, " /* billsec */
187 "'%s', " /* disposition */
188 "'%s', " /* amaflags */
189 "'%s', " /* uniqueid */
190 "'%s'" /* userfield */
207 ast_cdr_disp2str(cdr
->disposition
),
208 ast_cdr_flags2str(cdr
->amaflags
),
238 "'%s', " /* accountcode */
241 "'%s', " /* dcontext */
243 "'%s', " /* channel */
244 "'%s', " /* dstchannel */
245 "'%s', " /* lastapp */
246 "'%s', " /* lastdata */
250 "%ld, " /* duration */
251 "%ld, " /* billsec */
252 "'%s', " /* disposition */
253 "'%s', " /* amaflags */
254 "'%s'" /* uniqueid */
271 ast_cdr_disp2str(cdr
->disposition
),
272 ast_cdr_flags2str(cdr
->amaflags
),
280 ast_log(LOG_ERROR
, "Failed to reconnect to SQL database.\n");
282 ast_log(LOG_WARNING
, "Reconnected to SQL database.\n");
284 retried
= 1; /* note that we have now tried */
287 #ifdef FREETDS_PRE_0_62
288 if (!connected
|| (tds_submit_query(tds
, sqlcmd
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
, &result_type
) != TDS_SUCCEED
|| result_type
!= TDS_CMD_SUCCEED
))
290 if (!connected
|| (tds_submit_query(tds
, sqlcmd
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
) != TDS_SUCCEED
))
293 ast_log(LOG_ERROR
, "Failed to insert Call Data Record into SQL database.\n");
295 mssql_disconnect(); /* this is ok even if we are already disconnected */
297 } while (!connected
&& !retried
);
313 ast_mutex_unlock(&tds_lock
);
318 static char *anti_injection(const char *str
, int len
)
320 /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
323 char *buf_ptr
, *srh_ptr
;
324 char *known_bad
[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
327 if ((buf
= malloc(len
+ 1)) == NULL
)
329 ast_log(LOG_ERROR
, "cdr_tds: Out of memory error\n");
336 /* Escape single quotes */
337 for (; *str
&& strlen(buf
) < len
; str
++)
345 /* Erase known bad input */
346 for (idx
=0; *known_bad
[idx
]; idx
++)
348 while((srh_ptr
= strcasestr(buf
, known_bad
[idx
])))
350 memmove(srh_ptr
, srh_ptr
+strlen(known_bad
[idx
]), strlen(srh_ptr
+strlen(known_bad
[idx
]))+1);
357 static void get_date(char *dateField
, size_t length
, struct timeval tv
)
363 /* To make sure we have date variable if not insert null to SQL */
367 ast_localtime(&t
, &tm
, NULL
);
368 strftime(buf
, sizeof(buf
), DATE_FORMAT
, &tm
);
369 snprintf(dateField
, length
, "'%s'", buf
);
373 ast_copy_string(dateField
, "null", length
);
377 static int mssql_disconnect(void)
380 tds_free_socket(tds
);
385 tds_free_context(context
);
390 tds_free_login(login
);
399 static int mssql_connect(void)
401 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
402 TDSCONNECTION
*connection
= NULL
;
404 TDSCONNECTINFO
*connection
= NULL
;
408 /* Connect to M$SQL Server */
409 if (!(login
= tds_alloc_login()))
411 ast_log(LOG_ERROR
, "tds_alloc_login() failed.\n");
415 tds_set_server(login
, hostname
);
416 tds_set_user(login
, dbuser
);
417 tds_set_passwd(login
, password
);
418 tds_set_app(login
, "TSQL");
419 tds_set_library(login
, "TDS-Library");
420 #ifndef FREETDS_PRE_0_62
421 tds_set_client_charset(login
, charset
);
423 tds_set_language(login
, language
);
424 tds_set_packet(login
, 512);
425 tds_set_version(login
, 7, 0);
428 if (!(context
= tds_alloc_context(NULL
)))
430 if (!(context
= tds_alloc_context()))
433 ast_log(LOG_ERROR
, "tds_alloc_context() failed.\n");
437 if (!(tds
= tds_alloc_socket(context
, 512))) {
438 ast_log(LOG_ERROR
, "tds_alloc_socket() failed.\n");
442 tds_set_parent(tds
, NULL
);
443 connection
= tds_read_config_info(tds
, login
, context
->locale
);
446 ast_log(LOG_ERROR
, "tds_read_config() failed.\n");
450 if (tds_connect(tds
, connection
) == TDS_FAIL
)
452 ast_log(LOG_ERROR
, "Failed to connect to MSSQL server.\n");
453 tds
= NULL
; /* freed by tds_connect() on error */
454 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
455 tds_free_connection(connection
);
457 tds_free_connect(connection
);
462 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
463 tds_free_connection(connection
);
465 tds_free_connect(connection
);
469 snprintf(query
, sizeof(query
), "USE %s", dbname
);
470 #ifdef FREETDS_PRE_0_62
471 if ((tds_submit_query(tds
, query
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
, &result_type
) != TDS_SUCCEED
|| result_type
!= TDS_CMD_SUCCEED
))
473 if ((tds_submit_query(tds
, query
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
) != TDS_SUCCEED
))
476 ast_log(LOG_ERROR
, "Could not change database (%s)\n", dbname
);
480 snprintf(query
, sizeof(query
), "SELECT 1 FROM %s", table
);
481 #ifdef FREETDS_PRE_0_62
482 if ((tds_submit_query(tds
, query
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
, &result_type
) != TDS_SUCCEED
|| result_type
!= TDS_CMD_SUCCEED
))
484 if ((tds_submit_query(tds
, query
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
) != TDS_SUCCEED
))
487 ast_log(LOG_ERROR
, "Could not find table '%s' in database '%s'\n", table
, dbname
);
492 snprintf(query
, sizeof(query
), "SELECT userfield FROM %s WHERE 1 = 0", table
);
493 #ifdef FREETDS_PRE_0_62
494 if ((tds_submit_query(tds
, query
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
, &result_type
) != TDS_SUCCEED
|| result_type
!= TDS_CMD_SUCCEED
))
496 if ((tds_submit_query(tds
, query
) != TDS_SUCCEED
) || (tds_process_simple_query(tds
) != TDS_SUCCEED
))
499 ast_log(LOG_NOTICE
, "Unable to find 'userfield' column in table '%s'\n", table
);
511 static int tds_unload_module(void)
515 ast_cdr_unregister(name
);
517 if (hostname
) free(hostname
);
518 if (dbname
) free(dbname
);
519 if (dbuser
) free(dbuser
);
520 if (password
) free(password
);
521 if (charset
) free(charset
);
522 if (language
) free(language
);
523 if (table
) free(table
);
528 static int tds_load_module(void)
531 struct ast_config
*cfg
;
532 struct ast_variable
*var
;
533 const char *ptr
= NULL
;
534 #ifdef FREETDS_PRE_0_62
538 cfg
= ast_config_load(config
);
540 ast_log(LOG_NOTICE
, "Unable to load config for MSSQL CDR's: %s\n", config
);
544 var
= ast_variable_browse(cfg
, "global");
545 if (!var
) /* nothing configured */ {
546 ast_config_destroy(cfg
);
550 ptr
= ast_variable_retrieve(cfg
, "global", "hostname");
552 hostname
= strdup(ptr
);
554 ast_log(LOG_ERROR
,"Database server hostname not specified.\n");
556 ptr
= ast_variable_retrieve(cfg
, "global", "dbname");
558 dbname
= strdup(ptr
);
560 ast_log(LOG_ERROR
,"Database dbname not specified.\n");
562 ptr
= ast_variable_retrieve(cfg
, "global", "user");
564 dbuser
= strdup(ptr
);
566 ast_log(LOG_ERROR
,"Database dbuser not specified.\n");
568 ptr
= ast_variable_retrieve(cfg
, "global", "password");
570 password
= strdup(ptr
);
572 ast_log(LOG_ERROR
,"Database password not specified.\n");
574 ptr
= ast_variable_retrieve(cfg
, "global", "charset");
576 charset
= strdup(ptr
);
578 charset
= strdup("iso_1");
580 ptr
= ast_variable_retrieve(cfg
, "global", "language");
582 language
= strdup(ptr
);
584 language
= strdup("us_english");
586 ptr
= ast_variable_retrieve(cfg
,"global","table");
588 ast_log(LOG_DEBUG
,"cdr_tds: table not specified. Assuming cdr\n");
593 ast_config_destroy(cfg
);
597 /* Register MSSQL CDR handler */
598 res
= ast_cdr_register(name
, ast_module_info
->description
, tds_log
);
601 ast_log(LOG_ERROR
, "Unable to register MSSQL CDR handling\n");
607 static int reload(void)
610 return tds_load_module();
613 static int load_module(void)
615 if(!tds_load_module())
616 return AST_MODULE_LOAD_DECLINE
;
618 return AST_MODULE_LOAD_SUCCESS
;
621 static int unload_module(void)
623 return tds_unload_module();
626 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "MSSQL CDR Backend",
628 .unload
= unload_module
,