Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:48:26 2009

## Asterisk developer's documentation

# cdr_tds.c File Reference

FreeTDS CDR logger. More...

`#include <sys/types.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>`
`#include "asterisk.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 dependency graph for cdr_tds.c:

Go to the source code of this file.

## Defines

#define | DATE_FORMAT "%Y/%m/%d %T"

## Functions

static char * | anti_injection (const char *, int)
| AST_MUTEX_DEFINE_STATIC (tds_lock)
char * | description (void)
| Provides a description of the module.
static void | get_date (char *, struct timeval)
char * | key ()
| Returns the ASTERISK_GPL_KEY.
int | load_module (void)
| Initialize the module.
static int | mssql_connect (void)
static int | mssql_disconnect (void)
int | reload (void)
| Reload stuff.
static int | tds_load_module (void)
static int | tds_log (struct ast_cdr *cdr)
static int | tds_unload_module (void)
int | unload_module (void)
| Cleanup all module structures, sockets, etc.
int | usecount (void)
| Provides a usecount.

## Variables

static char * | charset = NULL
static char * | config = "cdr_tds.conf"
static int | connected = 0
static TDSCONTEXT * | context
static char * | dbname = NULL
static char * | dbuser = NULL
static char * | desc = "MSSQL CDR Backend"
static char * | hostname = NULL
static char * | language = NULL
static TDSLOGIN * | login
static char * | name = "mssql"
static char * | password = NULL
static TDSSOCKET * | tds

## Detailed Description

FreeTDS CDR logger.

See also

- CDR configuration
- http://www.freetds.org/

Definition in file cdr_tds.c.

## Define Documentation

#define DATE_FORMAT "%Y/%m/%d %T"

     *
     * Table Structure for `cdr`
     *
     * Created on: 05/20/2004 16:16
     * Last changed on: 07/27/2004 20: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]

Definition at line 85 of file cdr_tds.c.

## Function Documentation

static char * anti_injection | ( | const char * | str,
|  | int | len |
| ) |  |  | `[static]`

Definition at line 235 of file cdr_tds.c.

References ast_log(), LOG_ERROR, malloc, and strcasestr().

Referenced by tds_log().

    00236 {
    00237    /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */
    00238
    00239    char *buf;
    00240    char *buf_ptr, *srh_ptr;
    00241    char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"};
    00242    int idx;
    00243
    00244    if ((buf = malloc(len + 1)) == NULL)
    00245    {
    00246       ast_log(LOG_ERROR, "cdr_tds:  Out of memory error\n");
    00247       return NULL;
    00248    }
    00249    memset(buf, 0, len);
    00250
    00251    buf_ptr = buf;
    00252
    00253    /* Escape single quotes */
    00254    for (; *str && strlen(buf) < len; str++)
    00255    {
    00256       if (*str == '\'')
    00257          *buf_ptr++ = '\'';
    00258       *buf_ptr++ = *str;
    00259    }
    00260    *buf_ptr = '\0';
    00261
    00262    /* Erase known bad input */
    00263    for (idx=0; *known_bad[idx]; idx++)
    00264    {
    00265       while((srh_ptr = strcasestr(buf, known_bad[idx])))
    00266       {
    00267          memmove(srh_ptr, srh_ptr+strlen(known_bad[idx]), strlen(srh_ptr+strlen(known_bad[idx]))+1);
    00268       }
    00269    }
    00270
    00271    return buf;
    00272 }

AST_MUTEX_DEFINE_STATIC | ( | tds_lock |  | ) |

char* description | ( | void |  | ) |

Provides a description of the module.

Returns:
  a short description of your module

Definition at line 294 of file cdr_tds.c.

    00295 {
    00296    return desc;
    00297 }

static void get_date | ( | char * | dateField,
|  | struct timeval | tv |
| ) |  |  | `[static]`

Definition at line 274 of file cdr_tds.c.

References DATE_FORMAT, and t.

    00275 {
    00276    struct tm tm;
    00277    time_t t;
    00278    char buf[80];
    00279
    00280    /* To make sure we have date variable if not insert null to SQL */
    00281    if (!ast_tvzero(tv))
    00282    {
    00283       t = tv.tv_sec;
    00284       localtime_r(&t, &tm);
    00285       strftime(buf, 80, DATE_FORMAT, &tm);
    00286       sprintf(dateField, "'%s'", buf);
    00287    }
    00288    else
    00289    {
    00290       strcpy(dateField, "null");
    00291    }
    00292 }

char* key | ( | void |  | ) |

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

     char *key(void) {
             return ASTERISK_GPL_KEY;
     }

Returns:
  ASTERISK_GPL_KEY

Definition at line 523 of file cdr_tds.c.

References ASTERISK_GPL_KEY.

    00524 {
    00525    return ASTERISK_GPL_KEY;
    00526 }

int load_module | ( | void |  | ) |

Initialize the module.

This function is called at module load time. Put all code in here that needs to set up your module's hardware, software, registrations, etc.

Returns:
  This function should return 0 on success and non-zero on failure. If the module is not loaded successfully, Asterisk will call its unload_module() function.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
  int Always 0.

TE STUFF END

Definition at line 502 of file cdr_tds.c.

References tds_load_module().

    00503 {
    00504    return tds_load_module();
    00505 }

static int mssql_connect | ( | void |  | ) | `[static]`

Definition at line 321 of file cdr_tds.c.

References ast_log(), LOG_ERROR, and mssql_disconnect().

Referenced by tds_load_module(), and tds_log().

    00322 {
    00323 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
    00324    TDSCONNECTION *connection = NULL;
    00325 #else
    00326    TDSCONNECTINFO *connection = NULL;
    00327 #endif
    00328    char query[128];
    00329
    00330    /* Connect to M$SQL Server */
    00331    if (!(login = tds_alloc_login()))
    00332    {
    00333       ast_log(LOG_ERROR, "tds_alloc_login() failed.\n");
    00334       return -1;
    00335    }
    00336
    00337    tds_set_server(login, hostname);
    00338    tds_set_user(login, dbuser);
    00339    tds_set_passwd(login, password);
    00340    tds_set_app(login, "TSQL");
    00341    tds_set_library(login, "TDS-Library");
    00342 #ifndef FREETDS_PRE_0_62
    00343    tds_set_client_charset(login, charset);
    00344 #endif
    00345    tds_set_language(login, language);
    00346    tds_set_packet(login, 512);
    00347    tds_set_version(login, 7, 0);
    00348
    00349 #ifdef FREETDS_0_64
    00350    if (!(context = tds_alloc_context(NULL)))
    00351 #else
    00352    if (!(context = tds_alloc_context()))
    00353 #endif
    00354    {
    00355       ast_log(LOG_ERROR, "tds_alloc_context() failed.\n");
    00356       goto connect_fail;
    00357    }
    00358
    00359    if (!(tds = tds_alloc_socket(context, 512))) {
    00360       ast_log(LOG_ERROR, "tds_alloc_socket() failed.\n");
    00361       goto connect_fail;
    00362    }
    00363
    00364    tds_set_parent(tds, NULL);
    00365    connection = tds_read_config_info(tds, login, context->locale);
    00366    if (!connection)
    00367    {
    00368       ast_log(LOG_ERROR, "tds_read_config() failed.\n");
    00369       goto connect_fail;
    00370    }
    00371
    00372    if (tds_connect(tds, connection) == TDS_FAIL)
    00373    {
    00374       ast_log(LOG_ERROR, "Failed to connect to MSSQL server.\n");
    00375       tds = NULL; /* freed by tds_connect() on error */
    00376 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
    00377       tds_free_connection(connection);
    00378 #else
    00379       tds_free_connect(connection);
    00380 #endif
    00381       connection = NULL;
    00382       goto connect_fail;
    00383    }
    00384 #if (defined(FREETDS_0_63) || defined(FREETDS_0_64))
    00385    tds_free_connection(connection);
    00386 #else
    00387    tds_free_connect(connection);
    00388 #endif
    00389    connection = NULL;
    00390
    00391    sprintf(query, "USE %s", dbname);
    00392 #ifdef FREETDS_PRE_0_62
    00393    if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
    00394 #else
    00395    if ((tds_submit_query(tds, query) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
    00396 #endif
    00397    {
    00398       ast_log(LOG_ERROR, "Could not change database (%s)\n", dbname);
    00399       goto connect_fail;
    00400    }
    00401
    00402    connected = 1;
    00403    return 0;
    00404
    00405 connect_fail:
    00406    mssql_disconnect();
    00407    return -1;
    00408 }

static int mssql_disconnect | ( | void |  | ) | `[static]`

Definition at line 299 of file cdr_tds.c.

Referenced by mssql_connect(), tds_log(), and tds_unload_module().

    00300 {
    00301    if (tds) {
    00302       tds_free_socket(tds);
    00303       tds = NULL;
    00304    }
    00305
    00306    if (context) {
    00307       tds_free_context(context);
    00308       context = NULL;
    00309    }
    00310
    00311    if (login) {
    00312       tds_free_login(login);
    00313       login = NULL;
    00314    }
    00315
    00316    connected = 0;
    00317
    00318    return 0;
    00319 }

int reload | ( | void |  | ) |

Reload stuff.

This function is where any reload routines take place. Re-read config files, change signalling, whatever is appropriate on a reload.

Returns:
  The return value is not used.

Definition at line 496 of file cdr_tds.c.

References tds_load_module(), and tds_unload_module().

    00497 {
    00498    tds_unload_module();
    00499    return tds_load_module();
    00500 }

static int tds_load_module | ( | void |  | ) | `[static]`

Definition at line 426 of file cdr_tds.c.

References ast_cdr_register(), ast_config_destroy(), ast_config_load(), ast_log(), ast_variable_browse(), ast_variable_retrieve(), cfg, LOG_ERROR, LOG_NOTICE, mssql_connect(), strdup, tds_log(), and var.

Referenced by load_module(), and reload().

    00427 {
    00428    int res = 0;
    00429    struct ast_config *cfg;
    00430    struct ast_variable *var;
    00431    char *ptr = NULL;
    00432 #ifdef FREETDS_PRE_0_62
    00433    TDS_INT result_type;
    00434 #endif
    00435
    00436    cfg = ast_config_load(config);
    00437    if (!cfg) {
    00438       ast_log(LOG_NOTICE, "Unable to load config for MSSQL CDR's: %s\n", config);
    00439       return 0;
    00440    }
    00441
    00442    var = ast_variable_browse(cfg, "global");
    00443    if (!var) /* nothing configured */
    00444       return 0;
    00445
    00446    ptr = ast_variable_retrieve(cfg, "global", "hostname");
    00447    if (ptr)
    00448       hostname = strdup(ptr);
    00449    else
    00450       ast_log(LOG_ERROR,"Database server hostname not specified.\n");
    00451
    00452    ptr = ast_variable_retrieve(cfg, "global", "dbname");
    00453    if (ptr)
    00454       dbname = strdup(ptr);
    00455    else
    00456       ast_log(LOG_ERROR,"Database dbname not specified.\n");
    00457
    00458    ptr = ast_variable_retrieve(cfg, "global", "user");
    00459    if (ptr)
    00460       dbuser = strdup(ptr);
    00461    else
    00462       ast_log(LOG_ERROR,"Database dbuser not specified.\n");
    00463
    00464    ptr = ast_variable_retrieve(cfg, "global", "password");
    00465    if (ptr)
    00466       password = strdup(ptr);
    00467    else
    00468       ast_log(LOG_ERROR,"Database password not specified.\n");
    00469
    00470    ptr = ast_variable_retrieve(cfg, "global", "charset");
    00471    if (ptr)
    00472       charset = strdup(ptr);
    00473    else
    00474       charset = strdup("iso_1");
    00475
    00476    ptr = ast_variable_retrieve(cfg, "global", "language");
    00477    if (ptr)
    00478       language = strdup(ptr);
    00479    else
    00480       language = strdup("us_english");
    00481
    00482    ast_config_destroy(cfg);
    00483
    00484    mssql_connect();
    00485
    00486    /* Register MSSQL CDR handler */
    00487    res = ast_cdr_register(name, desc, tds_log);
    00488    if (res)
    00489    {
    00490       ast_log(LOG_ERROR, "Unable to register MSSQL CDR handling\n");
    00491    }
    00492
    00493    return res;
    00494 }

static int tds_log | ( | struct ast_cdr * | cdr | ) | `[static]`

Definition at line 107 of file cdr_tds.c.

References ast_cdr::accountcode, accountcode, ast_cdr::amaflags, ast_cdr::answer, answer, anti_injection(), ast_cdr_disp2str(), ast_cdr_flags2str(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_cdr::billsec, ast_cdr::channel, ast_cdr::clid, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_cdr::duration, ast_cdr::end, free, get_date(), ast_cdr::lastapp, ast_cdr::lastdata, LOG_ERROR, LOG_WARNING, mssql_connect(), mssql_disconnect(), ast_cdr::src, ast_cdr::start, and ast_cdr::uniqueid.

Referenced by tds_load_module().

    00108 {
    00109    char sqlcmd[2048], start[80], answer[80], end[80];
    00110    char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid;
    00111    int res = 0;
    00112    int retried = 0;
    00113 #ifdef FREETDS_PRE_0_62
    00114    TDS_INT result_type;
    00115 #endif
    00116
    00117    ast_mutex_lock(&tds_lock);
    00118
    00119    memset(sqlcmd, 0, 2048);
    00120
    00121    accountcode = anti_injection(cdr->accountcode, 20);
    00122    src = anti_injection(cdr->src, 80);
    00123    dst = anti_injection(cdr->dst, 80);
    00124    dcontext = anti_injection(cdr->dcontext, 80);
    00125    clid = anti_injection(cdr->clid, 80);
    00126    channel = anti_injection(cdr->channel, 80);
    00127    dstchannel = anti_injection(cdr->dstchannel, 80);
    00128    lastapp = anti_injection(cdr->lastapp, 80);
    00129    lastdata = anti_injection(cdr->lastdata, 80);
    00130    uniqueid = anti_injection(cdr->uniqueid, 32);
    00131
    00132    get_date(start, cdr->start);
    00133    get_date(answer, cdr->answer);
    00134    get_date(end, cdr->end);
    00135
    00136    sprintf(
    00137       sqlcmd,
    00138       "INSERT INTO cdr "
    00139       "("
    00140          "accountcode, "
    00141          "src, "
    00142          "dst, "
    00143          "dcontext, "
    00144          "clid, "
    00145          "channel, "
    00146          "dstchannel, "
    00147          "lastapp, "
    00148          "lastdata, "
    00149          "start, "
    00150          "answer, "
    00151          "[end], "
    00152          "duration, "
    00153          "billsec, "
    00154          "disposition, "
    00155          "amaflags, "
    00156          "uniqueid"
    00157       ") "
    00158       "VALUES "
    00159       "("
    00160          "'%s', " /* accountcode */
    00161          "'%s', " /* src */
    00162          "'%s', " /* dst */
    00163          "'%s', " /* dcontext */
    00164          "'%s', " /* clid */
    00165          "'%s', " /* channel */
    00166          "'%s', " /* dstchannel */
    00167          "'%s', " /* lastapp */
    00168          "'%s', " /* lastdata */
    00169          "%s, "      /* start */
    00170          "%s, "      /* answer */
    00171          "%s, "      /* end */
    00172          "%ld, "     /* duration */
    00173          "%ld, "     /* billsec */
    00174          "'%s', " /* disposition */
    00175          "'%s', " /* amaflags */
    00176          "'%s'"      /* uniqueid */
    00177       ")",
    00178       accountcode,
    00179       src,
    00180       dst,
    00181       dcontext,
    00182       clid,
    00183       channel,
    00184       dstchannel,
    00185       lastapp,
    00186       lastdata,
    00187       start,
    00188       answer,
    00189       end,
    00190       cdr->duration,
    00191       cdr->billsec,
    00192       ast_cdr_disp2str(cdr->disposition),
    00193       ast_cdr_flags2str(cdr->amaflags),
    00194       uniqueid
    00195    );
    00196
    00197    do {
    00198       if (!connected) {
    00199          if (mssql_connect())
    00200             ast_log(LOG_ERROR, "Failed to reconnect to SQL database.\n");
    00201          else
    00202             ast_log(LOG_WARNING, "Reconnected to SQL database.\n");
    00203
    00204          retried = 1;   /* note that we have now tried */
    00205       }
    00206
    00207 #ifdef FREETDS_PRE_0_62
    00208       if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds, &result_type) != TDS_SUCCEED || result_type != TDS_CMD_SUCCEED))
    00209 #else
    00210       if (!connected || (tds_submit_query(tds, sqlcmd) != TDS_SUCCEED) || (tds_process_simple_query(tds) != TDS_SUCCEED))
    00211 #endif
    00212       {
    00213          ast_log(LOG_ERROR, "Failed to insert Call Data Record into SQL database.\n");
    00214
    00215          mssql_disconnect();  /* this is ok even if we are already disconnected */
    00216       }
    00217    } while (!connected && !retried);
    00218
    00219    free(accountcode);
    00220    free(src);
    00221    free(dst);
    00222    free(dcontext);
    00223    free(clid);
    00224    free(channel);
    00225    free(dstchannel);
    00226    free(lastapp);
    00227    free(lastdata);
    00228    free(uniqueid);
    00229
    00230    ast_mutex_unlock(&tds_lock);
    00231
    00232    return res;
    00233 }

static int tds_unload_module | ( | void |  | ) | `[static]`

Definition at line 410 of file cdr_tds.c.

References ast_cdr_unregister(), free, and mssql_disconnect().

Referenced by reload(), and unload_module().

    00411 {
    00412    mssql_disconnect();
    00413
    00414    ast_cdr_unregister(name);
    00415
    00416    if (hostname) free(hostname);
    00417    if (dbname) free(dbname);
    00418    if (dbuser) free(dbuser);
    00419    if (password) free(password);
    00420    if (charset) free(charset);
    00421    if (language) free(language);
    00422
    00423    return 0;
    00424 }

int unload_module | ( | void |  | ) |

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
  Zero on success, or non-zero on error.

Definition at line 507 of file cdr_tds.c.

References tds_unload_module().

    00508 {
    00509    return tds_unload_module();
    00510 }

int usecount | ( | void |  | ) |

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
  The module's usecount.

Definition at line 512 of file cdr_tds.c.

References ast_mutex_trylock(), and ast_mutex_unlock().

    00513 {
    00514    /* Simplistic use count */
    00515    if (ast_mutex_trylock(&tds_lock)) {
    00516       return 1;
    00517    } else {
    00518       ast_mutex_unlock(&tds_lock);
    00519       return 0;
    00520    }
    00521 }

## Variable Documentation

char * charset = NULL`[static]`

Definition at line 91 of file cdr_tds.c.

char* config = "cdr_tds.conf"`[static]`

Definition at line 89 of file cdr_tds.c.

int connected = 0`[static]`

Definition at line 93 of file cdr_tds.c.

TDSCONTEXT* context`[static]`

Definition at line 99 of file cdr_tds.c.

char * dbname = NULL`[static]`

Definition at line 91 of file cdr_tds.c.

Referenced by lookupcidname_exec().

char * dbuser = NULL`[static]`

Definition at line 91 of file cdr_tds.c.

char* desc = "MSSQL CDR Backend"`[static]`

Definition at line 87 of file cdr_tds.c.

char* hostname = NULL`[static]`

Definition at line 91 of file cdr_tds.c.

char * language = NULL`[static]`

Definition at line 91 of file cdr_tds.c.

TDSLOGIN* login`[static]`

Definition at line 98 of file cdr_tds.c.

char* name = "mssql"`[static]`

Definition at line 88 of file cdr_tds.c.

char * password = NULL`[static]`

Definition at line 91 of file cdr_tds.c.

TDSSOCKET* tds`[static]`

Definition at line 97 of file cdr_tds.c.