diff options
-rwxr-xr-x | cdr/Makefile | 9 | ||||
-rwxr-xr-x | cdr/cdr_tds.c | 446 | ||||
-rwxr-xr-x | configs/cdr_tds.conf.sample | 9 |
3 files changed, 464 insertions, 0 deletions
diff --git a/cdr/Makefile b/cdr/Makefile index dcc81c8ad..5ccd6343f 100755 --- a/cdr/Makefile +++ b/cdr/Makefile @@ -29,6 +29,12 @@ MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "cdr_odbc.so"; fi) MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "cdr_odbc.so"; fi) # +# FreeTDS stuff... +# +MODS+=$(shell if [ -f "/usr/include/tds.h" ]; then echo "cdr_tds.so"; fi) +MODS+=$(shell if [ -f "/usr/local/include/tds.h" ]; then echo "cdr_tds.so"; fi) + +# # PGSQL stuff... Autoconf anyone?? # MODS+=$(shell if [ -d /usr/local/pgsql/include ] || [ -d /usr/include/pgsql ] || [ -d /usr/local/include/pgsql ] || [ -d /opt/pgsql/include ] || [ -f /usr/include/libpq-fe.h ] ; then echo "cdr_pgsql.so"; fi) @@ -68,6 +74,9 @@ endif cdr_odbc.so: cdr_odbc.o $(CC) $(SOLINK) -o $@ $< -lodbc $(MLFLAGS) +cdr_tds.so: cdr_tds.o + $(CC) $(SOLINK) -o $@ $< -ltds $(MLFLAGS) + cdr_pgsql.so: cdr_pgsql.o $(CC) $(SOLINK) -o $@ $< -lpq -lz $(MLFLAGS) diff --git a/cdr/cdr_tds.c b/cdr/cdr_tds.c new file mode 100755 index 000000000..70ae889cc --- /dev/null +++ b/cdr/cdr_tds.c @@ -0,0 +1,446 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * FreeTDS CDR logger + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * + * + * Table Structure for `cdr` + * + * Created on: 05/20/2004 16:16 + * Last changed on: 07/19/2004 13:01 + +CREATE TABLE [dbo].[cdr] ( + [accountcode] [varchar] (20) NULL , + [src] [varchar] (80) NULL , + [dst] [varchar] (80) NULL , + [dcontext] [varchar] (80) NULL , + [clid] [varchar] (80) NULL , + [channel] [varchar] (80) NULL , + [dstchannel] [varchar] (80) NULL , + [lastapp] [varchar] (80) NULL , + [lastdata] [varchar] (80) NULL , + [start] [datetime] NULL , + [answer] [datetime] NULL , + [end] [datetime] NULL , + [duration] [int] NULL , + [billsec] [int] NULL , + [disposition] [varchar] (20) NULL , + [amaflags] [varchar] (16) NULL , + [uniqueid] [varchar] (32) NULL +) ON [PRIMARY] + +*/ + +#include <sys/types.h> +#include <asterisk/config.h> +#include <asterisk/options.h> +#include <asterisk/channel.h> +#include <asterisk/cdr.h> +#include <asterisk/module.h> +#include <asterisk/logger.h> +#include "../asterisk.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <math.h> + +#include <tds.h> +#include <tdsconvert.h> +#include <ctype.h> + +#define DATE_FORMAT "%Y/%m/%d %T" + +static char *desc = "MSSQL CDR Backend"; +static char *name = "mssql"; +static char *config = "cdr_tds.conf"; + +AST_MUTEX_DEFINE_STATIC(tds_lock); + +static TDSSOCKET *tds; +static TDSLOGIN *login; +static TDSCONTEXT *context; + +char *stristr(const char*, const char*); +char *anti_injection(const char *, int); + +static int tds_log(struct ast_cdr *cdr) +{ + struct tm tm; + time_t t; + char sqlcmd[2048], start[80], answer[80], end[80]; + char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid; + int res = 0; + + ast_mutex_lock(&tds_lock); + + memset(sqlcmd, 0, 2048); + + accountcode = anti_injection(cdr->accountcode, 20); + src = anti_injection(cdr->src, 80); + dst = anti_injection(cdr->dst, 80); + dcontext = anti_injection(cdr->dcontext, 80); + clid = anti_injection(cdr->clid, 80); + channel = anti_injection(cdr->channel, 80); + dstchannel = anti_injection(cdr->dstchannel, 80); + lastapp = anti_injection(cdr->lastapp, 80); + lastdata = anti_injection(cdr->lastdata, 80); + uniqueid = anti_injection(cdr->uniqueid, 32); + + t = cdr->start.tv_sec; + localtime_r(&t, &tm); + strftime(start, 80, DATE_FORMAT, &tm); + t = cdr->answer.tv_sec; + localtime_r(&t, &tm); + strftime(answer, 80, DATE_FORMAT, &tm); + t = cdr->end.tv_sec; + localtime_r(&t, &tm); + strftime(end, 80, DATE_FORMAT, &tm); + + sprintf(sqlcmd, "INSERT INTO cdr (accountcode, src, dst, dcontext, clid, channel, dstchannel, lastapp, lastdata, start, answer, [end], duration, billsec, disposition, amaflags, uniqueid) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %i, %i, '%s', '%s', '%s')", accountcode, src, dst, dcontext, clid, channel, dstchannel, lastapp, lastdata, start, answer, end, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid); + + if ((tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED)) + { + ast_log(LOG_ERROR, "Failed to insert record into database.\n"); + + res = -1; + } + + free(accountcode); + free(src); + free(dst); + free(dcontext); + free(clid); + free(channel); + free(dstchannel); + free(lastapp); + free(lastdata); + free(uniqueid); + + ast_mutex_unlock(&tds_lock); + + return res; +} + +/* Return the offset of one string within another. + Copyright (C) 1994, 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * My personal strstr() implementation that beats most other algorithms. + * Until someone tells me otherwise, I assume that this is the + * fastest implementation of strstr() in C. + * I deliberately chose not to comment it. You should have at least + * as much fun trying to understand it, as I had to write it :-). + * + * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ + +char * +stristr (phaystack, pneedle) + const char *phaystack; + const char *pneedle; +{ + typedef unsigned chartype; + + const unsigned char *haystack, *needle; + chartype b; + const unsigned char *rneedle; + + haystack = (const unsigned char *) phaystack; + + if ((b = toupper(*(needle = (const unsigned char *) pneedle)))) + { + chartype c; + haystack--; /* possible ANSI violation */ + + { + chartype a; + do + if (!(a = toupper(*++haystack))) + goto ret0; + while (a != b); + } + + if (!(c = toupper(*++needle))) + goto foundneedle; + ++needle; + goto jin; + + for (;;) + { + { + chartype a; + if (0) + jin:{ + if ((a = toupper(*++haystack)) == c) + goto crest; + } + else + a = toupper(*++haystack); + do + { + for (; a != b; a = toupper(*++haystack)) + { + if (!a) + goto ret0; + if ((a = toupper(*++haystack)) == b) + break; + if (!a) + goto ret0; + } + } + while ((a = toupper(*++haystack)) != c); + } + crest: + { + chartype a; + { + const unsigned char *rhaystack; + if (toupper(*(rhaystack = haystack-- + 1)) == (a = toupper(*(rneedle = needle)))) + do + { + if (!a) + goto foundneedle; + if (toupper(*++rhaystack) != (a = toupper(*++needle))) + break; + if (!a) + goto foundneedle; + } + while (toupper(*++rhaystack) == (a = toupper(*++needle))); + needle = rneedle; /* took the register-poor aproach */ + } + if (!a) + break; + } + } + } +foundneedle: + return (char *) haystack; +ret0: + return 0; +} + +char *anti_injection(const char *str, int len) +{ + /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */ + + char *buf; + char *buf_ptr, *srh_ptr; + char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"}; + int idx; + + if ((buf = malloc(len + 1)) == NULL) + { + ast_log(LOG_ERROR, "cdr_tds: Out of memory error\n"); + return NULL; + } + memset(buf, 0, len); + + buf_ptr = buf; + + /* Escape single quotes */ + for (; *str && strlen(buf) < len; str++) + { + if (*str == '\'') + *buf_ptr++ = '\''; + *buf_ptr++ = *str; + } + *buf_ptr = '\0'; + + /* Erase known bad input */ + for (idx=0; *known_bad[idx]; idx++) + { + while(srh_ptr = stristr(buf, known_bad[idx])) /* fix me! */ + { + memmove(srh_ptr, srh_ptr+strlen(known_bad[idx]), strlen(srh_ptr+strlen(known_bad[idx]))+1); + } + } + + return buf; +} + +char *description(void) +{ + return desc; +} + +int unload_module(void) +{ + tds_free_socket(tds); + tds_free_login(login); + tds_free_context(context); + + ast_cdr_unregister(name); + + return 0; +} + +int load_module(void) +{ + TDSCONNECTINFO *connection; + int res = 0; + struct ast_config *cfg; + struct ast_variable *var; + char query[1024], *ptr = NULL; + char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *charset = NULL, *language = NULL; + + cfg = ast_load(config); + if (!cfg) + { + ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDR's: %s\n", config); + return 0; + } + + var = ast_variable_browse(cfg, "global"); + if (!var) /* nothing configured */ + return 0; + + ptr = ast_variable_retrieve(cfg, "global", "hostname"); + if (ptr) + { + hostname = strdupa(ptr); + } + else + { + ast_log(LOG_ERROR,"Database server hostname not specified.\n"); + } + + ptr = ast_variable_retrieve(cfg, "global", "dbname"); + if (ptr) + { + dbname = strdupa(ptr); + } + else + { + ast_log(LOG_ERROR,"Database dbname not specified.\n"); + } + + ptr = ast_variable_retrieve(cfg, "global", "user"); + if (ptr) + { + dbuser = strdupa(ptr); + } + else + { + ast_log(LOG_ERROR,"Database dbuser not specified.\n"); + } + + ptr = ast_variable_retrieve(cfg, "global", "password"); + if (ptr) + { + password = strdupa(ptr); + } + else + { + ast_log(LOG_ERROR,"Database password not specified.\n"); + } + + ptr = ast_variable_retrieve(cfg, "global", "charset"); + if (ptr) + { + charset = strdupa(ptr); + } + else + { + charset = strdupa("iso_1"); + } + + ptr = ast_variable_retrieve(cfg, "global", "language"); + if (ptr) + { + language = strdupa(ptr); + } + else + { + language = strdupa("us_english"); + } + + ast_destroy(cfg); + + /* Connect to M$SQL Server */ + if (!(login = tds_alloc_login())) + { + ast_log(LOG_ERROR, "tds_alloc_login() failed.\n"); + res = -1; + } + else + { + tds_set_server(login, hostname); + tds_set_user(login, dbuser); + tds_set_passwd(login, password); + tds_set_app(login, "TSQL"); + tds_set_library(login, "TDS-Library"); + tds_set_client_charset(login, charset); + tds_set_language(login, language); + tds_set_packet(login, 512); + tds_set_version(login, 7, 0); + + context = tds_alloc_context(); + tds = tds_alloc_socket(context, 512); + + tds_set_parent(tds, NULL); + connection = tds_read_config_info(NULL, login, context->locale); + if (!connection || tds_connect(tds, connection) == TDS_FAIL) + { + ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n"); + res = -1; + } + tds_free_connect(connection); + + if (!res) + { + memset(query, 0, sizeof(query)); + sprintf(query, "USE %s", dbname); + if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED)) + { + ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname); + res = -1; + } + else + { + /* Register MSSQL CDR handler */ + res = ast_cdr_register(name, desc, tds_log); + if (res) + { + ast_log(LOG_ERROR, "Unable to register MSSQL CDR handling\n"); + } + } + } + } + return res; +} + +int reload(void) +{ + return 0; +} + +int usecount(void) +{ + return 0; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff --git a/configs/cdr_tds.conf.sample b/configs/cdr_tds.conf.sample new file mode 100755 index 000000000..9fffec099 --- /dev/null +++ b/configs/cdr_tds.conf.sample @@ -0,0 +1,9 @@ +; Sample Asterisk config file for CDR logging to FreeTDS + +;[global] +;hostname=fs.malico.loc +;port=1433 +;dbname=MalicoHN +;user=mangUsr +;password= +;charset=BIG5 |