Changeset 285:0daf6fc2b751 in freeDiameter
- Timestamp:
- Apr 30, 2010, 5:55:16 PM (14 years ago)
- Branch:
- default
- Phase:
- public
- Files:
-
- 1 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/app_acct/CMakeLists.txt
r284 r285 17 17 app_acct.h 18 18 app_acct.c 19 acct_db.c 20 acct_records.c 21 ) 22 SET( APP_ACCT_SRC_GEN 19 23 lex.acct_conf.c 20 24 acct_conf.tab.c 21 25 acct_conf.tab.h 22 acct_db.c23 acct_records.c24 26 ) 25 27 -
extensions/app_acct/acct_db.c
r284 r285 51 51 52 52 static const char * stmt = "acct_db_stmt"; 53 static PGconn *conn = NULL; 54 53 #ifndef TEST_DEBUG 54 static 55 #endif /* TEST_DEBUG */ 56 PGconn *conn = NULL; 57 58 /* Initialize the database context: connection to the DB, prepared statement to insert new records */ 55 59 int acct_db_init(void) 56 60 { … … 63 67 int idx = 0; 64 68 PGresult * res; 65 #define REALLOC_SIZE 1024 69 #define REALLOC_SIZE 1024 /* We extend the buffer by this amount */ 66 70 67 71 TRACE_ENTRY(); … … 76 80 acct_db_free(); 77 81 return EINVAL; 78 } else { 79 TRACE_DEBUG(INFO, "Connection to database successfull: user:%s, db:%s, host:%s.", PQuser(conn), PQdb(conn), PQhost(conn)); 80 } 82 } 83 if (PQprotocolVersion(conn) < 3) { 84 fd_log_debug("Database protocol version is too old, version 3 is required for prepared statements.\n"); 85 acct_db_free(); 86 return EINVAL; 87 } 88 89 TRACE_DEBUG(FULL, "Connection to database successful, server version %d.", PQserverVersion(conn)); 81 90 82 91 /* Now, prepare the request object */ … … 174 183 PQclear(res); 175 184 176 177 185 free(sql); 186 acct_rec_empty(&emptyrecords); 187 188 /* Ok, ready */ 178 189 return 0; 179 190 } 180 191 192 /* Terminate the connection to the DB */ 181 193 void acct_db_free(void) 182 { 183 if (conn) 194 { 195 if (conn) { 196 /* Note: the prepared statement is automatically freed when the session terminates */ 184 197 PQfinish(conn); 185 conn = NULL; 198 conn = NULL; 199 } 186 200 } 187 201 202 /* When a new message has been received, insert the content of the parsed mapping into the DB (using prepared statement) */ 188 203 int acct_db_insert(struct acct_record_list * records) 189 204 { 190 return ENOTSUP; 205 char **val; 206 int *val_len; 207 int *val_isbin; 208 int idx = 0; 209 PGresult *res; 210 struct fd_list *li; 211 212 TRACE_ENTRY("%p", records); 213 CHECK_PARAMS( conn && records ); 214 215 /* First, check if the connection with the DB has not staled, and eventually try to fix it */ 216 if (PQstatus(conn) != CONNECTION_OK) { 217 /* Attempt a reset */ 218 PQreset(conn); 219 if (PQstatus(conn) != CONNECTION_OK) { 220 TRACE_DEBUG(INFO, "Lost connection to the database server, and attempt to reestablish it failed"); 221 TODO("Terminate the freeDiameter instance completly?"); 222 return ENOTCONN; 223 } 224 } 225 226 /* Alloc the arrays of parameters */ 227 CHECK_MALLOC( val = calloc(records->nball, sizeof(const char *)) ); 228 CHECK_MALLOC( val_len = calloc(records->nball, sizeof(const int)) ); 229 CHECK_MALLOC( val_isbin = calloc(records->nball, sizeof(const int)) ); 230 231 /* Now write all the map'd records in these arrays */ 232 for (li = records->all.next; li != &records->all; li = li->next) { 233 struct acct_record_item * r = (struct acct_record_item *)(li->o); 234 if (r->value) { 235 val_isbin[idx] = 1; /* We always pass binary parameters */ 236 switch (r->param->avptype) { 237 case AVP_TYPE_OCTETSTRING: 238 val[idx] = (void *)(r->value->os.data); 239 val_len[idx] = r->value->os.len; 240 break; 241 242 case AVP_TYPE_INTEGER32: 243 case AVP_TYPE_UNSIGNED32: 244 case AVP_TYPE_FLOAT32: 245 r->scalar.v32 = htonl(r->value->u32); 246 val[idx] = &r->scalar.c; 247 val_len[idx] = sizeof(uint32_t); 248 break; 249 250 case AVP_TYPE_INTEGER64: 251 case AVP_TYPE_UNSIGNED64: 252 case AVP_TYPE_FLOAT64: 253 r->scalar.v64 = htonll(r->value->u64); 254 val[idx] = &r->scalar.c; 255 val_len[idx] = sizeof(uint64_t); 256 break; 257 258 default: 259 ASSERT(0); /* detect bugs */ 260 } 261 } 262 263 idx++; 264 } 265 266 /* OK, now execute the SQL statement */ 267 res = PQexecPrepared(conn, stmt, records->nball, (const char * const *)val, val_len, val_isbin, 1 /* We actually don't care here */); 268 269 /* Done with the parameters */ 270 free(val); 271 free(val_len); 272 free(val_isbin); 273 274 /* Now check the result code */ 275 if (PQresultStatus(res) != PGRES_COMMAND_OK) { 276 TRACE_DEBUG(INFO, "An error occurred while INSERTing in the database: %s", PQerrorMessage(conn)); 277 PQclear(res); 278 return EINVAL; /* It was probably a mistake in configuration file... */ 279 } 280 PQclear(res); 281 282 /* Ok, we are done */ 283 return 0; 191 284 } 192 285 -
extensions/app_acct/acct_records.c
r284 r285 74 74 } 75 75 76 /* Find the AVPs from configuration inside a received message */ 76 77 int acct_rec_map(struct acct_record_list * records, struct msg * msg) 77 78 { 79 struct avp * avp; 80 78 81 TRACE_ENTRY("%p %p", records, msg); 79 82 80 83 /* For each AVP in the message, search if we have a corresponding unmap'd record */ 84 CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); 85 while (avp) { 86 struct fd_list * li; 87 struct dict_object * model; 88 89 CHECK_FCT( fd_msg_model(avp, &model) ); 90 if (model != NULL) { /* we ignore the AVPs we don't recognize */ 91 92 /* Search this model in the list */ 93 for (li = records->unmaped.next; li != &records->unmaped; li = li->next) { 94 struct acct_record_item * r = (struct acct_record_item *)(li->o); 95 if (r->param->avpobj == model) { 96 /* It matches: save the AVP value and unlink this record from the unmap'd list */ 97 struct avp_hdr * h; 98 CHECK_FCT( fd_msg_avp_hdr( avp, &h ) ); 99 r->value = h->avp_value; 100 fd_list_unlink(&r->unmapd); 101 records->nbunmap -= 1; 102 break; 103 } 104 } 105 106 /* Continue only while there are some AVPs to map */ 107 if (FD_IS_LIST_EMPTY(&records->unmaped)) 108 break; 109 } 110 111 /* Go to next AVP in the message */ 112 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); 113 } 81 114 82 return ENOTSUP; 115 /* Done */ 116 return 0; 83 117 } 118 119 /* Check that a mapped list is not empty and no required AVP is missing. Free the record list in case of error */ 120 int acct_rec_validate(struct acct_record_list * records) 121 { 122 struct fd_list * li; 123 TRACE_ENTRY("%p", records); 124 CHECK_PARAMS( records ); 125 126 /* Check at least one AVP was mapped */ 127 if (records->nball == records->nbunmap) { 128 fd_log_debug("The received ACR does not contain any AVP from the configuration file.\n" 129 "This is an invalid situation. Please fix your configuration file.\n" 130 "One way to ensure this does not happen is to include Session-Id in the database.\n"); 131 acct_rec_empty(records); 132 return EINVAL; 133 } 134 135 /* Now, check there is no required AVP unmap'd */ 136 for (li = records->unmaped.next; li != &records->unmaped; li = li->next) { 137 struct acct_record_item * r = (struct acct_record_item *)(li->o); 138 if (r->param->required && (r->index <= 1)) { 139 fd_log_debug("The received ACR does not contain the required AVP '%s'.\n", r->param->avpname); 140 acct_rec_empty(records); 141 return EINVAL; 142 } 143 } 144 145 /* The record list is OK */ 146 return 0; 147 } 148 149 /* Free all the items in an acct_record_list returned by acct_rec_prepare */ 150 void acct_rec_empty(struct acct_record_list * records) 151 { 152 TRACE_ENTRY("%p", records); 153 CHECK_PARAMS_DO( records, return ); 154 155 while (!FD_IS_LIST_EMPTY(&records->all)) { 156 struct acct_record_item * r = (struct acct_record_item *)(records->all.next); 157 fd_list_unlink( &r->chain ); 158 fd_list_unlink( &r->unmapd ); 159 free(r); 160 } 161 } -
extensions/app_acct/app_acct.c
r284 r285 38 38 #include "app_acct.h" 39 39 40 /* Default callback for the Accounting application. */ 41 static int acct_fallback( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act) 42 { 43 /* This CB should never be called */ 44 TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act); 45 46 fd_log_debug("Unexpected message received!\n"); 47 48 return ENOTSUP; 49 } 40 /* Mandatory AVPs for the Accounting-Answer */ 41 static struct { 42 struct dict_object * Accounting_Record_Number; 43 struct dict_object * Accounting_Record_Type; 44 } acct_dict; 50 45 51 46 … … 54 49 { 55 50 struct msg_hdr *hdr = NULL; 56 struct msg * ans, *qry;51 struct msg * m; 57 52 struct avp * a = NULL; 58 struct avp_hdr * h = NULL;53 struct avp_hdr * art=NULL, *arn=NULL; /* We keep a pointer on the Accounting-Record-{Type, Number} AVPs from the query */ 59 54 char * s; 55 struct acct_record_list rl; 60 56 61 57 TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act); … … 63 59 return EINVAL; 64 60 65 qry = *msg; 66 /* Create the answer message, including the Session-Id AVP */ 61 m = *msg; 62 63 /* Prepare a new record list */ 64 CHECK_FCT( acct_rec_prepare( &rl ) ); 65 66 /* Maps the AVPs from the query with this record list */ 67 CHECK_FCT( acct_rec_map( &rl, m ) ); 68 69 /* Check that at least one AVP was mapped */ 70 CHECK_FCT( acct_rec_validate( &rl ) ); 71 72 /* Now, save these mapped AVPs in the database */ 73 CHECK_FCT( acct_db_insert( &rl ) ); 74 75 acct_rec_empty( &rl ); 76 77 /* OK, we can send a positive reply now */ 78 79 /* Get Accounting-Record-{Number,Type} values */ 80 CHECK_FCT( fd_msg_search_avp ( m, acct_dict.Accounting_Record_Type, &a) ); 81 if (a) { 82 CHECK_FCT( fd_msg_avp_hdr( a, &art ) ); 83 } 84 CHECK_FCT( fd_msg_search_avp ( m, acct_dict.Accounting_Record_Number, &a) ); 85 if (a) { 86 CHECK_FCT( fd_msg_avp_hdr( a, &arn ) ); 87 } 88 89 /* Create the answer message */ 67 90 CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) ); 68 ans= *msg;91 m = *msg; 69 92 70 93 /* Set the Origin-Host, Origin-Realm, Result-Code AVPs */ 71 CHECK_FCT( fd_msg_rescode_set( ans, "DIAMETER_SUCCESS", NULL, NULL, 1 ) ); 72 73 fd_log_debug("--------------Received the following Accounting message:--------------\n"); 74 75 CHECK_FCT( fd_sess_getsid ( sess, &s ) ); 76 fd_log_debug("Session: %s\n", s); 77 78 /* We may also dump other data from the message, such as Accounting session Id, number of packets, ... */ 79 80 fd_log_debug("----------------------------------------------------------------------\n"); 81 94 CHECK_FCT( fd_msg_rescode_set( m, "DIAMETER_SUCCESS", NULL, NULL, 1 ) ); 95 96 /* Add the mandatory AVPs in the ACA */ 97 if (art) { 98 CHECK_FCT( fd_msg_avp_new ( acct_dict.Accounting_Record_Type, 0, &a ) ); 99 CHECK_FCT( fd_msg_avp_setvalue( a, art->avp_value ) ); 100 CHECK_FCT( fd_msg_avp_add( m, MSG_BRW_LAST_CHILD, a ) ); 101 } 102 if (arn) { 103 CHECK_FCT( fd_msg_avp_new ( acct_dict.Accounting_Record_Number, 0, &a ) ); 104 CHECK_FCT( fd_msg_avp_setvalue( a, arn->avp_value ) ); 105 CHECK_FCT( fd_msg_avp_add( m, MSG_BRW_LAST_CHILD, a ) ); 106 } 107 82 108 /* Send the answer */ 83 CHECK_FCT( fd_msg_send( msg, NULL, NULL ) ); 84 109 *act = DISP_ACT_SEND; 85 110 return 0; 86 111 } … … 94 119 TRACE_ENTRY("%p", conffile); 95 120 121 #ifndef TEST_DEBUG /* We do this differently in the test scenario */ 96 122 /* Initialize the configuration and parse the file */ 97 123 CHECK_FCT( acct_conf_init() ); 98 124 CHECK_FCT( acct_conf_parse(conffile) ); 99 125 CHECK_FCT( acct_conf_check(conffile) ); 126 #endif /* TEST_DEBUG */ 100 127 101 128 /* Now initialize the database module */ 102 129 CHECK_FCT( acct_db_init() ); 103 130 131 /* Search the AVPs we will need in this file */ 132 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &acct_dict.Accounting_Record_Number, ENOENT) ); 133 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &acct_dict.Accounting_Record_Type, ENOENT) ); 134 104 135 /* Register the dispatch callbacks */ 105 136 memset(&data, 0, sizeof(data)); 106 137 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &data.app, ENOENT) ); 107 CHECK_FCT( fd_disp_register( acct_fallback, DISP_HOW_APPID, &data, NULL ) );108 138 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &data.command, ENOENT) ); 109 139 CHECK_FCT( fd_disp_register( acct_cb, DISP_HOW_CC, &data, NULL ) ); -
extensions/app_acct/app_acct.h
r284 r285 84 84 unsigned index; /* in case of multi */ 85 85 union avp_value *value; /* If the AVP was found in the message, this points to its value. Otherwise, NULL */ 86 union { 87 uint32_t v32 /* Storage area for network byte-order copy of the AVP value */; 88 uint64_t v64; 89 char c; /* pointer that is passed to the database */ 90 } scalar;/* for scalar AVP (all types except OCTETSTRING) we copy in this area the value in network byte order */ 86 91 }; 87 92 … … 112 117 int acct_rec_prepare(struct acct_record_list * records); 113 118 int acct_rec_map(struct acct_record_list * records, struct msg * msg); 119 int acct_rec_validate(struct acct_record_list * records); 120 void acct_rec_empty(struct acct_record_list * records); -
freeDiameter/extensions.c
r14 r285 153 153 154 154 /* Now unload the extensions and free the memory */ 155 int fd_ext_ fini( void )155 int fd_ext_term( void ) 156 156 { 157 157 TRACE_ENTRY(); -
freeDiameter/fD.h
r258 r285 84 84 int fd_ext_load(); 85 85 void fd_ext_dump(void); 86 int fd_ext_ fini(void);86 int fd_ext_term(void); 87 87 88 88 /* Messages */ -
freeDiameter/main.c
r258 r285 165 165 CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ ); 166 166 167 CHECK_FCT_DO( fd_ext_ fini(), /* Cleanup all extensions */ );167 CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ ); 168 168 CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ ); 169 169 -
freeDiameter/tests/CMakeLists.txt
r29 r285 50 50 ADD_LIBRARY(fDcore STATIC ${TEST_COMMON_SRC}) 51 51 52 ############################## 53 # App_acct test 54 55 IF(BUILD_APP_ACCT) 56 OPTION(TEST_APP_ACCT "Test app_acct extension? (Requires a configured database, see testappacct.c for details)" OFF) 57 IF(TEST_APP_ACCT) 58 59 OPTION(TEST_APP_ACCT_CONNINFO "The connection string to the database") 60 IF(TEST_APP_ACCT_CONNINFO) 61 ADD_DEFINITIONS(-DTEST_CONNINFO="${TEST_APP_ACCT_CONNINFO}") 62 ENDIF(TEST_APP_ACCT_CONNINFO) 63 64 SET(TEST_LIST ${TEST_LIST} testappacct) 65 66 # Extension dependencies 67 FIND_PACKAGE(PostgreSQL REQUIRED) 68 INCLUDE_DIRECTORIES(${POSTGRESQL_INCLUDE_DIR}) 69 SET(testappacct_ADDITIONAL_LIB ${POSTGRESQL_LIBRARIES}) 70 71 # List of source files, copied from the extension CMakeLists. 72 SET( APP_ACCT_SRC 73 app_acct.h 74 app_acct.c 75 acct_db.c 76 acct_records.c 77 ) 78 SET( APP_ACCT_SRC_GEN 79 lex.acct_conf.c 80 acct_conf.tab.c 81 acct_conf.tab.h 82 ) 83 84 # The extension headers 85 INCLUDE_DIRECTORIES( "../../extensions/app_acct" ) 86 87 SET(testappacct_ADDITIONAL "") 88 89 FOREACH( SRC_FILE ${APP_ACCT_SRC}) 90 SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "../../extensions/app_acct/${SRC_FILE}") 91 ENDFOREACH(SRC_FILE) 92 93 FOREACH( SRC_FILE ${APP_ACCT_SRC_GEN}) 94 SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "${CMAKE_CURRENT_BINARY_DIR}/../../extensions/app_acct/${SRC_FILE}") 95 ENDFOREACH(SRC_FILE) 96 97 ENDIF(TEST_APP_ACCT) 98 ENDIF(BUILD_APP_ACCT) 99 52 100 53 101 ############################# 54 102 # Compile each test 55 103 FOREACH( TEST ${TEST_LIST} ) 56 ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h )57 TARGET_LINK_LIBRARIES(${TEST} fDcore ${FD_LIBS} )104 ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h ${${TEST}_ADDITIONAL}) 105 TARGET_LINK_LIBRARIES(${TEST} fDcore ${FD_LIBS} ${${TEST}_ADDITIONAL_LIB}) 58 106 ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST}) 59 107 ENDFOREACH( TEST ) -
freeDiameter/tests/tests.h
r258 r285 125 125 int c; 126 126 int no_timeout = 0; 127 while ((c = getopt (argc, argv, "dqn ")) != -1) {127 while ((c = getopt (argc, argv, "dqnf:")) != -1) { 128 128 switch (c) { 129 129 case 'd': /* Increase verbosity of debug messages. */ … … 139 139 break; 140 140 141 case 'f': /* Full debug for the function with this name. */ 142 #ifdef DEBUG 143 fd_debug_one_function = optarg; 144 #else /* DEBUG */ 145 TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature"); 146 return EINVAL; 147 #endif /* DEBUG */ 148 break; 149 141 150 default: /* bug: option not considered. */ 142 151 return;
Note: See TracChangeset
for help on using the changeset viewer.