# HG changeset patch # User Sebastien Decugis # Date 1272448448 -32400 # Node ID 397cdcd41f53f5755d35298581b8caf2e6007d51 # Parent f433bc04eb6a834591d0245e9f216f9563fca6a0 New app_acct in progress, backup only diff -r f433bc04eb6a -r 397cdcd41f53 cmake/Modules/FindGnuTLS.cmake --- a/cmake/Modules/FindGnuTLS.cmake Tue Apr 27 15:46:21 2010 +0900 +++ b/cmake/Modules/FindGnuTLS.cmake Wed Apr 28 18:54:08 2010 +0900 @@ -5,9 +5,9 @@ # GNUTLS_INCLUDE_DIRS - where to find gnutls.h, etc. # GNUTLS_LIBRARIES - List of libraries when using gnutls. -if (GNUTLS_INCLUDE_DIRS) +if (GNUTLS_INCLUDE_DIRS AND GNUTLS_LIBRARIES) set(GNUTLS_FIND_QUIETLY TRUE) -endif (GNUTLS_INCLUDE_DIRS) +endif (GNUTLS_INCLUDE_DIRS AND GNUTLS_LIBRARIES) # Include dir find_path(GNUTLS_INCLUDE_DIR diff -r f433bc04eb6a -r 397cdcd41f53 cmake/Modules/FindPostgreSQL.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/Modules/FindPostgreSQL.cmake Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,36 @@ +# - Find PostgreSQL library +# +# This module defines: +# POSTGRESQL_FOUND - True if the package is found +# POSTGRESQL_INCLUDE_DIR - containing libpq-fe.h +# POSTGRESQL_LIBRARIES - Libraries to link to use PQ functions. + +if (POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) + set(POSTGRESQL_FIND_QUIETLY TRUE) +endif (POSTGRESQL_INCLUDE_DIR AND POSTGRESQL_LIBRARIES) + +# Include dir +find_path(POSTGRESQL_INCLUDE_DIR + NAMES libpq-fe.h + PATH_SUFFIXES postgresql +) + +# Library +find_library(POSTGRESQL_LIBRARY + NAMES pq +) + +# handle the QUIETLY and REQUIRED arguments and set POSTGRESQL_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(POSTGRESQL DEFAULT_MSG POSTGRESQL_LIBRARY POSTGRESQL_INCLUDE_DIR) + +IF(POSTGRESQL_FOUND) + SET( POSTGRESQL_LIBRARIES ${POSTGRESQL_LIBRARY} ) +ELSE(POSTGRESQL_FOUND) + SET( POSTGRESQL_LIBRARIES ) +ENDIF(POSTGRESQL_FOUND) + +# Lastly make it so that the POSTGRESQL_LIBRARY and POSTGRESQL_INCLUDE_DIR variables +# only show up under the advanced options in the gui cmake applications. +MARK_AS_ADVANCED( POSTGRESQL_LIBRARIES POSTGRESQL_INCLUDE_DIR ) diff -r f433bc04eb6a -r 397cdcd41f53 cmake/Modules/FindSCTP.cmake --- a/cmake/Modules/FindSCTP.cmake Tue Apr 27 15:46:21 2010 +0900 +++ b/cmake/Modules/FindSCTP.cmake Wed Apr 28 18:54:08 2010 +0900 @@ -5,9 +5,9 @@ # SCTP_INCLUDE_DIRS - the SCTP include directories # SCTP_LIBRARIES - link these to use SCTP -if (SCTP_INCLUDE_DIRS) +if (SCTP_INCLUDE_DIRS AND SCTP_LIBRARIES) set(SCTP_FIND_QUIETLY TRUE) -endif (SCTP_INCLUDE_DIRS) +endif (SCTP_INCLUDE_DIRS AND SCTP_LIBRARIES) # Include dir find_path(SCTP_INCLUDE_DIR diff -r f433bc04eb6a -r 397cdcd41f53 doc/app_acct.conf.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/app_acct.conf.sample Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,73 @@ +####################### +# This file contains the description of configuration and general information about the +# "App_Acct" extension. + +# This extension provides a simple Diameter Accounting server. +# The extension does receive the Accounting-Request message, then +# saves the contents of the AVP in a database, as configured here. +# The data is not processed at all. The intent is that a dedicated +# application will then pull from this database and process the data (consolidate +# sessions, ...) + +# In order to enable this extension, the main freeDiameter configuration file +# must contain the following declaration: +# LoadExtension = "extensions/app_acct.fdx" : "/path/to/app_acct.conf" ; +####################### + +################# +## Part I: AVP ## +################# + +# Configure here the AVPs that must be stored in the database. +# The format is: +# "AVP-dictionary-name" = { +# field = "db-field-name"; +# required; +# multi=N; +# }; +# Where: +# AVP-dictionary-name: +# The name of the AVP, such as "Session-Id". Only this field is mandatory. +# field="...": +# The name of the field in the database where is AVP contents will be saved. +# The default is that the field is named as the AVP-dictionary-name. +# required: +# By default, if the AVP is not in the Diameter message, it is not passed in +# the INSERT statement (will get NULL). If Required is specified for the AVP, +# an error is returned in the Diameter answer, and the data is discarded. +# multi=N: +# If an AVP may appear several times in a request, you may specify a number of +# occurrences to save in the database. The Nth first occurrences of the AVP +# will be saved in fields "db-field-name1", "db-field-name2", ... "db-field-nameN" +# +# In case the default behavior for an AVP is fine, you can use the short syntax: +# "AVP-dictionary-name"; +# +# Note that at the moment, GROUPED AVP are not supported. Also, only the top-level AVPs are +# search. This behavior can be changed quite easily if needed. + + +####################### +## Part II: Database ## +####################### + +# You must specify the connection information to the database here. +# Please note that if the connection is terminated, it will trig the shutdown of the freeDiameter daemon +# For this reason, you should as much as possible use a local database. + +# ConnInfo: +# The connection string to the database. See http://www.postgresql.org/docs/8.4/static/libpq-connect.html +# Example: ConnInfo = "host=localhost port=5432 dbname=acct user=acct password=freediameter"; + +# Table: +# The name of the table to use. The fields and types in this table must be created accordingly to the Part I configuration in this file. +# Example: Table = "incoming"; + +# Timestamp_field: +# Optionaly, you can specify a name of a field that will receive the value 'now' when a new record is inserted. +# Default: no timestamp is inserted. +# Example: Timestamp_field = "inc_ts"; + + + + diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/CMakeLists.txt Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,28 @@ +# The dict_nasreq extension +PROJECT("Simple Accounting server" C) + +######################## +# Search for libpg (postgresql package) +FIND_PACKAGE(PostgreSQL REQUIRED) +INCLUDE_DIRECTORIES(${POSTGRESQL_INCLUDE_DIR}) + +######################## +# Parser files +BISON_FILE(acct_conf.y) +FLEX_FILE(acct_conf.l) +SET_SOURCE_FILES_PROPERTIES(lex.acct_conf.c acct_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}") + +# List of source files +SET( APP_ACCT_SRC + app_acct.h + app_acct.c + lex.acct_conf.c + acct_conf.tab.c + acct_conf.tab.h + acct_db.c + acct_records.c +) + +# Compile as a module +FD_ADD_EXTENSION(app_acct ${APP_ACCT_SRC}) +TARGET_LINK_LIBRARIES(app_acct ${POSTGRESQL_LIBRARIES}) diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/acct_conf.l --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/acct_conf.l Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,139 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2010, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Lex extension's configuration parser. + * + */ + +%{ +#include "app_acct.h" +#include "acct_conf.tab.h" + +/* Update the column information */ +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ +} + +/* Avoid warning with newer flex */ +#define YY_NO_INPUT + +%} + +%option bison-bridge bison-locations +%option noyywrap +%option nounput + +/* Quoted string. Multilines do not match. */ +qstring \"[^\"\n]*\" + +%% + + /* Update the line count */ +\n { + yylloc->first_line++; + yylloc->last_line++; + yylloc->last_column=0; + } + + /* Eat all spaces but not new lines */ +([[:space:]]{-}[\n])+ ; + /* Eat all comments */ +#.*$ ; + + /* Recognize any integer */ +[-]?[[:digit:]]+ { + /* Convert this to an integer value */ + int ret=0; + ret = sscanf(yytext, "%i", &yylval->integer); + if (ret != 1) { + /* No matching: an error occurred */ + fd_log_debug("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + /* Maybe we could REJECT instead of failing here? */ + } + return INTEGER; + } + + /* Recognize quoted strings -- we do not support escaped \" in the string currently. */ +{qstring} { + /* Match a quoted string. Let's be very permissive. */ + yylval->string = strdup(yytext+1); + if (!yylval->string) { + fd_log_debug("Unable to copy the string '%s': %s\n", yytext, strerror(errno)); + TRACE_DEBUG(INFO, "strdup failed"); + return LEX_ERROR; /* trig an error in yacc parser */ + } + yylval->string[strlen(yytext) - 2] = '\0'; + return QSTRING; + } + + /* Recognize the tokens */ +(?i:"ConnInfo") { + return CONNINFO; + } + +(?i:"Table") { + return TABLE; + } + +(?i:"Timestamp_field") { + return TSFIELD; + } + +(?i:"field") { + return FIELD; + } + +(?i:"required") { + return REQUIRED; + } + +(?i:"multi") { + return MULTI; + } + + + + /* Valid single characters for yyparse */ +[=;{}] { return yytext[0]; } + + /* Unrecognized sequence, if it did not match any previous pattern */ +[^[:space:]"*=>;\n]+ { + fd_log_debug("Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext); + return LEX_ERROR; + } + +%% diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/acct_conf.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/acct_conf.y Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,317 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2010, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Yacc extension's configuration parser. + * See doc/app_acct.conf.sample for configuration file format + */ + +/* For development only : */ +%debug +%error-verbose + +/* The parser receives the configuration file filename as parameter */ +%parse-param {char * conffile} + +/* Keep track of location */ +%locations +%pure-parser + +%{ +#include "app_acct.h" +#include "acct_conf.tab.h" + +#include +#include + +/* Forward declaration */ +int yyparse(char * conffile); + +/* the global configuration */ +struct acct_conf * acct_config = NULL; + +/* Initialize the blank configuration structure */ +int acct_conf_init(void) +{ + TRACE_ENTRY(); + + CHECK_MALLOC( acct_config = malloc(sizeof(struct acct_conf)) ); + memset(acct_config, 0, sizeof(struct acct_conf) ); + fd_list_init(&acct_config->avps, NULL); + + return 0; +} + +/* Validate and eventually display the content of the configuration file for debug */ +int acct_conf_check(char * conffile) +{ + CHECK_PARAMS(acct_config); + + if ( ! acct_config->conninfo) { + fd_log_debug("[app_acct] ERROR: 'ConnInfo' is missing in file '%s'.\n" + " You can specify 'ConnInfo=\"\";' to use default parameters.\n", conffile); + return EINVAL; + } + if ( ! acct_config->tablename) { + fd_log_debug("[app_acct] ERROR: 'Table' value is missing in file '%s'.\n", conffile); + return EINVAL; + } + + struct fd_list * li; + if (!TRACE_BOOL(FULL)) + return; + + fd_log_debug("[app_acct] Configuration dump:\n"); + fd_log_debug(" Database:\n"); + fd_log_debug(" ConnInfo ...... : '%s'\n", acct_config->conninfo ?: ""); + fd_log_debug(" Table name .... : '%s'\n", acct_config->tablename ?: ""); + fd_log_debug(" Timestamp field : '%s'\n", acct_config->tsfield ?: ""); + fd_log_debug(" AVPs that will be saved to the database:\n"); + for (li = acct_config->avps.next; li != &acct_config->avps; li = li->next) { + struct acct_conf_avp * a = (struct acct_conf_avp *)li; + fd_log_debug(" %-*s AVP%s saved in ", 30, a->avpname, a->required ? " [required]":"" ); + if (a->multi) { + fd_log_debug("fields '%s[1..%d]' ", a->field?:a->avpname, a->multi); + } else { + fd_log_debug("field '%s' ", a->field?:a->avpname); + } + fd_log_debug("as ::%s\n", diam2db_types_mapping[a->avptype]); + } + fd_log_debug("[app_acct] Complete.\n"); +} + +void acct_conf_free(void) +{ + TRACE_ENTRY(); + + if (!acct_config) + return; + + /* Destroy the list */ + while (!FD_IS_LIST_EMPTY(&acct_config->avps)) { + struct acct_conf_avp * a = (struct acct_conf_avp *)(acct_config->avps.next); + fd_list_unlink(&a->chain); + free(a->avpname); + free(a->field); + free(a); + } + + /* destroy other data */ + free(acct_config->conninfo); + free(acct_config->tablename); + free(acct_config->tsfield); + + /* Done */ + free(acct_config); + acct_config = NULL; +} + +/* Parse the configuration file */ +int acct_conf_parse(char * conffile) +{ + extern FILE * acct_confin; + int ret; + + TRACE_ENTRY("%p", conffile); + + TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); + + acct_confin = fopen(conffile, "r"); + if (acct_confin == NULL) { + ret = errno; + fd_log_debug("Unable to open extension configuration file %s for reading: %s\n", conffile, strerror(ret)); + return ret; + } + + ret = yyparse(conffile); + + fclose(acct_confin); + + if (ret != 0) { + TRACE_DEBUG (INFO, "Unable to parse the configuration file."); + return EINVAL; + } + + return 0; +} + +/* Function to report the errors */ +void yyerror (YYLTYPE *ploc, char * conffile, char const *s) +{ + TRACE_DEBUG(INFO, "Error in configuration parsing"); + + if (ploc->first_line != ploc->last_line) + fd_log_debug("%s:%d.%d-%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); + else if (ploc->first_column != ploc->last_column) + fd_log_debug("%s:%d.%d-%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + else + fd_log_debug("%s:%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, s); +} + +static struct acct_conf_avp avpdata; + +%} + +/* Values returned by lex for token */ +%union { + char *string; /* The string is allocated by strdup in lex.*/ + int integer; /* Store integer values */ +} + +/* In case of error in the lexical analysis */ +%token LEX_ERROR + +/* Keywords */ +%token FIELD +%token REQUIRED +%token MULTI +%token CONNINFO +%token TABLE +%token TSFIELD + +/* Tokens and types */ +/* A (de)quoted string (malloc'd in lex parser; it must be freed after use) */ +%token QSTRING + +/* An integer value */ +%token INTEGER + + + +/* -------------------------------------- */ +%% + + /* The grammar definition */ +conffile: /* empty grammar is OK for the parser (will be validated afterwards) */ + | conffile avpline + | conffile conninfoline + | conffile tableline + | conffile tsfieldline + | conffile errors + { + yyerror(&yylloc, conffile, "An error occurred while parsing the configuration file."); + return EINVAL; + } + ; + + /* Catch lexical and syntax errors */ +errors: LEX_ERROR + | error + ; + + /* The tokens */ +avpline: { + memset(&avpdata, 0, sizeof(struct acct_conf_avp)); + } + QSTRING avpcontents ';' + { + struct acct_conf_avp *new; + struct dict_object * dict; + struct dict_avp_data dictdata; + + /* Validate the avp name first */ + CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, $2, &dict, ENOENT), + { yyerror (&yylloc, conffile, "AVP definition not found in the dictionary. Was the appropriate dict_*.fdx extension loaded?"); YYERROR; } ); + CHECK_FCT( fd_dict_getval( dict, &dictdata )); + + /* Create a new entry */ + CHECK_MALLOC_DO( new = malloc(sizeof(struct acct_conf_avp)), + { yyerror (&yylloc, conffile, "Out of memory"); YYERROR; } ); + + /* Retrieve all the data from avpcontents parsing (field, required, multi) */ + memcpy(new, &avpdata, sizeof(struct acct_conf_avp)); + + /* Initialize the other data */ + fd_list_init(&new->chain, NULL); + new->avpname = $2; + new->avpobj = dict; + new->avptype = dictdata.avp_basetype; + + /* Add this new entry at the end of the list */ + fd_list_insert_before( &acct_config->avps, &new->chain ); + } + ; + +avpcontents: /* Empty content is fine */ + | '=' '{' avpflagline '}' + ; + +avpflagline: /* Empty flags is also fine */ + | avpflagline FIELD '=' QSTRING ';' + { + if (avpdata.field) { + yyerror (&yylloc, conffile, "Duplicate entry"); + YYERROR; + } + avpdata.field = $4; + } + | avpflagline REQUIRED ';' + { + avpdata.required = 1; + } + | avpflagline MULTI '=' INTEGER ';' + { + avpdata.multi = (unsigned) $4; + } + ; + +conninfoline: CONNINFO '=' QSTRING ';' + { + if (acct_config->conninfo) { + yyerror (&yylloc, conffile, "Duplicate entry"); + YYERROR; + } + acct_config->conninfo = $3 + } + ; + +tableline: TABLE '=' QSTRING ';' + { + if (acct_config->tablename) { + yyerror (&yylloc, conffile, "Duplicate entry"); + YYERROR; + } + acct_config->tablename = $3 + } + ; + +tsfieldline: TSFIELD '=' QSTRING ';' + { + if (acct_config->tsfield) { + yyerror (&yylloc, conffile, "Duplicate entry"); + YYERROR; + } + acct_config->tsfield = $3 + } + ; diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/acct_db.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/acct_db.c Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,193 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2010, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Database interface module */ + +#include "app_acct.h" +#include + +const char * diam2db_types_mapping[AVP_TYPE_MAX + 1] = { + "" /* AVP_TYPE_GROUPED */, + "bytea" /* AVP_TYPE_OCTETSTRING */, + "integer" /* AVP_TYPE_INTEGER32 */, + "bigint" /* AVP_TYPE_INTEGER64 */, + "integer" /* AVP_TYPE_UNSIGNED32 + cast */, + "bigint" /* AVP_TYPE_UNSIGNED64 + cast */, + "real" /* AVP_TYPE_FLOAT32 */, + "double precision" /* AVP_TYPE_FLOAT64 */ +}; + +static const char * stmt = "acct_db_stmt"; +static PGconn *conn = NULL; + +int acct_db_init(void) +{ + struct acct_record_list emptyrecords; + struct fd_list * li; + char * sql=NULL; /* The buffer that will contain the SQL query */ + size_t sql_allocd = 0; /* The malloc'd size of the buffer */ + size_t sql_offset = 0; /* The actual data already written in this buffer */ + size_t p; + int idx = 0; + PGresult * res; + #define REALLOC_SIZE 1024 + + TRACE_ENTRY(); + CHECK_PARAMS( acct_config && acct_config->conninfo && acct_config->tablename ); + + /* Use the information from acct_config to create the connection and prepare the query */ + conn = PQconnectdb(acct_config->conninfo); + + /* Check to see that the backend connection was successfully made */ + if (PQstatus(conn) != CONNECTION_OK) { + fd_log_debug("Connection to database failed: %s\n", PQerrorMessage(conn)); + acct_db_free(); + return EINVAL; + } else { + TRACE_DEBUG(INFO, "Connection to database successfull: user:%s, db:%s, host:%s.", PQuser(conn), PQdb(conn), PQhost(conn)); + } + + /* Now, prepare the request object */ + + /* First, we build the list of AVP we will insert in the database */ + CHECK_FCT( acct_rec_prepare(&emptyrecords) ); + + /* Now, prepare the text of the request */ + CHECK_MALLOC(sql = malloc(REALLOC_SIZE)); + sql_allocd = REALLOC_SIZE; + + /* This macro hides the details of extending the buffer on each sprintf... */ + #define ADD_EXTEND(args...) { \ + size_t p; \ + int loop = 0; \ + do { \ + p = snprintf(sql + sql_offset, sql_allocd - sql_offset, ##args); \ + if (p >= sql_allocd - sql_offset) { \ + /* Too short, extend the buffer and start again */ \ + CHECK_MALLOC( sql = realloc(sql, sql_allocd + REALLOC_SIZE) ); \ + sql_allocd += REALLOC_SIZE; \ + loop++; \ + ASSERT(loop < 100); /* detect infinite loops */ \ + continue; \ + } \ + sql_offset += p; \ + break; \ + } while (1); \ + } + + /* This macro allows to add a value in the SQL buffer while escaping in properly */ + #define ADD_ESCAPE(str) { \ + char * __s = (char *)str; \ + /* Check we have at least twice the size available +1 */ \ + size_t p = strlen(__s); \ + \ + while (sql_allocd - sql_offset < 2 * p + 1) { \ + /* Too short, extend the buffer */ \ + CHECK_MALLOC( sql = realloc(sql, sql_allocd + REALLOC_SIZE) ); \ + sql_allocd += REALLOC_SIZE; \ + } \ + \ + /* Now add the escaped string */ \ + p = PQescapeStringConn(conn, sql+sql_offset, __s, p, NULL); \ + sql_offset += p; \ + } + + /* INSERT INTO table (tsfield, field1, field2, ...) VALUES (now, $1::bytea, $2::integer, ...) */ + ADD_EXTEND("INSERT INTO %s (", acct_config->tablename); + + if (acct_config->tsfield) { + ADD_EXTEND("\""); + ADD_ESCAPE(acct_config->tsfield); + ADD_EXTEND("\", "); + } + + for (li = emptyrecords.all.next; li != &emptyrecords.all; li = li->next) { + struct acct_record_item * i = (struct acct_record_item *)(li->o); + ADD_EXTEND("\""); + ADD_ESCAPE(i->param->field?:i->param->avpname); + if (i->index) { + ADD_EXTEND("%d", i->index); + } + if (li->next != &emptyrecords.all) { + ADD_EXTEND("\", "); + } + } + + ADD_EXTEND("\") VALUES ("); + + if (acct_config->tsfield) { + ADD_EXTEND("'now', "); + } + + for (li = emptyrecords.all.next; li != &emptyrecords.all; li = li->next) { + struct acct_record_item * i = (struct acct_record_item *)(li->o); + idx += 1; + ADD_EXTEND("$%d::%s", idx, diam2db_types_mapping[i->param->avptype]); + + if (li->next != &emptyrecords.all) { + ADD_EXTEND(", "); + } + } + + ADD_EXTEND(");"); + + TRACE_DEBUG(FULL, "Preparing the following SQL statement:\n%s\n", sql); + res = PQprepare(conn, stmt, sql, emptyrecords.nball, NULL); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + TRACE_DEBUG(INFO, "Preparing statement '%s' failed: %s", + sql, PQerrorMessage(conn)); + PQclear(res); + return EINVAL; + } + PQclear(res); + + + + return 0; +} + +void acct_db_free(void) +{ + if (conn) + PQfinish(conn); + conn = NULL; +} + +int acct_db_insert(struct acct_record_list * records) +{ + return ENOTSUP; +} + + diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/acct_records.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/acct_records.c Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,83 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2010, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Functions to create a list of AVPs according to the configuration file */ + +#include "app_acct.h" + +/* Prepare a list of acct_record entries from the configuration, without mapping any value at this time */ +int acct_rec_prepare(struct acct_record_list * records) +{ + struct fd_list * li; + TRACE_ENTRY("%p", records); + CHECK_PARAMS( records && acct_config ); + + /* Prepare the records structure */ + memset(records, 0, sizeof(struct acct_record_list)); + fd_list_init(&records->all, records); + fd_list_init(&records->unmaped, records); + + /* for each entry in the configuration */ + for (li = acct_config->avps.next; li != &acct_config->avps; li = li->next) { + struct acct_conf_avp * a = (struct acct_conf_avp *)li; + struct acct_record_item * new; + int i = a->multi ? 1 : 0; + /* Create as many records as the 'multi' parameter requires */ + do { + CHECK_MALLOC( new = malloc(sizeof(struct acct_record_item)) ); + memset(new, 0, sizeof(struct acct_record_item)); + fd_list_init(&new->chain, new); + fd_list_init(&new->unmapd, new); + new->param = a; + new->index = i; + fd_list_insert_before(&records->all, &new->chain); + fd_list_insert_before(&records->unmaped, &new->unmapd); + records->nball++; + records->nbunmap++; + i++; + } while (i <= a->multi); + } + + return 0; +} + +int acct_rec_map(struct acct_record_list * records, struct msg * msg) +{ + TRACE_ENTRY("%p %p", records, msg); + + /* For each AVP in the message, search if we have a corresponding unmap'd record */ + + return ENOTSUP; +} diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/app_acct.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/app_acct.c Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,127 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2010, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* The simple Accounting server for freeDiameter */ + +#include "app_acct.h" + +/* Default callback for the Accounting application. */ +static int acct_fallback( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act) +{ + /* This CB should never be called */ + TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act); + + fd_log_debug("Unexpected message received!\n"); + + return ENOTSUP; +} + + +/* Callback for incoming Base Accounting Accounting-Request messages */ +static int acct_cb( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act) +{ + struct msg_hdr *hdr = NULL; + struct msg *ans, *qry; + struct avp * a = NULL; + struct avp_hdr * h = NULL; + char * s; + + TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act); + if (msg == NULL) + return EINVAL; + + qry = *msg; + /* Create the answer message, including the Session-Id AVP */ + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) ); + ans = *msg; + + /* Set the Origin-Host, Origin-Realm, Result-Code AVPs */ + CHECK_FCT( fd_msg_rescode_set( ans, "DIAMETER_SUCCESS", NULL, NULL, 1 ) ); + + fd_log_debug("--------------Received the following Accounting message:--------------\n"); + + CHECK_FCT( fd_sess_getsid ( sess, &s ) ); + fd_log_debug("Session: %s\n", s); + + /* We may also dump other data from the message, such as Accounting session Id, number of packets, ... */ + + fd_log_debug("----------------------------------------------------------------------\n"); + + /* Send the answer */ + CHECK_FCT( fd_msg_send( msg, NULL, NULL ) ); + + return 0; +} + + +/* entry point */ +static int acct_entry(char * conffile) +{ + struct disp_when data; + + TRACE_ENTRY("%p", conffile); + + /* Initialize the configuration and parse the file */ + CHECK_FCT( acct_conf_init() ); + CHECK_FCT( acct_conf_parse(conffile) ); + CHECK_FCT( acct_conf_check(conffile) ); + + /* Now initialize the database module */ + CHECK_FCT( acct_db_init() ); + + /* Register the dispatch callbacks */ + memset(&data, 0, sizeof(data)); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &data.app, ENOENT) ); + CHECK_FCT( fd_disp_register( acct_fallback, DISP_HOW_APPID, &data, NULL ) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &data.command, ENOENT) ); + CHECK_FCT( fd_disp_register( acct_cb, DISP_HOW_CC, &data, NULL ) ); + + /* Advertise the support for the Diameter Base Accounting application in the peer */ + CHECK_FCT( fd_disp_app_support ( data.app, NULL, 0, 1 ) ); + + return 0; +} + +/* Unload */ +void fd_ext_fini(void) +{ + /* Close the db connection */ + acct_db_free(); + + /* Destroy the configuration */ + acct_conf_free(); +} + +EXTENSION_ENTRY("app_acct", acct_entry); diff -r f433bc04eb6a -r 397cdcd41f53 extensions/app_acct/app_acct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_acct/app_acct.h Wed Apr 28 18:54:08 2010 +0900 @@ -0,0 +1,113 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2010, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Header file for the app_acct extension. + * + * This extension is a simple Diameter Accounting server. + * + * It receives the Diameter Accounting-Request message, and sends its content in a + * "buffer" postgreSQL database (see configuration sample file app_acct.conf.sample). + * + * The intent is that another application will then pick the records from this "buffer" database + * and process it accordingly to the requirements of the end application. + */ + +#include + +/* The structure corresponding to an AVP entry in the configuration file */ +struct acct_conf_avp { + /* Chain */ + struct fd_list chain; /* link in the global list */ + + /* Raw information from the configuration file */ + char *avpname; /* the name of the AVP, as appear in the configuration file */ + char *field; /* the field name in the database, or NULL if it is the same as avpname */ + int required; /* set to true if this AVP has to be in the message */ + unsigned multi; /* the number of occurrences of this AVP we convert, or 0 if it was not specified */ + + /* Parsed information */ + struct dict_object *avpobj; /* the dictionary object corresponding to this AVP */ + enum dict_avp_basetype avptype; /* this info is extracted from avpobj. GROUPED avps are not allowed yet */ +}; + +/* This is described only inside acct_db.c */ +struct acct_db; + +/* The complete configuration */ +struct acct_conf { + /* AVPs */ + struct fd_list avps; /* the list of acct_conf_avp entries */ + + /* Raw information */ + char *conninfo; /* the connection string to the database, that is passed as is to the database library */ + char *tablename; /* the name of the table we are working with */ + char *tsfield; /* the name of the timestamp field, or NULL if not required */ +}; + +/* A successfully parsed Accounting-Request produces a list of these: */ +struct acct_record_item { + struct fd_list chain; /* link with all others */ + struct fd_list unmapd;/* link with only unmap'd records */ + struct acct_conf_avp *param; /* the AVP entry this refers to. */ + unsigned index; /* in case of multi */ + union avp_value *value; /* If the AVP was found in the message, this points to its value. Otherwise, NULL */ +}; + +/* The sentinel for a list of acct_record_items */ +struct acct_record_list { + struct fd_list all; /* The list of records */ + int nball; /* The number of records in all */ + struct fd_list unmaped;/* Only the records without a value */ + int nbunmap;/* The number of unmap'd records */ +}; + +/* Mapping of the data types between Diameter AVP and PQ types: */ +extern const char * diam2db_types_mapping[]; + +/* In acct_conf.y */ +extern struct acct_conf * acct_config; /* the global configuration */ +int acct_conf_init(void); +int acct_conf_parse(char * conffile); +int acct_conf_check(char * conffile); +void acct_conf_free(void); + +/* In acct_db.c */ +int acct_db_init(void); +int acct_db_insert(struct acct_record_list * records); +void acct_db_free(void); + +/* In acct_records.c */ +int acct_rec_prepare(struct acct_record_list * records); +int acct_rec_map(struct acct_record_list * records, struct msg * msg);