Asterisk - The Open Source Telephony Project GIT-master-5467495
Loading...
Searching...
No Matches
cel_tds.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2008, 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
17/*! \file
18 *
19 * \brief FreeTDS CEL logger
20 * http://www.freetds.org/
21 * \ingroup cel_drivers
22 */
23
24/*! \verbatim
25 *
26 * Table Structure for `cel`
27 *
28
29CREATE TABLE [dbo].[cel] (
30 [accountcode] [varchar] (20) NULL ,
31 [cidname] [varchar] (80) NULL ,
32 [cidnum] [varchar] (80) NULL ,
33 [cidani] [varchar] (80) NULL ,
34 [cidrdnis] [varchar] (80) NULL ,
35 [ciddnid] [varchar] (80) NULL ,
36 [exten] [varchar] (80) NULL ,
37 [context] [varchar] (80) NULL ,
38 [channame] [varchar] (80) NULL ,
39 [appname] [varchar] (80) NULL ,
40 [appdata] [varchar] (80) NULL ,
41 [eventtime] [datetime] NULL ,
42 [eventtype] [varchar] (32) NULL ,
43 [uniqueid] [varchar] (32) NULL ,
44 [linkedid] [varchar] (32) NULL ,
45 [amaflags] [varchar] (16) NULL ,
46 [userfield] [varchar] (32) NULL ,
47 [peer] [varchar] (32) NULL
48) ON [PRIMARY]
49
50\endverbatim
51
52*/
53
54/*** MODULEINFO
55 <depend>freetds</depend>
56 <support_level>extended</support_level>
57 ***/
58
59#include "asterisk.h"
60
61#include <time.h>
62#include <math.h>
63
64#include "asterisk/config.h"
65#include "asterisk/channel.h"
66#include "asterisk/cel.h"
67#include "asterisk/module.h"
68#include "asterisk/logger.h"
69
70#include <sqlfront.h>
71#include <sybdb.h>
72
73#ifdef FREETDS_PRE_0_62
74#warning "You have older TDS, you should upgrade!"
75#endif
76
77#define DATE_FORMAT "%Y/%m/%d %T"
78
79#define TDS_BACKEND_NAME "CEL TDS logging backend"
80
81static char *config = "cel_tds.conf";
82
96
98
99static struct cel_tds_config *settings;
100
101static char *anti_injection(const char *, int);
102static void get_date(char *, size_t len, struct timeval);
103
104static int execute_and_consume(DBPROCESS *dbproc, const char *fmt, ...)
105 __attribute__((format(printf, 2, 3)));
106
107static int mssql_connect(void);
108static int mssql_disconnect(void);
109
110static void tds_log(struct ast_event *event)
111{
112 char start[80];
113 char *accountcode_ai, *clidnum_ai, *exten_ai, *context_ai, *clid_ai, *channel_ai, *app_ai, *appdata_ai, *uniqueid_ai, *linkedid_ai, *cidani_ai, *cidrdnis_ai, *ciddnid_ai, *peer_ai, *userfield_ai, *eventtype_ai;
114 RETCODE erc;
115 int attempt = 1;
116 struct ast_cel_event_record record = {
118 };
119
120 if (ast_cel_fill_record(event, &record)) {
121 return;
122 }
123
125
126 accountcode_ai = anti_injection(record.account_code, 20);
127 clidnum_ai = anti_injection(record.caller_id_num, 80);
128 clid_ai = anti_injection(record.caller_id_name, 80);
129 cidani_ai = anti_injection(record.caller_id_ani, 80);
130 cidrdnis_ai = anti_injection(record.caller_id_rdnis, 80);
131 ciddnid_ai = anti_injection(record.caller_id_dnid, 80);
132 exten_ai = anti_injection(record.extension, 80);
133 context_ai = anti_injection(record.context, 80);
134 channel_ai = anti_injection(record.channel_name, 80);
135 app_ai = anti_injection(record.application_name, 80);
136 appdata_ai = anti_injection(record.application_data, 80);
137 uniqueid_ai = anti_injection(record.unique_id, 32);
138 linkedid_ai = anti_injection(record.linked_id, 32);
139 userfield_ai = anti_injection(record.user_field, 32);
140 peer_ai = anti_injection(record.peer, 32);
141 eventtype_ai = anti_injection(
143 ? record.user_defined_name : record.event_name, 32);
144
145 get_date(start, sizeof(start), record.event_time);
146
147retry:
148 /* Ensure that we are connected */
149 if (!settings->connected) {
150 ast_log(LOG_NOTICE, "Attempting to reconnect to %s (Attempt %d)\n", settings->connection, attempt);
151 if (mssql_connect()) {
152 /* Connect failed */
153 if (attempt++ < 3) {
154 goto retry;
155 }
156 goto done;
157 }
158 }
159
160 erc = dbfcmd(settings->dbproc,
161 "INSERT INTO %s "
162 "("
163 "accountcode,"
164 "cidnum,"
165 "cidname,"
166 "cidani,"
167 "cidrdnis,"
168 "ciddnid,"
169 "exten,"
170 "context,"
171 "channel,"
172 "appname,"
173 "appdata,"
174 "eventtime,"
175 "eventtype,"
176 "amaflags, "
177 "uniqueid,"
178 "linkedid,"
179 "userfield,"
180 "peer"
181 ") "
182 "VALUES "
183 "("
184 "'%s'," /* accountcode */
185 "'%s'," /* clidnum */
186 "'%s'," /* clid */
187 "'%s'," /* cid-ani */
188 "'%s'," /* cid-rdnis */
189 "'%s'," /* cid-dnid */
190 "'%s'," /* exten */
191 "'%s'," /* context */
192 "'%s'," /* channel */
193 "'%s'," /* app */
194 "'%s'," /* appdata */
195 "%s, " /* eventtime */
196 "'%s'," /* eventtype */
197 "'%s'," /* amaflags */
198 "'%s'," /* uniqueid */
199 "'%s'," /* linkedid */
200 "'%s'," /* userfield */
201 "'%s'" /* peer */
202 ")",
203 settings->table, accountcode_ai, clidnum_ai, clid_ai, cidani_ai, cidrdnis_ai,
204 ciddnid_ai, exten_ai, context_ai, channel_ai, app_ai, appdata_ai, start,
205 eventtype_ai,
206 ast_channel_amaflags2string(record.amaflag), uniqueid_ai, linkedid_ai,
207 userfield_ai, peer_ai);
208
209 if (erc == FAIL) {
210 if (attempt++ < 3) {
211 ast_log(LOG_NOTICE, "Failed to build INSERT statement, retrying...\n");
213 goto retry;
214 } else {
215 ast_log(LOG_ERROR, "Failed to build INSERT statement, no CEL was logged.\n");
216 goto done;
217 }
218 }
219
220 if (dbsqlexec(settings->dbproc) == FAIL) {
221 if (attempt++ < 3) {
222 ast_log(LOG_NOTICE, "Failed to execute INSERT statement, retrying...\n");
224 goto retry;
225 } else {
226 ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CEL was logged.\n");
227 goto done;
228 }
229 }
230
231 /* Consume any results we might get back (this is more of a sanity check than
232 * anything else, since an INSERT shouldn't return results). */
233 while (dbresults(settings->dbproc) != NO_MORE_RESULTS) {
234 while (dbnextrow(settings->dbproc) != NO_MORE_ROWS);
235 }
236
237done:
239
240 ast_free(accountcode_ai);
241 ast_free(clidnum_ai);
242 ast_free(clid_ai);
243 ast_free(cidani_ai);
244 ast_free(cidrdnis_ai);
245 ast_free(ciddnid_ai);
246 ast_free(exten_ai);
247 ast_free(context_ai);
248 ast_free(channel_ai);
249 ast_free(app_ai);
250 ast_free(appdata_ai);
251 ast_free(uniqueid_ai);
252 ast_free(linkedid_ai);
253 ast_free(userfield_ai);
254 ast_free(peer_ai);
255 ast_free(eventtype_ai);
256
257 return;
258}
259
260static char *anti_injection(const char *str, int len)
261{
262 /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
263 char *buf;
264 char *buf_ptr, *srh_ptr;
265 char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
266 int idx;
267
268 if (!(buf = ast_calloc(1, len + 1))) {
269 ast_log(LOG_ERROR, "Out of memory\n");
270 return NULL;
271 }
272
273 buf_ptr = buf;
274
275 /* Escape single quotes */
276 for (; *str && strlen(buf) < len; str++) {
277 if (*str == '\'') {
278 *buf_ptr++ = '\'';
279 }
280 *buf_ptr++ = *str;
281 }
282 *buf_ptr = '\0';
283
284 /* Erase known bad input */
285 for (idx = 0; *known_bad[idx]; idx++) {
286 while ((srh_ptr = strcasestr(buf, known_bad[idx]))) {
287 memmove(srh_ptr, srh_ptr + strlen(known_bad[idx]), strlen(srh_ptr + strlen(known_bad[idx])) + 1);
288 }
289 }
290 return buf;
291}
292
293static void get_date(char *dateField, size_t len, struct timeval when)
294{
295 /* To make sure we have date variable if not insert null to SQL */
296 if (!ast_tvzero(when)) {
297 struct ast_tm tm;
298 ast_localtime(&when, &tm, NULL);
299 ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm);
300 } else {
301 ast_copy_string(dateField, "null", len);
302 }
303}
304
305static int execute_and_consume(DBPROCESS *dbproc, const char *fmt, ...)
306{
307 va_list ap;
308 char *buffer;
309
310 va_start(ap, fmt);
311 if (ast_vasprintf(&buffer, fmt, ap) < 0) {
312 va_end(ap);
313 return 1;
314 }
315 va_end(ap);
316
317 if (dbfcmd(dbproc, buffer) == FAIL) {
318 ast_free(buffer);
319 return 1;
320 }
321
322 ast_free(buffer);
323
324 if (dbsqlexec(dbproc) == FAIL) {
325 return 1;
326 }
327
328 /* Consume the result set (we don't really care about the result, though) */
329 while (dbresults(dbproc) != NO_MORE_RESULTS) {
330 while (dbnextrow(dbproc) != NO_MORE_ROWS);
331 }
332
333 return 0;
334}
335
336static int mssql_disconnect(void)
337{
338 if (settings->dbproc) {
339 dbclose(settings->dbproc);
341 }
342 settings->connected = 0;
343
344 return 0;
345}
346
347static int mssql_connect(void)
348{
349 LOGINREC *login;
350
351 if ((login = dblogin()) == NULL) {
352 ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n");
353 return -1;
354 }
355
356 DBSETLAPP(login, "TSQL");
357 DBSETLUSER(login, (char *) settings->username);
358 DBSETLPWD(login, (char *) settings->password);
359
361 DBSETLCHARSET(login, (char *) settings->charset);
362 }
363
365 DBSETLNATLANG(login, (char *) settings->language);
366 }
367
368 if ((settings->dbproc = dbopen(login, (char *) settings->connection)) == NULL) {
369 ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->connection);
370 dbloginfree(login);
371 return -1;
372 }
373
374 dbloginfree(login);
375
376 if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) {
377 ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database);
378 goto failed;
379 }
380
381 if (execute_and_consume(settings->dbproc, "SELECT 1 FROM [%s]", settings->table)) {
382 ast_log(LOG_ERROR, "Unable to find table '%s'\n", settings->table);
383 goto failed;
384 }
385
386 settings->connected = 1;
387
388 return 0;
389
390failed:
391 dbclose(settings->dbproc);
393 return -1;
394}
395
396static int tds_unload_module(void)
397{
399
400 if (settings) {
404
407 }
408
409 dbexit();
410
411 return 0;
412}
413
414static int tds_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
415{
416 ast_log(LOG_ERROR, "%s (%d)\n", dberrstr, dberr);
417
418 if (oserr != DBNOERR) {
419 ast_log(LOG_ERROR, "%s (%d)\n", oserrstr, oserr);
420 }
421
422 return INT_CANCEL;
423}
424
425static int tds_message_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
426{
427 ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line);
428 ast_log(LOG_NOTICE, "%s\n", msgtext);
429
430 return 0;
431}
432
433static int tds_load_module(int reload)
434{
435 struct ast_config *cfg;
436 const char *ptr = NULL;
437 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
438
439 cfg = ast_config_load(config, config_flags);
440 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
441 ast_log(LOG_NOTICE, "Unable to load TDS config for CELs: %s\n", config);
442 return 0;
443 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
444 return 0;
445 }
446
447 if (!ast_variable_browse(cfg, "global")) {
448 /* nothing configured */
450 ast_log(LOG_NOTICE, "cel_tds has no global category, nothing to configure.\n");
451 return 0;
452 }
453
455
456 /* Clear out any existing settings */
458
459 ptr = ast_variable_retrieve(cfg, "global", "connection");
460 if (ptr) {
461 ast_string_field_set(settings, connection, ptr);
462 } else {
463 ast_log(LOG_ERROR, "Failed to connect: Database connection name not specified.\n");
464 goto failed;
465 }
466
467 ptr = ast_variable_retrieve(cfg, "global", "dbname");
468 if (ptr) {
469 ast_string_field_set(settings, database, ptr);
470 } else {
471 ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n");
472 goto failed;
473 }
474
475 ptr = ast_variable_retrieve(cfg, "global", "user");
476 if (ptr) {
477 ast_string_field_set(settings, username, ptr);
478 } else {
479 ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n");
480 goto failed;
481 }
482
483 ptr = ast_variable_retrieve(cfg, "global", "password");
484 if (ptr) {
485 ast_string_field_set(settings, password, ptr);
486 } else {
487 ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n");
488 goto failed;
489 }
490
491 ptr = ast_variable_retrieve(cfg, "global", "charset");
492 if (ptr) {
494 }
495
496 ptr = ast_variable_retrieve(cfg, "global", "language");
497 if (ptr) {
499 }
500
501 ptr = ast_variable_retrieve(cfg, "global", "table");
502 if (ptr) {
503 ast_string_field_set(settings, table, ptr);
504 } else {
505 ast_log(LOG_NOTICE, "Table name not specified, using 'cel' by default.\n");
506 ast_string_field_set(settings, table, "cel");
507 }
508
510
511 if (mssql_connect()) {
512 /* We failed to connect (mssql_connect takes care of logging it) */
513 goto failed;
514 }
515
518
519 return 1;
520
521failed:
524
525 return 0;
526}
527
528static int reload(void)
529{
530 return tds_load_module(1);
531}
532
533static int load_module(void)
534{
535 if (dbinit() == FAIL) {
536 ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n");
538 }
539
540 dberrhandle(tds_error_handler);
541 dbmsghandle(tds_message_handler);
542
544
545 if (!settings) {
546 dbexit();
548 }
549
550 if (!tds_load_module(0)) {
553 settings = NULL;
554 dbexit();
555 ast_log(LOG_WARNING,"cel_tds module had config problems; declining load\n");
557 }
558
559 /* Register MSSQL CEL handler */
561 ast_log(LOG_ERROR, "Unable to register MSSQL CEL handling\n");
564 settings = NULL;
565 dbexit();
567 }
568
570}
571
572static int unload_module(void)
573{
574 return tds_unload_module();
575}
576
578 .support_level = AST_MODULE_SUPPORT_EXTENDED,
579 .load = load_module,
580 .unload = unload_module,
581 .reload = reload,
582 .load_pri = AST_MODPRI_CDR_DRIVER,
583 .requires = "cel",
const char * str
Definition app_jack.c:150
char * strcasestr(const char *, const char *)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition astmm.h:180
#define ast_vasprintf(ret, fmt, ap)
A wrapper for vasprintf()
Definition astmm.h:278
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
Call Event Logging API.
int ast_cel_backend_unregister(const char *name)
Unregister a CEL backend.
Definition cel.c:1824
@ AST_CEL_USER_DEFINED
a user-defined event, the event name field should be set
Definition cel.h:70
int ast_cel_fill_record(const struct ast_event *event, struct ast_cel_event_record *r)
Fill in an ast_cel_event_record from a CEL event.
Definition cel.c:870
#define AST_CEL_EVENT_RECORD_VERSION
struct ABI version
Definition cel.h:162
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
Register a CEL backend.
Definition cel.c:1836
#define TDS_BACKEND_NAME
Definition cel_tds.c:79
static char * anti_injection(const char *, int)
Definition cel_tds.c:260
static int mssql_connect(void)
Definition cel_tds.c:347
static void tds_log(struct ast_event *event)
Definition cel_tds.c:110
static struct cel_tds_config * settings
Definition cel_tds.c:99
static void get_date(char *, size_t len, struct timeval)
Definition cel_tds.c:293
static char * config
Definition cel_tds.c:81
static int tds_error_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
Definition cel_tds.c:414
static int execute_and_consume(DBPROCESS *dbproc, const char *fmt,...)
Definition cel_tds.c:305
static int tds_message_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
Definition cel_tds.c:425
static ast_mutex_t tds_lock
Definition cel_tds.c:97
static int mssql_disconnect(void)
Definition cel_tds.c:336
static int tds_unload_module(void)
Definition cel_tds.c:396
static int load_module(void)
Definition cel_tds.c:533
static int unload_module(void)
Definition cel_tds.c:572
static int reload(void)
Definition cel_tds.c:528
static int tds_load_module(int reload)
Definition cel_tds.c:433
#define DATE_FORMAT
Definition cel_tds.c:77
static char language[MAX_LANGUAGE]
Definition chan_iax2.c:361
charset
General Asterisk PBX channel definitions.
const char * ast_channel_amaflags2string(enum ama_flags flags)
Convert the enum representation of an AMA flag to a string representation.
Definition channel.c:4395
char buf[BUFSIZE]
Definition eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition localtime.c:2524
#define ast_mutex_unlock(a)
Definition lock.h:197
#define ast_mutex_lock(a)
Definition lock.h:196
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition lock.h:527
enum ast_security_event_severity severity
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_CDR_DRIVER
Definition module.h:345
@ AST_MODULE_SUPPORT_EXTENDED
Definition module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
#define NULL
Definition resample.c:96
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
Helper struct for getting the fields out of a CEL event.
Definition cel.h:157
const char * caller_id_dnid
Definition cel.h:176
const char * application_data
Definition cel.h:181
const char * account_code
Definition cel.h:182
const char * caller_id_rdnis
Definition cel.h:175
const char * extension
Definition cel.h:177
const char * caller_id_num
Definition cel.h:173
const char * channel_name
Definition cel.h:179
const char * linked_id
Definition cel.h:185
const char * peer
Definition cel.h:189
enum ast_cel_event_type event_type
Definition cel.h:168
const char * unique_id
Definition cel.h:184
const char * user_defined_name
Definition cel.h:171
const char * context
Definition cel.h:178
const char * application_name
Definition cel.h:180
struct timeval event_time
Definition cel.h:169
uint32_t version
struct ABI version
Definition cel.h:167
const char * user_field
Definition cel.h:188
const char * caller_id_ani
Definition cel.h:174
const char * caller_id_name
Definition cel.h:172
const char * event_name
Definition cel.h:170
An event.
Definition event.c:81
Structure used to handle boolean flags.
Definition utils.h:220
DBPROCESS * dbproc
Definition cel_tds.c:93
const ast_string_field charset
Definition cel_tds.c:92
const ast_string_field language
Definition cel_tds.c:92
const ast_string_field database
Definition cel_tds.c:92
const ast_string_field password
Definition cel_tds.c:92
const ast_string_field username
Definition cel_tds.c:92
const ast_string_field table
Definition cel_tds.c:92
unsigned int connected
Definition cel_tds.c:94
const ast_string_field connection
Definition cel_tds.c:92
int done
Time-related functions and macros.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition time.h:117