Changes in / [1331:229ecec59ff5:1349:73b9f2aefb75] in freeDiameter
- Files:
-
- 33 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
INSTALL.pkgsrc
r811 r1334 12 12 and follow the usual installation note. 13 13 14 NetBSD does not support SCTP, so you have to add 15 -DDISABLE_SCTP:BOOL=ON 16 to the cmake command line.14 For the optional extensions, you need mysql*-client, postgres*-client, 15 libxml2, jsoncpp, and json-schema. For the debugging extensions, 16 you also need swig and a python -- install as usual. 17 17 18 For the optional extensions, you need mysql*-client, postgres*-client,19 libxml2. For the debugging extensions, you also need swig and a20 python -- install as usual.21 -
LICENSE
r1331 r1349 3 3 Software License Agreement (BSD License) 4 4 5 Copyright (c) 2008-201 8, WIDE Project and NICT5 Copyright (c) 2008-2019, WIDE Project and NICT 6 6 All rights reserved. 7 7 … … 53 53 * Thomas Klausner: 54 54 several extensions and numerous fixes / improvements. 55 56 57 -
doc/rt_default.conf.sample
r1197 r1336 2 2 # 3 3 # This extension provides configurable routing properties for freeDiameter. 4 5 # This extension supports configuration reload at runtime. Send 6 # signal SIGUSR1 to the process to cause the process to reload its 7 # config. 4 8 5 9 # Lines starting with a # are comments and ignored. -
doc/rt_ereg.conf.sample
r525 r1338 4 4 # The rt_ereg extension allows creation of routing rules based on AVP value matching regular expressions. 5 5 6 # This extension supports configuration reload at runtime. Send 7 # signal SIGUSR1 to the process to cause the process to reload its 8 # config. 9 6 10 # First, one must indicate which AVP should be used for matching. 7 11 # At the moment, only AVP with OCTETSTRING types are valid. 8 12 # AVP = "User-Name"; 13 # It is possible to specify AVPs below GROUPED AVPs with the by separating AVPs with a colon (':'): 14 # AVP = "Grouped-AVP1" : "Grouped-AVP2" : "Octetstring-AVP"; 9 15 # This parameter is mandatory. There is no default value. 10 16 … … 20 26 # (reminder: the server with the peer with the highest score gets the message) 21 27 # Note that all rules are tested for each message that contain the AVP, not only the first match. 28 29 # There can be multiple blocks of AVPs and rules; just start the next one with another AVP line: 30 # AVP = "Other-AVP"; 31 # and continue with rules as above. -
extensions/CMakeLists.txt
r1278 r1348 13 13 ENDMACRO(FD_ADD_EXTENSION) 14 14 15 # Use the macro FD_EXTENSION_SUBDIR(extmacroname subdir descr default) to 15 # Use the macro FD_EXTENSION_SUBDIR(extmacroname subdir descr default) to 16 16 # add a new extension subdirectory. 17 17 MACRO(FD_EXTENSION_SUBDIR EXTSUBDIR EXTDESCR EXTDEFAULT) … … 40 40 FD_EXTENSION_SUBDIR(dict_eap "Diameter EAP (RFC4072) Dictionary definitions" ON) 41 41 42 FD_EXTENSION_SUBDIR(dict_dcca "Diameter CC (RFC4006) Dictionary definitions [incomplete]" ON)43 FD_EXTENSION_SUBDIR(dict_dcca_3gpp "Diameter CC 3GPP Dictionary definitions [incomplete]" ON)44 FD_EXTENSION_SUBDIR(dict_dcca_starent 42 FD_EXTENSION_SUBDIR(dict_dcca "Diameter CC (RFC4006) Dictionary definitions [incomplete]" ON) 43 FD_EXTENSION_SUBDIR(dict_dcca_3gpp "Diameter CC 3GPP Dictionary definitions [incomplete]" ON) 44 FD_EXTENSION_SUBDIR(dict_dcca_starent "Diameter CC Starent Dictionary definitions" ON) 45 45 46 46 FD_EXTENSION_SUBDIR(dict_sip "Diameter SIP (RFC4740) Dictionary definitions" ON) … … 51 51 FD_EXTENSION_SUBDIR(dict_rfc5777 "Classification and QoS (RFC 5777) Dictionary definitions" ON) 52 52 53 FD_EXTENSION_SUBDIR(dict_json "Load Diameter dictionary definitions from JSON files." OFF) 53 54 FD_EXTENSION_SUBDIR(dict_legacy_xml "Load Diameter dictionary definitions from XML files." OFF) 54 55 … … 68 69 # Routing extensions 69 70 70 FD_EXTENSION_SUBDIR(rt_busypeers "Handling of Diameter TOO_BUSY messages and relay timeouts" ON) 71 FD_EXTENSION_SUBDIR(rt_default "Configurable routing rules for freeDiameter" ON) 72 FD_EXTENSION_SUBDIR(rt_ereg "Configurable routing based on regexp matching of AVP values" OFF) 73 FD_EXTENSION_SUBDIR(rt_ignore_dh "Stow Destination-Host in Proxy-Info, restore to Origin-Host for answers" ON) 71 FD_EXTENSION_SUBDIR(rt_busypeers "Handling of Diameter TOO_BUSY messages and relay timeouts" ON) 72 FD_EXTENSION_SUBDIR(rt_default "Configurable routing rules for freeDiameter" ON) 73 FD_EXTENSION_SUBDIR(rt_deny_by_size "Deny messages that are larger than a configured size" ON) 74 FD_EXTENSION_SUBDIR(rt_ereg "Configurable routing based on regexp matching of AVP values" OFF) 75 FD_EXTENSION_SUBDIR(rt_ignore_dh "Stow Destination-Host in Proxy-Info, restore to Origin-Host for answers" ON) 74 76 FD_EXTENSION_SUBDIR(rt_load_balance "Balance load over multiple equal hosts, based on outstanding requests" ON) 75 FD_EXTENSION_SUBDIR(rt_randomize "Randomly choose one of the highest scored hosts and increase its score by one" ON) 76 FD_EXTENSION_SUBDIR(rt_redirect "Handling of Diameter Redirect messages" ON) 77 FD_EXTENSION_SUBDIR(rt_randomize "Randomly choose one of the highest scored hosts and increase its score by one" ON) 78 FD_EXTENSION_SUBDIR(rt_redirect "Handling of Diameter Redirect messages" ON) 79 FD_EXTENSION_SUBDIR(rt_rewrite "Convert/remove AVP data in messages" ON) 77 80 78 81 … … 86 89 # Debug & test extensions 87 90 88 FD_EXTENSION_SUBDIR(dbg_monitor "Outputs periodical status information" ON) 91 FD_EXTENSION_SUBDIR(dbg_loglevel "Read loglevel from file -- allows runtime change" ON) 92 FD_EXTENSION_SUBDIR(dbg_monitor "Outputs periodical status information" ON) 89 93 FD_EXTENSION_SUBDIR(dbg_msg_timings "Show some timing information for messages" ON) 90 FD_EXTENSION_SUBDIR(dbg_msg_dumps "Show human-readable content of the received & sent messages" ON) 91 FD_EXTENSION_SUBDIR(dbg_rt "Routing extension for debugging the routing module" ON) 92 FD_EXTENSION_SUBDIR(test_app "Testing application to send dummy message to another peer, like a Diameter 'ping'" OFF) 93 FD_EXTENSION_SUBDIR(test_sip "Testing application to simulate Diameter-SIP client (RFC4740)" OFF) 94 FD_EXTENSION_SUBDIR(dbg_msg_dumps "Show human-readable content of the received & sent messages" ON) 95 FD_EXTENSION_SUBDIR(dbg_rt "Routing extension for debugging the routing module" ON) 96 FD_EXTENSION_SUBDIR(test_app "Testing application to send dummy message to another peer, like a Diameter 'ping'" OFF) 97 FD_EXTENSION_SUBDIR(test_as "Receive Abort-Session-Requests and display the data" OFF) 98 FD_EXTENSION_SUBDIR(test_cc "Receive Credit-Control-Requests and display the data" OFF) 99 FD_EXTENSION_SUBDIR(test_sip "Testing application to simulate Diameter-SIP client (RFC4740)" OFF) 94 100 FD_EXTENSION_SUBDIR(dbg_interactive "Python-interpreter based module" OFF) 95 FD_EXTENSION_SUBDIR(test_netemul "Simple Diameter network emulator proxy extension (latency, PDV, duplicates)" OFF)101 FD_EXTENSION_SUBDIR(test_netemul "Simple Diameter network emulator proxy extension (latency, PDV, duplicates)" OFF) 96 102 97 103 -
extensions/dict_dcca_3gpp/dict_dcca_3gpp.c
r1324 r1337 10580 10580 { { .avp_vendor = 10415, .avp_name = "Interface-Port" }, RULE_OPTIONAL, -1, 1 }, 10581 10581 { { .avp_vendor = 10415, .avp_name = "Interface-Type" }, RULE_OPTIONAL, -1, 1 }, 10582 }; 10583 PARSE_loc_rules(rules, rule_avp); 10584 } 10585 10586 { 10587 struct dict_object *rule_avp; 10588 struct dict_avp_request vpa; 10589 vpa.avp_vendor = 10415; 10590 vpa.avp_name = "Originator-Received-Address"; 10591 CHECK_dict_search(DICT_AVP, AVP_BY_NAME_AND_VENDOR, &vpa, &rule_avp); 10592 struct local_rules_definition rules[] = 10593 { 10594 { { .avp_vendor = 10415, .avp_name = "Address-Type" }, RULE_OPTIONAL, -1, 1 }, 10595 { { .avp_vendor = 10415, .avp_name = "Address-Data" }, RULE_OPTIONAL, -1, 1 }, 10596 { { .avp_vendor = 10415, .avp_name = "Address-Domain" }, RULE_OPTIONAL, -1, 1 }, 10582 10597 }; 10583 10598 PARSE_loc_rules(rules, rule_avp); -
extensions/rt_default/rt_default.c
r1235 r1336 38 38 */ 39 39 40 #include <signal.h> 41 40 42 #include "rt_default.h" 43 44 #define MODULE_NAME "rt_default" 45 46 #include <pthread.h> 47 48 static pthread_rwlock_t rtd_lock; 49 50 static char *rtd_config_file; 41 51 42 52 /* The callback called on new messages */ … … 45 55 struct msg * msg = *pmsg; 46 56 TRACE_ENTRY("%p %p %p", cbdata, msg, candidates); 57 int ret; 47 58 48 59 CHECK_PARAMS(msg && candidates); 49 60 61 if (pthread_rwlock_rdlock(&rtd_lock) != 0) { 62 fd_log_notice("%s: read-lock failed, skipping handler", MODULE_NAME); 63 return 0; 64 } 50 65 /* Simply pass it to the appropriate function */ 51 66 if (FD_IS_LIST_EMPTY(candidates)) { 52 ret urn0;67 ret = 0; 53 68 } else { 54 ret urnrtd_process( msg, candidates );69 ret = rtd_process( msg, candidates ); 55 70 } 71 if (pthread_rwlock_unlock(&rtd_lock) != 0) { 72 fd_log_notice("%s: read-unlock failed after rtd_out, exiting", MODULE_NAME); 73 exit(1); 74 } 75 return ret; 56 76 } 57 77 … … 59 79 static struct fd_rt_out_hdl * rtd_hdl = NULL; 60 80 81 static volatile int in_signal_handler = 0; 82 83 /* signal handler */ 84 static void sig_hdlr(void) 85 { 86 if (in_signal_handler) { 87 fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); 88 return; 89 } 90 in_signal_handler = 1; 91 92 if (pthread_rwlock_wrlock(&rtd_lock) != 0) { 93 fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); 94 return; 95 } 96 rtd_conf_reload(rtd_config_file); 97 if (pthread_rwlock_unlock(&rtd_lock) != 0) { 98 fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); 99 exit(1); 100 } 101 102 fd_log_notice("%s: reloaded configuration", MODULE_NAME); 103 104 in_signal_handler = 0; 105 } 106 107 61 108 /* entry point */ 62 109 static int rtd_entry(char * conffile) 63 110 { 64 111 TRACE_ENTRY("%p", conffile); 65 112 113 rtd_config_file = conffile; 114 pthread_rwlock_init(&rtd_lock, NULL); 115 116 if (pthread_rwlock_wrlock(&rtd_lock) != 0) { 117 fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); 118 return EDEADLK; 119 } 120 66 121 /* Initialize the repo */ 67 122 CHECK_FCT( rtd_init() ); … … 69 124 /* Parse the configuration file */ 70 125 CHECK_FCT( rtd_conf_handle(conffile) ); 71 126 127 if (pthread_rwlock_unlock(&rtd_lock) != 0) { 128 fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); 129 return EDEADLK; 130 } 131 132 /* Register reload callback */ 133 CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); 134 72 135 #if 0 73 136 /* Dump the rules */ … … 92 155 /* Destroy the data */ 93 156 rtd_fini(); 94 157 158 pthread_rwlock_destroy(&rtd_lock); 159 95 160 /* Done */ 96 161 return ; 97 162 } 98 163 99 EXTENSION_ENTRY( "rt_default", rtd_entry);164 EXTENSION_ENTRY(MODULE_NAME, rtd_entry); -
extensions/rt_default/rt_default.h
r741 r1336 83 83 int rtd_process( struct msg * msg, struct fd_list * candidates ); 84 84 85 /* Reload the config file */ 86 void rtd_conf_reload(char *config_file); 87 85 88 /* For debug: dump the rule repository */ 86 89 void rtd_dump(void); -
extensions/rt_default/rtd_conf.y
r1127 r1336 55 55 /* Forward declaration */ 56 56 int yyparse(char * conffile); 57 void rtd_confrestart(FILE *input_file); 57 58 58 59 static int rules_added = 0; … … 66 67 TRACE_ENTRY("%p", conffile); 67 68 68 TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); 69 69 TRACE_DEBUG (FULL, "rt_default: Parsing configuration file: %s...", conffile); 70 71 rules_added = 0; 70 72 rtd_confin = fopen(conffile, "r"); 71 73 if (rtd_confin == NULL) { 72 74 ret = errno; 73 75 fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret)); 74 TRACE_DEBUG (INFO, " Error occurred, message logged -- configuration file.");76 TRACE_DEBUG (INFO, "rt_default: Error occurred, message logged -- configuration file."); 75 77 return ret; 76 78 } 77 79 80 rtd_confrestart(rtd_confin); 78 81 ret = yyparse(conffile); 79 82 … … 81 84 82 85 if (ret != 0) { 83 TRACE_DEBUG (INFO, " Unable to parse the configuration file.");86 TRACE_DEBUG (INFO, "rt_default: Unable to parse the configuration file."); 84 87 return EINVAL; 85 88 } else { 86 TRACE_DEBUG( FULL, "Added %d RULES routing entries successfully.", rules_added);89 TRACE_DEBUG(INFO, "rt_default: Added %d RULES routing entries successfully.", rules_added); 87 90 } 88 91 … … 96 99 void yyerror (YYLTYPE *ploc, char * conffile, char const *s) 97 100 { 98 TRACE_DEBUG(INFO, " Error in configuration parsing");101 TRACE_DEBUG(INFO, "rt_default: Error in configuration parsing"); 99 102 100 103 if (ploc->first_line != ploc->last_line) -
extensions/rt_default/rtd_rules.c
r1179 r1336 47 47 * Under each TARGET element, we have the list of RULES that are defined for this target, ordered by CRITERIA type, then is_regex, then string value. 48 48 * 49 * Note: Except during configuration parsing and module termination, the lists are only ever accessed read-only, so we do not need a lock.49 * Note: Access these only when holding rtd_lock; config reload may change the underlying data. 50 50 */ 51 51 … … 390 390 } 391 391 392 /* Destroy the module's data */ 393 void rtd_fini(void) 392 static void free_targets(void) 394 393 { 395 394 int i; 396 397 TRACE_ENTRY();398 395 399 396 for (i = 0; i < RTD_TAR_MAX; i++) { … … 402 399 } 403 400 } 404 401 } 402 403 /* Destroy the module's data */ 404 void rtd_fini(void) 405 { 406 TRACE_ENTRY(); 407 408 free_targets(); 405 409 } 406 410 … … 493 497 494 498 return 0; 499 } 500 501 void rtd_conf_reload(char *config_file) 502 { 503 /* save old config in case reload goes wrong */ 504 struct fd_list old_config[RTD_TAR_MAX]; 505 int i; 506 507 for (i = 0; i < RTD_TAR_MAX; i++) { 508 old_config[i] = TARGETS[i]; 509 } 510 memset(TARGETS, 0, sizeof(*TARGETS) * RTD_TAR_MAX); 511 for (i = 0; i < RTD_TAR_MAX; i++) { 512 fd_list_init(&TARGETS[i], NULL); 513 } 514 515 if (rtd_conf_handle(config_file) != 0) { 516 fd_log_notice("rt_default: error reloading configuration, restoring previous configuration"); 517 free_targets(); 518 for (i = 0; i < RTD_TAR_MAX; i++) { 519 TARGETS[i] = old_config[i]; 520 } 521 } else { 522 /* this has to be done in this weird way because the items contain back pointers referencing TARGETS */ 523 struct fd_list save_config[RTD_TAR_MAX]; 524 for (i = 0; i < RTD_TAR_MAX; i++) { 525 save_config[i] = TARGETS[i]; 526 TARGETS[i] = old_config[i]; 527 } 528 free_targets(); 529 for (i = 0; i < RTD_TAR_MAX; i++) { 530 TARGETS[i] = save_config[i]; 531 } 532 } 495 533 } 496 534 -
extensions/rt_ereg/rtereg.c
r1216 r1338 34 34 *********************************************************************************************************/ 35 35 36 /* 36 /* 37 37 * This extension allows to perform some pattern-matching on an AVP 38 38 * and send the message to a server accordingly. … … 40 40 */ 41 41 42 #include <pthread.h> 43 #include <signal.h> 44 42 45 #include "rtereg.h" 43 46 47 static pthread_rwlock_t rte_lock; 48 49 #define MODULE_NAME "rt_ereg" 50 51 static char *rt_ereg_config_file; 52 44 53 /* The configuration structure */ 45 struct rtereg_conf rtereg_conf; 54 struct rtereg_conf *rtereg_conf; 55 int rtereg_conf_size; 46 56 47 57 #ifndef HAVE_REG_STARTEND … … 51 61 #endif /* HAVE_REG_STARTEND */ 52 62 53 static int proceed(char * value, size_t len, struct fd_list * candidates) 63 static int rtereg_init(void); 64 static int rtereg_init_config(void); 65 static void rtereg_fini(void); 66 67 void rtereg_conf_free(struct rtereg_conf *config_struct, int config_size) 68 { 69 int i, j; 70 71 /* Destroy the data */ 72 for (j=0; j<config_size; j++) { 73 if (config_struct[j].rules) { 74 for (i = 0; i < config_struct[j].rules_nb; i++) { 75 free(config_struct[j].rules[i].pattern); 76 free(config_struct[j].rules[i].server); 77 regfree(&config_struct[j].rules[i].preg); 78 } 79 } 80 free(config_struct[j].avps); 81 free(config_struct[j].rules); 82 } 83 free(config_struct); 84 } 85 86 static int proceed(char * value, size_t len, struct fd_list * candidates, int conf) 54 87 { 55 88 int i; 56 57 for (i = 0; i < rtereg_conf .rules_nb; i++) {89 90 for (i = 0; i < rtereg_conf[conf].rules_nb; i++) { 58 91 /* Does this pattern match the value? */ 59 struct rtereg_rule * r = &rtereg_conf .rules[i];92 struct rtereg_rule * r = &rtereg_conf[conf].rules[i]; 60 93 int err = 0; 61 94 struct fd_list * c; 62 95 63 96 TRACE_DEBUG(ANNOYING, "Attempt pattern matching of '%.*s' with rule '%s'", (int)len, value, r->pattern); 64 97 65 98 #ifdef HAVE_REG_STARTEND 66 99 { … … 77 110 } 78 111 #endif /* HAVE_REG_STARTEND */ 79 112 80 113 if (err == REG_NOMATCH) 81 114 continue; 82 115 83 116 if (err != 0) { 84 117 char * errstr; … … 100 133 /* Free the buffer, return the error */ 101 134 free(errstr); 102 135 103 136 return (err == REG_ESPACE) ? ENOMEM : EINVAL; 104 137 } 105 138 106 139 /* From this point, the expression matched the AVP value */ 107 140 TRACE_DEBUG(FULL, "[rt_ereg] Match: '%s' to value '%.*s' => '%s' += %d", … … 111 144 r->server, 112 145 r->score); 113 146 114 147 for (c = candidates->next; c != candidates; c = c->next) { 115 148 struct rtd_candidate * cand = (struct rtd_candidate *)c; … … 121 154 } 122 155 }; 123 156 157 return 0; 158 } 159 160 static int find_avp(msg_or_avp *where, int conf_index, int level, struct fd_list * candidates) 161 { 162 struct dict_object *what; 163 struct dict_avp_data dictdata; 164 struct avp *nextavp = NULL; 165 struct avp_hdr *avp_hdr = NULL; 166 167 /* iterate over all AVPs and try to find a match */ 168 // for (i = 0; i<rtereg_conf[j].level; i++) { 169 if (level > rtereg_conf[conf_index].level) { 170 TRACE_DEBUG(INFO, "internal error, dug too deep"); 171 return 1; 172 } 173 what = rtereg_conf[conf_index].avps[level]; 174 175 CHECK_FCT(fd_dict_getval(what, &dictdata)); 176 CHECK_FCT(fd_msg_browse(where, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)); 177 while (nextavp) { 178 CHECK_FCT(fd_msg_avp_hdr(nextavp, &avp_hdr)); 179 if ((avp_hdr->avp_code == dictdata.avp_code) && (avp_hdr->avp_vendor == dictdata.avp_vendor)) { 180 if (level != rtereg_conf[conf_index].level - 1) { 181 TRACE_DEBUG(INFO, "[rt_ereg] found grouped AVP %d (vendor %d), digging deeper", avp_hdr->avp_code, avp_hdr->avp_vendor); 182 CHECK_FCT(find_avp(nextavp, conf_index, level+1, candidates)); 183 } else { 184 TRACE_DEBUG(INFO, "[rt_ereg] found AVP %d (vendor %d)", avp_hdr->avp_code, avp_hdr->avp_vendor); 185 if (avp_hdr->avp_value != NULL) { 186 #ifndef HAVE_REG_STARTEND 187 int ret; 188 189 /* Lock the buffer */ 190 CHECK_POSIX( pthread_mutex_lock(&mtx) ); 191 192 /* Augment the buffer if needed */ 193 if (avp_hdr->avp_value->os.len >= bufsz) { 194 CHECK_MALLOC_DO( buf = realloc(buf, avp_hdr->avp_value->os.len + 1), 195 { pthread_mutex_unlock(&mtx); return ENOMEM; } ); 196 } 197 198 /* Copy the AVP value */ 199 memcpy(buf, avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len); 200 buf[avp_hdr->avp_value->os.len] = '\0'; 201 202 /* Now apply the rules */ 203 ret = proceed(buf, avp_hdr->avp_value->os.len, candidates, conf_index); 204 205 CHECK_POSIX(pthread_mutex_unlock(&mtx)); 206 207 CHECK_FCT(ret); 208 #else /* HAVE_REG_STARTEND */ 209 CHECK_FCT( proceed((char *) avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, candidates, conf_index) ); 210 #endif /* HAVE_REG_STARTEND */ 211 } 212 } 213 } 214 CHECK_FCT(fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL)); 215 } 216 124 217 return 0; 125 218 } … … 128 221 static int rtereg_out(void * cbdata, struct msg ** pmsg, struct fd_list * candidates) 129 222 { 130 struct msg * msg = *pmsg; 131 struct avp * avp = NULL; 132 133 TRACE_ENTRY("%p %p %p", cbdata, msg, candidates); 134 135 CHECK_PARAMS(msg && candidates); 136 223 msg_or_avp *where; 224 int j, ret; 225 226 TRACE_ENTRY("%p %p %p", cbdata, *pmsg, candidates); 227 228 CHECK_PARAMS(pmsg && *pmsg && candidates); 229 230 if (pthread_rwlock_rdlock(&rte_lock) != 0) { 231 fd_log_notice("%s: read-lock failed, skipping handler", MODULE_NAME); 232 return 0; 233 } 234 ret = 0; 137 235 /* Check if it is worth processing the message */ 138 if (FD_IS_LIST_EMPTY(candidates)) { 139 return 0; 140 } 141 142 /* Now search the AVP in the message */ 143 CHECK_FCT( fd_msg_search_avp ( msg, rtereg_conf.avp, &avp ) ); 144 if (avp != NULL) { 145 struct avp_hdr * ahdr = NULL; 146 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) ); 147 if (ahdr->avp_value != NULL) { 148 #ifndef HAVE_REG_STARTEND 149 int ret; 150 151 /* Lock the buffer */ 152 CHECK_POSIX( pthread_mutex_lock(&mtx) ); 153 154 /* Augment the buffer if needed */ 155 if (ahdr->avp_value->os.len >= bufsz) { 156 CHECK_MALLOC_DO( buf = realloc(buf, ahdr->avp_value->os.len + 1), 157 { pthread_mutex_unlock(&mtx); return ENOMEM; } ); 236 if (!FD_IS_LIST_EMPTY(candidates)) { 237 /* Now search the AVPs in the message */ 238 239 for (j=0; j<rtereg_conf_size; j++) { 240 where = *pmsg; 241 TRACE_DEBUG(INFO, "[rt_ereg] iterating over AVP group %d", j); 242 if ((ret=find_avp(where, j, 0, candidates)) != 0) { 243 break; 158 244 } 159 160 /* Copy the AVP value */ 161 memcpy(buf, ahdr->avp_value->os.data, ahdr->avp_value->os.len); 162 buf[ahdr->avp_value->os.len] = '\0'; 163 164 /* Now apply the rules */ 165 ret = proceed(buf, ahdr->avp_value->os.len, candidates); 166 167 CHECK_POSIX(pthread_mutex_unlock(&mtx)); 168 169 CHECK_FCT(ret); 170 #else /* HAVE_REG_STARTEND */ 171 CHECK_FCT( proceed((char *) ahdr->avp_value->os.data, ahdr->avp_value->os.len, candidates) ); 172 #endif /* HAVE_REG_STARTEND */ 173 } 174 } 175 176 return 0; 245 } 246 } 247 if (pthread_rwlock_unlock(&rte_lock) != 0) { 248 fd_log_notice("%s: read-unlock failed after rtereg_out, exiting", MODULE_NAME); 249 exit(1); 250 } 251 252 return ret; 177 253 } 178 254 … … 180 256 static struct fd_rt_out_hdl * rtereg_hdl = NULL; 181 257 258 static volatile int in_signal_handler = 0; 259 260 /* signal handler */ 261 static void sig_hdlr(void) 262 { 263 struct rtereg_conf *old_config; 264 int old_config_size; 265 266 if (in_signal_handler) { 267 fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); 268 return; 269 } 270 in_signal_handler = 1; 271 272 if (pthread_rwlock_wrlock(&rte_lock) != 0) { 273 fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); 274 return; 275 } 276 277 /* save old config in case reload goes wrong */ 278 old_config = rtereg_conf; 279 old_config_size = rtereg_conf_size; 280 rtereg_conf = NULL; 281 rtereg_conf_size = 0; 282 283 if (rtereg_init_config() != 0) { 284 fd_log_notice("%s: error reloading configuration, restoring previous configuration", MODULE_NAME); 285 rtereg_conf = old_config; 286 rtereg_conf_size = old_config_size; 287 } else { 288 rtereg_conf_free(old_config, old_config_size); 289 } 290 291 if (pthread_rwlock_unlock(&rte_lock) != 0) { 292 fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); 293 exit(1); 294 } 295 296 fd_log_notice("%s: reloaded configuration, %d AVP group%s defined", MODULE_NAME, rtereg_conf_size, rtereg_conf_size != 1 ? "s" : ""); 297 298 in_signal_handler = 0; 299 } 300 182 301 /* entry point */ 183 302 static int rtereg_entry(char * conffile) 184 303 { 185 304 TRACE_ENTRY("%p", conffile); 186 305 306 rt_ereg_config_file = conffile; 307 308 if (rtereg_init() != 0) { 309 return 1; 310 } 311 312 /* Register reload callback */ 313 CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); 314 315 fd_log_notice("%s: configured, %d AVP group%s defined", MODULE_NAME, rtereg_conf_size, rtereg_conf_size != 1 ? "s" : ""); 316 317 return 0; 318 } 319 320 static int rtereg_init_config(void) 321 { 187 322 /* Initialize the configuration */ 188 memset(&rtereg_conf, 0, sizeof(rtereg_conf)); 189 323 if ((rtereg_conf=malloc(sizeof(*rtereg_conf))) == NULL) { 324 TRACE_DEBUG(INFO, "malloc failured"); 325 return 1; 326 } 327 rtereg_conf_size = 1; 328 memset(rtereg_conf, 0, sizeof(*rtereg_conf)); 329 190 330 /* Parse the configuration file */ 191 CHECK_FCT( rtereg_conf_handle(conffile) ); 192 331 CHECK_FCT( rtereg_conf_handle(rt_ereg_config_file) ); 332 333 return 0; 334 } 335 336 337 /* Load */ 338 static int rtereg_init(void) 339 { 340 int ret; 341 342 pthread_rwlock_init(&rte_lock, NULL); 343 344 if (pthread_rwlock_wrlock(&rte_lock) != 0) { 345 fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); 346 return EDEADLK; 347 } 348 349 if ((ret=rtereg_init_config()) != 0) { 350 pthread_rwlock_unlock(&rte_lock); 351 return ret; 352 } 353 354 if (pthread_rwlock_unlock(&rte_lock) != 0) { 355 fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); 356 return EDEADLK; 357 } 358 193 359 /* Register the callback */ 194 360 CHECK_FCT( fd_rt_out_register( rtereg_out, NULL, 1, &rtereg_hdl ) ); 195 361 196 362 /* We're done */ 197 363 return 0; … … 199 365 200 366 /* Unload */ 201 void fd_ext_fini(void) 202 { 203 int i; 367 static void rtereg_fini(void) 368 { 204 369 TRACE_ENTRY(); 205 370 206 371 /* Unregister the cb */ 207 372 CHECK_FCT_DO( fd_rt_out_unregister ( rtereg_hdl, NULL ), /* continue */ ); 208 373 209 374 /* Destroy the data */ 210 if (rtereg_conf.rules) 211 for (i = 0; i < rtereg_conf.rules_nb; i++) { 212 free(rtereg_conf.rules[i].pattern); 213 free(rtereg_conf.rules[i].server); 214 regfree(&rtereg_conf.rules[i].preg); 215 } 216 free(rtereg_conf.rules); 375 rtereg_conf_free(rtereg_conf, rtereg_conf_size); 376 rtereg_conf = NULL; 377 rtereg_conf_size = 0; 217 378 #ifndef HAVE_REG_STARTEND 218 379 free(buf); 380 buf = NULL; 219 381 #endif /* HAVE_REG_STARTEND */ 220 382 221 383 /* Done */ 222 384 return ; 223 385 } 224 386 225 EXTENSION_ENTRY("rt_ereg", rtereg_entry); 387 void fd_ext_fini(void) 388 { 389 rtereg_fini(); 390 } 391 392 EXTENSION_ENTRY(MODULE_NAME, rtereg_entry); -
extensions/rt_ereg/rtereg.h
r741 r1338 58 58 int rules_nb; /* Number of rules in the configuration */ 59 59 struct rtereg_rule *rules; /* The array of rules */ 60 61 int level; /* how many levels of AVPs we have to dig down into */ 62 int finished; /* AVP fully configured, for configuration file reading */ 63 struct dict_object **avps; /* cache the dictionary objects that we are searching */ 60 64 61 struct dict_object * avp; /* cache the dictionary object that we are searching */ 62 63 } rtereg_conf; 65 } *rtereg_conf; 64 66 67 extern int rtereg_conf_size; -
extensions/rt_ereg/rtereg_conf.y
r1127 r1342 38 38 39 39 /* For development only : */ 40 %debug 40 %debug 41 41 %error-verbose 42 42 … … 45 45 46 46 /* Keep track of location */ 47 %locations 47 %locations 48 48 %pure-parser 49 49 … … 54 54 /* Forward declaration */ 55 55 int yyparse(char * conffile); 56 void rtereg_confrestart(FILE *input_file); 56 57 57 58 /* Parse the configuration file */ … … 60 61 extern FILE * rtereg_confin; 61 62 int ret; 62 63 63 64 TRACE_ENTRY("%p", conffile); 64 65 65 66 TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); 66 67 67 68 rtereg_confin = fopen(conffile, "r"); 68 69 if (rtereg_confin == NULL) { 69 70 ret = errno; 70 71 fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret)); 71 TRACE_DEBUG (INFO, " Error occurred, message logged -- configuration file.");72 TRACE_DEBUG (INFO, "rt_ereg: error occurred, message logged -- configuration file."); 72 73 return ret; 73 74 } 74 75 76 rtereg_confrestart(rtereg_confin); 75 77 ret = yyparse(conffile); 76 78 77 79 fclose(rtereg_confin); 78 80 81 if (rtereg_conf[rtereg_conf_size-1].finished == 0) { 82 TRACE_DEBUG(INFO, "rt_ereg: configuration invalid, AVP ended without OCTETSTRING AVP"); 83 return EINVAL; 84 } 85 79 86 if (ret != 0) { 80 TRACE_DEBUG (INFO, "Unable to parse the configuration file.");87 TRACE_DEBUG(INFO, "rt_ereg: unable to parse the configuration file."); 81 88 return EINVAL; 82 89 } else { 83 TRACE_DEBUG(FULL, "[rt-ereg] Added %d rules successfully.", rtereg_conf.rules_nb); 84 } 85 90 int i, sum = 0; 91 for (i=0; i<rtereg_conf_size; i++) { 92 sum += rtereg_conf[i].rules_nb; 93 } 94 TRACE_DEBUG(FULL, "[rt-ereg] Added %d rules successfully.", sum); 95 } 96 97 return 0; 98 } 99 100 int avp_add(const char *name) 101 { 102 void *ret; 103 int level; 104 105 if (rtereg_conf[rtereg_conf_size-1].finished) { 106 if ((ret = realloc(rtereg_conf, sizeof(*rtereg_conf)*(rtereg_conf_size+1))) == NULL) { 107 TRACE_DEBUG(INFO, "rt_ereg: realloc failed"); 108 return -1; 109 } 110 rtereg_conf_size++; 111 rtereg_conf = ret; 112 memset(&rtereg_conf[rtereg_conf_size-1], 0, sizeof(*rtereg_conf)); 113 TRACE_DEBUG(INFO, "rt_ereg: New AVP group found starting with %s", name); 114 } 115 level = rtereg_conf[rtereg_conf_size-1].level + 1; 116 117 if ((ret = realloc(rtereg_conf[rtereg_conf_size-1].avps, sizeof(*rtereg_conf[rtereg_conf_size-1].avps)*level)) == NULL) { 118 TRACE_DEBUG(INFO, "rt_ereg: realloc failed"); 119 return -1; 120 } 121 rtereg_conf[rtereg_conf_size-1].avps = ret; 122 123 CHECK_FCT_DO( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, name, &rtereg_conf[rtereg_conf_size-1].avps[level-1], ENOENT ), 124 { 125 TRACE_DEBUG(INFO, "rt_ereg: Unable to find '%s' AVP in the loaded dictionaries.", name); 126 return -1; 127 } ); 128 129 /* Now check the type */ 130 { 131 struct dict_avp_data data; 132 CHECK_FCT( fd_dict_getval( rtereg_conf[rtereg_conf_size-1].avps[level-1], &data) ); 133 if (data.avp_basetype == AVP_TYPE_OCTETSTRING) { 134 rtereg_conf[rtereg_conf_size-1].finished = 1; 135 } else if (data.avp_basetype != AVP_TYPE_GROUPED) { 136 TRACE_DEBUG(INFO, "rt_ereg: '%s' AVP is not an OCTETSTRING nor GROUPED AVP (%d).", name, data.avp_basetype); 137 return -1; 138 } 139 } 140 rtereg_conf[rtereg_conf_size-1].level = level; 86 141 return 0; 87 142 } … … 93 148 void yyerror (YYLTYPE *ploc, char * conffile, char const *s) 94 149 { 95 TRACE_DEBUG(INFO, " Error in configuration parsing");96 150 TRACE_DEBUG(INFO, "rt_ereg: error in configuration parsing"); 151 97 152 if (ploc->first_line != ploc->last_line) 98 153 fd_log_debug("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); … … 126 181 127 182 /* The grammar definition */ 128 conffile: rules avp rules 129 ; 130 183 conffile: avp rules 184 | conffile avp rules 185 ; 186 131 187 /* a server entry */ 132 avp: AVP '=' QSTRING ';' 133 { 134 if (rtereg_conf.avp != NULL) { 135 yyerror(&yylloc, conffile, "Only one AVP can be specified"); 136 YYERROR; 137 } 138 139 CHECK_FCT_DO( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, $3, &rtereg_conf.avp, ENOENT ), 140 { 141 TRACE_DEBUG(INFO, "Unable to find '%s' AVP in the loaded dictionaries.", $3); 142 yyerror (&yylloc, conffile, "Invalid AVP value."); 143 YYERROR; 144 } ); 145 146 /* Now check the type */ 147 { 148 struct dict_avp_data data; 149 CHECK_FCT( fd_dict_getval( rtereg_conf.avp, &data) ); 150 CHECK_PARAMS_DO (data.avp_basetype == AVP_TYPE_OCTETSTRING, 151 { 152 TRACE_DEBUG(INFO, "'%s' AVP in not an OCTETSTRING AVP (%d).", $3, data.avp_basetype); 153 yyerror (&yylloc, conffile, "AVP in not an OCTETSTRING type."); 154 YYERROR; 155 } ); 156 } 157 } 158 ; 159 188 avp: AVP '=' avp_part ';' 189 ; 190 191 avp_part: avp_part ':' QSTRING { if (avp_add($3) < 0) { YYERROR; } } 192 | QSTRING { if (avp_add($1) < 0) { YYERROR; } } 193 ; 194 160 195 rules: /* empty OK */ 161 196 | rules rule 162 197 ; 163 198 164 199 rule: QSTRING ':' QSTRING '+' '=' INTEGER ';' 165 200 { 166 201 struct rtereg_rule * new; 167 202 int err; 168 203 169 204 /* Add new rule in the array */ 170 rtereg_conf .rules_nb += 1;171 CHECK_MALLOC_DO(rtereg_conf .rules = realloc(rtereg_conf.rules, rtereg_conf.rules_nb * sizeof(struct rtereg_rule)),205 rtereg_conf[rtereg_conf_size-1].rules_nb += 1; 206 CHECK_MALLOC_DO(rtereg_conf[rtereg_conf_size-1].rules = realloc(rtereg_conf[rtereg_conf_size-1].rules, rtereg_conf[rtereg_conf_size-1].rules_nb * sizeof(struct rtereg_rule)), 172 207 { 173 208 yyerror (&yylloc, conffile, "Not enough memory to store the configuration..."); 174 209 YYERROR; 175 210 } ); 176 177 new = &rtereg_conf .rules[rtereg_conf.rules_nb - 1];178 211 212 new = &rtereg_conf[rtereg_conf_size-1].rules[rtereg_conf[rtereg_conf_size-1].rules_nb - 1]; 213 179 214 new->pattern = $1; 180 215 new->server = $3; 181 216 new->score = $6; 182 217 183 218 /* Attempt to compile the regex */ 184 219 CHECK_FCT_DO( err=regcomp(&new->preg, new->pattern, REG_EXTENDED | REG_NOSUB), … … 188 223 189 224 /* Error while compiling the regex */ 190 TRACE_DEBUG(INFO, " Error while compiling the regular expression '%s':", new->pattern);225 TRACE_DEBUG(INFO, "rt_ereg: error while compiling the regular expression '%s':", new->pattern); 191 226 192 227 /* Get the error message size */ … … 199 234 regerror(err, &new->preg, buf, bl); 200 235 TRACE_DEBUG(INFO, "\t%s", buf); 201 236 202 237 /* Free the buffer, return the error */ 203 238 free(buf); 204 239 205 240 yyerror (&yylloc, conffile, "Invalid regular expression."); 206 241 YYERROR; -
freeDiameterd/main.c
r1305 r1339 34 34 *********************************************************************************************************/ 35 35 36 #if defined(__GLIBC__) 37 #define _BSD_SOURCE /* for vsyslog */ 38 #endif 39 36 40 #include <freeDiameter/freeDiameter-host.h> 37 41 #include <freeDiameter/libfdcore.h> … … 40 44 #include <getopt.h> 41 45 #include <locale.h> 42 46 #include <syslog.h> 47 #include <stdarg.h> 43 48 44 49 /* forward declarations */ … … 56 61 57 62 63 static void syslog_logger(int loglevel, const char * format, va_list args) 64 { 65 int level; 66 67 switch (loglevel) { 68 case FD_LOG_NOTICE: 69 level = LOG_NOTICE; 70 break; 71 case FD_LOG_ERROR: 72 level = LOG_ERR; 73 break; 74 case FD_LOG_FATAL: 75 level = LOG_CRIT; 76 break; 77 default: 78 /* fallthrough */ 79 case FD_LOG_DEBUG: 80 /* some systems log LOG_DEBUG to a file; but 81 * freeDiameter debug output is too verbose */ 82 return; 83 #if 0 84 level = LOG_DEBUG; 85 break; 86 #endif 87 } 88 89 vsyslog(level, format, args); 90 } 91 92 58 93 /* freeDiameter starting point */ 59 94 int main(int argc, char * argv[]) … … 61 96 int ret; 62 97 sigset_t sig_all; 63 98 64 99 /* Block all signals from the current thread and all its future children -- we will catch everything in catch_signals */ 65 100 sigfillset(&sig_all); 66 101 ret = pthread_sigmask(SIG_BLOCK, &sig_all, NULL); 67 102 ASSERT(ret == 0); 68 103 69 104 /* Parse the command-line */ 70 105 ret = main_cmdline(argc, argv); … … 72 107 return ret; 73 108 } 74 109 75 110 /* Initialize the core library */ 76 111 ret = fd_core_initialize(); … … 79 114 return ret; 80 115 } 81 116 82 117 /* Set gnutls debug level ? */ 83 118 if (gnutls_debug) { … … 86 121 TRACE_DEBUG(INFO, "Enabled GNUTLS debug at level %d", gnutls_debug); 87 122 } 88 123 89 124 /* Parse the configuration file */ 90 125 CHECK_FCT_DO( fd_core_parseconf(conffile), goto error ); 91 126 92 127 /* Start the servers */ 93 128 CHECK_FCT_DO( fd_core_start(), goto error ); 94 129 95 130 /* Allow SIGINT and SIGTERM from this point to terminate the application */ 96 131 CHECK_POSIX_DO( pthread_create(&signals_thr, NULL, catch_signals, NULL), goto error ); 97 132 98 133 TRACE_DEBUG(INFO, FD_PROJECT_BINARY " daemon initialized."); 99 134 100 135 /* Now, just wait for termination */ 101 136 CHECK_FCT( fd_core_wait_shutdown_complete() ); 102 137 103 138 /* Just in case it was not the result of a signal, we cancel signals_thr */ 104 139 fd_thr_term(&signals_thr); 105 140 106 141 return 0; 107 error: 142 error: 108 143 CHECK_FCT_DO( fd_core_shutdown(), ); 109 144 CHECK_FCT( fd_core_wait_shutdown_complete() ); … … 138 173 " -V, --version Print version and exit\n" 139 174 " -c, --config=filename Read configuration from this file instead of the \n" 140 " default location (" DEFAULT_CONF_PATH "/" FD_DEFAULT_CONF_FILENAME ").\n"); 175 " default location (" DEFAULT_CONF_PATH "/" FD_DEFAULT_CONF_FILENAME ").\n" 176 " -s, --syslog Write log output to syslog (instead of stdout)\n"); 141 177 printf( "\nDebug:\n" 142 178 " These options are mostly useful for developers\n" … … 156 192 int option_index = 0; 157 193 char * locale; 158 194 159 195 struct option long_options[] = { 160 196 { "help", no_argument, NULL, 'h' }, 161 197 { "version", no_argument, NULL, 'V' }, 162 198 { "config", required_argument, NULL, 'c' }, 199 { "syslog", no_argument, NULL, 's' }, 163 200 { "debug", no_argument, NULL, 'd' }, 164 201 { "quiet", no_argument, NULL, 'q' }, … … 169 206 { NULL, 0, NULL, 0 } 170 207 }; 171 208 172 209 /* Loop on arguments */ 173 210 while (1) { 174 c = getopt_long (argc, argv, "hVc:dql:f:F:g: ", long_options, &option_index);175 if (c == -1) 211 c = getopt_long (argc, argv, "hVc:dql:f:F:g:s", long_options, &option_index); 212 if (c == -1) 176 213 break; /* Exit from the loop. */ 177 214 178 215 switch (c) { 179 216 case 'h': /* Print help and exit. */ … … 200 237 } 201 238 break; 202 239 203 240 case 'd': /* Increase verbosity of debug messages. */ 204 241 fd_g_debug_lvl--; 205 242 break; 206 243 207 244 case 'f': /* Full debug for the function with this name. */ 208 245 #ifdef DEBUG … … 214 251 #endif /* DEBUG */ 215 252 break; 216 253 217 254 case 'F': /* Full debug for the file with this name. */ 218 255 #ifdef DEBUG … … 224 261 #endif /* DEBUG */ 225 262 break; 226 263 227 264 case 'g': /* Set a debug level and function for GNU TLS calls. */ 228 265 gnutls_debug = (int)atoi(optarg); 229 266 break; 230 267 231 268 case 'q': /* Decrease verbosity then remove debug messages. */ 232 269 fd_g_debug_lvl++; 270 break; 271 272 case 's': /* Write log data using syslog(3) */ 273 if (fd_log_handler_register(syslog_logger) != 0) { 274 fprintf(stderr, "Cannot initialize syslog logger\n"); 275 return EINVAL; 276 } 233 277 break; 234 278 … … 244 288 } 245 289 } 246 290 247 291 return 0; 248 292 } … … 253 297 sigset_t ss; 254 298 fd_log_threadname ( "signals catcher" ); 255 299 256 300 sigemptyset(&ss); 257 301 258 302 /* Signals that terminate the daemon */ 259 303 sigaddset(&ss, SIGTERM); 260 304 sigaddset(&ss, SIGINT); 261 305 262 306 /* Signals that send an event */ 263 307 sigaddset(&ss, SIGUSR1); 264 308 sigaddset(&ss, SIGUSR2); 265 309 266 310 /* We unblock all other signals, so that their default handler is used (such as SIGTSTP) */ 267 311 CHECK_SYS_DO( pthread_sigmask( SIG_SETMASK, &ss, NULL ), goto out ); 268 312 269 313 /* Now loop on the reception of the signal */ 270 314 while (1) { 271 315 int sig, *ps; 272 316 273 317 /* Wait to receive the next signal */ 274 318 CHECK_POSIX_DO( sigwait(&ss, &sig), break ); 275 319 276 320 TRACE_DEBUG(FULL, "Signal %d caught", sig); 277 321 278 322 switch (sig) { 279 323 case SIGUSR1: … … 283 327 CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TRIGGER, sizeof(int), ps), goto out ); 284 328 break; 285 329 286 330 case SIGINT: 287 331 case SIGTERM: … … 290 334 } 291 335 } 292 out: 336 out: 293 337 /* Better way to handle this ? */ 294 338 ASSERT(0); -
include/freeDiameter/CMakeLists.txt
r1295 r1333 18 18 # In DEBUG mode, each log can contain pid, calling function and file for easy debug. Set to ON to display this information. 19 19 OPTION(DEBUG_WITH_META "Show calling location in logs?" OFF) 20 20 21 21 # Create the absolute path for searching extensions 22 22 SET(DEFAULT_EXTENSIONS_PATH ${CMAKE_INSTALL_PREFIX}/${INSTALL_EXTENSIONS_SUFFIX}) … … 32 32 OPTION(DISABLE_PEER_EXPIRY "Disable RFC3539 Peers Connections Expiration after inactivity?" OFF) 33 33 34 # The following workaround increases compatibility with some implementations without breaking anything in freeDiameter, 35 # so it can be enabled without risk. We keep it disabled by default anyway for those people who use freeDiameter to check the 34 # The following workaround increases compatibility with some implementations without breaking anything in freeDiameter, 35 # so it can be enabled without risk. We keep it disabled by default anyway for those people who use freeDiameter to check the 36 36 # compliancy of their implementation with the Diameter RFC... 37 37 OPTION(WORKAROUND_ACCEPT_INVALID_VSAI "Do not reject a CER/CEA with a Vendor-Specific-Application-Id AVP containing both Auth- and Acct- application AVPs?" OFF) … … 45 45 INCLUDE (CheckFunctionExists) 46 46 INCLUDE (CheckIncludeFiles) 47 INCLUDE (CheckSymbolExists) 47 INCLUDE (CheckSymbolExists) 48 48 INCLUDE (CheckCSourceCompiles) 49 49 INCLUDE (TestBigEndian) … … 134 134 135 135 # IDNA process: we use libidn from GNU (unless the function & header files are included in libc) 136 IF(NOT DIAMID_IDNA_IGNORE 136 IF(NOT DIAMID_IDNA_IGNORE AND NOT DIAMID_IDNA_REJECT) 137 137 FIND_PACKAGE(IDNA) 138 138 SET(CHECK_IDNA_SOURCE_CODE " … … 160 160 SET(GNUTLS_LIBRARIES ${GNUTLS_LIBRARIES} PARENT_SCOPE) 161 161 162 find_path(GCRYPT_INCLUDE_DIR NAMES gcrypt.h)163 I f ( NOT GCRYPT_INCLUDE_DIR)162 FIND_PATH(GCRYPT_INCLUDE_DIR NAMES gcrypt.h) 163 IF(NOT GCRYPT_INCLUDE_DIR) 164 164 MESSAGE(SEND_ERROR "Unable to find gcrypt.h, please install libgcrypt-dev or equivalent") 165 E ndif ( NOT GCRYPT_INCLUDE_DIR)165 ENDIF(NOT GCRYPT_INCLUDE_DIR) 166 166 MARK_AS_ADVANCED(GCRYPT_INCLUDE_DIR) 167 167 SET(GCRYPT_INCLUDE_DIR ${GCRYPT_INCLUDE_DIR} PARENT_SCOPE) 168 168 169 169 # Also we need libgcrypt to... display its version :( 170 find_library(GCRYPT_LIBRARY 170 FIND_LIBRARY(GCRYPT_LIBRARY 171 171 NAMES gcrypt 172 172 ) 173 I f ( NOT GCRYPT_LIBRARY)173 IF(NOT GCRYPT_LIBRARY) 174 174 MESSAGE(SEND_ERROR "Unable to find libgcrypt, please install libgcrypt or equivalent") 175 E ndif ( NOT GCRYPT_LIBRARY)175 ENDIF(NOT GCRYPT_LIBRARY) 176 176 SET(GCRYPT_LIBRARY ${GCRYPT_LIBRARY} PARENT_SCOPE) 177 177 … … 229 229 SET(LFDCORE_INCLUDES ${SCTP_INCLUDE_DIR} ${GNUTLS_INCLUDE_DIR} ${GCRYPT_INCLUDE_DIR} PARENT_SCOPE) 230 230 # And dependencies 231 SET(LFDCORE_LINK_INTERFACES "" PARENT_SCOPE) 231 SET(LFDCORE_LINK_INTERFACES "" PARENT_SCOPE) 232 232 # We don't force other libraries, the programs will link with what it needs 233 233 # (such as libgnutls if it uses GNUTLS_DEBUG() macro -
libfdcore/sctp.c
r1268 r1332 401 401 fd_log_debug( " sctp_shutdown_event : %hhu", event.sctp_shutdown_event); 402 402 fd_log_debug( " sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event); 403 fd_log_debug( " sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event);403 // fd_log_debug( " sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event); 404 404 // fd_log_debug( " sctp_authentication_event : %hhu", event.sctp_authentication_event); 405 405 } -
libfdcore/sctp3436.c
r1190 r1344 51 51 - the push function sends the data on a certain stream. 52 52 We also have a demux thread that reads the socket and store received data in the appropriate fifo 53 53 54 54 We have one gnutls_session per stream pair, and as many threads that read the gnutls records and save incoming data to the target queue. 55 55 56 56 This complexity is required because we cannot read a socket for a given stream only; we can only get the next message and find its stream. 57 57 */ … … 71 71 int event; 72 72 uint16_t strid; 73 73 74 74 TRACE_ENTRY("%p", arg); 75 75 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out); 76 76 77 77 /* Set the thread name */ 78 78 { 79 char buf[ 48];79 char buf[100]; 80 80 snprintf(buf, sizeof(buf), "Demuxer (%d:%s)", conn->cc_socket, conn->cc_remid); 81 81 fd_log_threadname ( buf ); 82 82 } 83 83 84 84 ASSERT( conn->cc_proto == IPPROTO_SCTP ); 85 85 ASSERT( fd_cnx_target_queue(conn) ); 86 86 ASSERT( conn->cc_sctp3436_data.array ); 87 87 88 88 do { 89 89 CHECK_FCT_DO( fd_sctp_recvmeta(conn, &strid, &buf, &bufsz, &event), goto fatal ); … … 98 98 } 99 99 break; 100 100 101 101 case FDEVP_CNX_EP_CHANGE: 102 102 /* Send this event to the target queue */ 103 103 CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), event, bufsz, buf), goto fatal ); 104 104 break; 105 105 106 106 case FDEVP_CNX_ERROR: 107 107 goto out; 108 108 109 109 case FDEVP_CNX_SHUTDOWN: 110 110 /* Just ignore the notification for now, we will get another error later anyway */ 111 111 continue; 112 112 113 113 default: 114 114 goto fatal; 115 115 } 116 116 117 117 } while (conn->cc_loop); 118 118 119 119 out: 120 120 /* Signal termination of the connection to all decipher threads */ … … 125 125 } 126 126 fd_cnx_markerror(conn); 127 TRACE_DEBUG(FULL, "Thread terminated"); 127 TRACE_DEBUG(FULL, "Thread terminated"); 128 128 return NULL; 129 129 130 130 fatal: 131 131 /* An unrecoverable error occurred, stop the daemon */ … … 139 139 struct sctp3436_ctx * ctx = arg; 140 140 struct cnxctx *cnx; 141 141 142 142 TRACE_ENTRY("%p", arg); 143 143 CHECK_PARAMS_DO(ctx && ctx->raw_recv && ctx->parent, goto error); 144 144 cnx = ctx->parent; 145 145 ASSERT( fd_cnx_target_queue(cnx) ); 146 146 147 147 /* Set the thread name */ 148 148 { 149 char buf[ 48];149 char buf[100]; 150 150 snprintf(buf, sizeof(buf), "Decipher (%hu@%d:%s)", ctx->strid, cnx->cc_socket, cnx->cc_remid); 151 151 fd_log_threadname ( buf ); 152 152 } 153 153 154 154 /* The next function loops while there is no error */ 155 155 CHECK_FCT_DO(fd_tls_rcvthr_core(cnx, ctx->strid ? ctx->session : cnx->cc_tls_para.session), /* continue */); 156 156 error: 157 157 fd_cnx_markerror(cnx); 158 TRACE_DEBUG(FULL, "Thread terminated"); 158 TRACE_DEBUG(FULL, "Thread terminated"); 159 159 return NULL; 160 160 } … … 171 171 struct timespec tsstore, *ts = NULL; 172 172 int ret; 173 173 174 174 TRACE_ENTRY("%p %d", tr, ms); 175 175 176 176 if (ctx->partial.buf) 177 177 return 1; /* data is already available for pull */ 178 178 179 179 if (ms) { 180 180 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &tsstore), return -1 ); … … 184 184 ts = &tsstore; 185 185 } 186 186 187 187 ret = fd_fifo_select ( ctx->raw_recv, ts ); 188 188 if (ret < 0) { … … 190 190 ret = -1; 191 191 } 192 192 193 193 return ret; 194 194 } … … 201 201 struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr; 202 202 struct iovec iov; 203 203 204 204 TRACE_ENTRY("%p %p %zd", tr, data, len); 205 205 CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } ); 206 206 207 207 iov.iov_base = (void *)data; 208 208 iov.iov_len = len; 209 209 210 210 return fd_sctp_sendstrv(ctx->parent, ctx->strid, &iov, 1); 211 211 } … … 214 214 { 215 215 struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) tr; 216 216 217 217 TRACE_ENTRY("%p %p %d", tr, iov, iovcnt); 218 218 CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } ); 219 219 220 220 return fd_sctp_sendstrv(ctx->parent, ctx->strid, (const struct iovec *)iov, iovcnt); 221 221 } … … 228 228 size_t pulled = 0; 229 229 int emptied; 230 230 231 231 TRACE_ENTRY("%p %p %zd", tr, buf, len); 232 232 CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; goto error; } ); 233 233 234 234 /* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */ 235 235 if (!ctx->partial.buf) { … … 241 241 } 242 242 } 243 243 244 244 pulled = ctx->partial.bufsz - ctx->partial.offset; 245 245 if (pulled <= len) { … … 264 264 /* We are done */ 265 265 return pulled; 266 266 267 267 error: 268 268 gnutls_transport_set_errno (ctx->session, errno); … … 278 278 /* Set the transport pointer passed to push & pull callbacks */ 279 279 GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) ctx ) ); 280 280 281 281 /* Reset the low water value, since we don't use sockets */ 282 282 #ifndef GNUTLS_VERSION_300 … … 287 287 GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp3436_pull_timeout ) ); 288 288 #endif /* GNUTLS_VERSION_300 */ 289 289 290 290 /* Set the push and pull callbacks */ 291 291 GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp3436_pull) ); … … 325 325 TRACE_ENTRY("%p", conn); 326 326 CHECK_PARAMS( conn && !conn->cc_sctp3436_data.sess_store ); 327 327 328 328 CHECK_MALLOC( conn->cc_sctp3436_data.sess_store = malloc(sizeof(struct sr_store)) ); 329 329 memset(conn->cc_sctp3436_data.sess_store, 0, sizeof(struct sr_store)); 330 330 331 331 fd_list_init(&conn->cc_sctp3436_data.sess_store->list, NULL); 332 332 CHECK_POSIX( pthread_rwlock_init(&conn->cc_sctp3436_data.sess_store->lock, NULL) ); 333 333 conn->cc_sctp3436_data.sess_store->parent = conn; 334 334 335 335 return 0; 336 336 } … … 342 342 TRACE_ENTRY("%p", conn); 343 343 CHECK_PARAMS_DO( conn, return ); 344 344 345 345 if (!conn->cc_sctp3436_data.sess_store) 346 346 return; 347 347 348 348 CHECK_POSIX_DO( pthread_rwlock_destroy(&conn->cc_sctp3436_data.sess_store->lock), /* continue */ ); 349 349 350 350 while (!FD_IS_LIST_EMPTY(&conn->cc_sctp3436_data.sess_store->list)) { 351 351 struct sr_data * sr = (struct sr_data *) conn->cc_sctp3436_data.sess_store->list.next; … … 355 355 free(sr); 356 356 } 357 357 358 358 free(conn->cc_sctp3436_data.sess_store); 359 359 conn->cc_sctp3436_data.sess_store = NULL; … … 366 366 struct fd_list * ret; 367 367 *match = 0; 368 368 369 369 for (ret = sto->list.next; ret != &sto->list; ret = ret->next) { 370 370 int cmp = 0; 371 371 struct sr_data * sr = (struct sr_data *)ret; 372 372 373 373 cmp = fd_os_cmp(key.data, key.size, sr->key.data, sr->key.size); 374 374 if (cmp > 0) 375 375 continue; 376 376 377 377 if (cmp == 0) 378 378 *match = 1; 379 379 380 380 break; 381 381 } 382 382 383 383 return ret; 384 384 } … … 392 392 int match = 0; 393 393 int ret = 0; 394 394 395 395 TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ ); 396 396 CHECK_PARAMS_DO( sto && key.data && data.data, return -1 ); 397 397 398 398 CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 ); 399 399 TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session store [key ", key.data, key.size, "]"); 400 400 401 401 li = find_or_next(sto, key, &match); 402 402 if (match) { 403 403 sr = (struct sr_data *)li; 404 404 405 405 /* Check the data is the same */ 406 406 if ((data.size != sr->data.size) || memcmp(data.data, sr->data.data, data.size)) { … … 409 409 TRACE_BUFFER(FD_LOG_DEBUG, INFO, " -- old data [", sr->data.data, sr->data.size, "]"); 410 410 TRACE_BUFFER(FD_LOG_DEBUG, INFO, " -- new data [", data.data, data.size, "]"); 411 411 412 412 ret = -1; 413 413 } else { … … 416 416 goto out; 417 417 } 418 418 419 419 /* Create a new entry */ 420 420 CHECK_MALLOC_DO( sr = malloc(sizeof(struct sr_data)), { ret = -1; goto out; } ); … … 430 430 sr->data.size = data.size; 431 431 memcpy(sr->data.data, data.data, data.size); 432 432 433 433 /* Save this new entry in the list, we are done */ 434 434 fd_list_insert_before(li, &sr->chain); 435 435 436 out: 436 out: 437 437 CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 ); 438 438 return ret; … … 446 446 int match = 0; 447 447 int ret = 0; 448 448 449 449 TRACE_DEBUG( GNUTLS_DBG_LEVEL, "GNUTLS Callback: %s", __PRETTY_FUNCTION__ ); 450 450 CHECK_PARAMS_DO( sto && key.data, return -1 ); 451 451 452 452 CHECK_POSIX_DO( pthread_rwlock_wrlock(&sto->lock), return -1 ); 453 453 TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session delete [key ", key.data, key.size, "]"); 454 454 455 455 li = find_or_next(sto, key, &match); 456 456 if (match) { 457 457 sr = (struct sr_data *)li; 458 458 459 459 /* Destroy this data */ 460 460 fd_list_unlink(li); … … 466 466 ret = -1; 467 467 } 468 468 469 469 CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return -1 ); 470 470 return ret; … … 485 485 CHECK_POSIX_DO( pthread_rwlock_rdlock(&sto->lock), return error ); 486 486 TRACE_BUFFER(FD_LOG_DEBUG, GNUTLS_DBG_LEVEL, "Session fetch [key ", key.data, key.size, "]"); 487 487 488 488 li = find_or_next(sto, key, &match); 489 489 if (match) { … … 493 493 memcpy(res.data, sr->data.data, res.size); 494 494 } 495 out: 495 out: 496 496 TRACE_DEBUG(GNUTLS_DBG_LEVEL, "Fetched (%p, %d) from store %p", res.data, res.size, sto); 497 497 CHECK_POSIX_DO( pthread_rwlock_unlock(&sto->lock), return error); … … 503 503 { 504 504 TRACE_ENTRY("%p", conn); 505 505 506 506 GNUTLS_TRACE( gnutls_db_set_retrieve_function(session, sr_fetch)); 507 507 GNUTLS_TRACE( gnutls_db_set_remove_function (session, sr_remove)); 508 508 GNUTLS_TRACE( gnutls_db_set_store_function (session, sr_store)); 509 509 GNUTLS_TRACE( gnutls_db_set_ptr (session, conn->cc_sctp3436_data.sess_store)); 510 510 511 511 return; 512 512 } … … 517 517 struct sctp3436_ctx * ctx = (struct sctp3436_ctx *) arg; 518 518 int resumed; 519 519 520 520 TRACE_ENTRY("%p", arg); 521 521 522 522 /* Set the thread name */ 523 523 { … … 526 526 fd_log_threadname ( buf ); 527 527 } 528 528 529 529 TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid); 530 530 531 531 CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL); 532 532 533 533 GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) ); 534 534 #ifndef GNUTLS_VERSION_300 … … 545 545 } 546 546 } 547 547 548 548 /* Finished, OK */ 549 549 return arg; … … 559 559 { 560 560 uint16_t i; 561 561 562 562 TRACE_ENTRY("%p", conn); 563 563 CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && (!conn->cc_sctp3436_data.array) ); 564 564 565 565 /* First, alloc the array and initialize the non-TLS data */ 566 566 CHECK_MALLOC( conn->cc_sctp3436_data.array = calloc(conn->cc_sctp_para.pairs, sizeof(struct sctp3436_ctx)) ); … … 570 570 CHECK_FCT( fd_fifo_new(&conn->cc_sctp3436_data.array[i].raw_recv, 10) ); 571 571 } 572 572 573 573 /* Set push/pull functions in the master session, using fifo in array[0] */ 574 574 set_sess_transport(conn->cc_tls_para.session, &conn->cc_sctp3436_data.array[0]); 575 575 576 576 /* For server side, we also initialize the resuming capabilities */ 577 577 if (conn->cc_tls_para.mode == GNUTLS_SERVER) { 578 578 579 579 /* Prepare the store for sessions data */ 580 580 CHECK_FCT( store_init(conn) ); 581 581 582 582 /* Set the callbacks for resuming in the master session */ 583 583 set_resume_callbacks(conn->cc_tls_para.session, conn); … … 586 586 /* Start the demux thread */ 587 587 CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, demuxer, conn ) ); 588 588 589 589 return 0; 590 590 } … … 596 596 int errors = 0; 597 597 gnutls_datum_t master_data; 598 598 599 599 TRACE_ENTRY("%p %p", conn, priority); 600 600 CHECK_PARAMS( conn && (conn->cc_sctp_para.pairs > 1) && conn->cc_sctp3436_data.array ); … … 602 602 /* Server side: we set all the parameters, the resume callback will take care of resuming the session */ 603 603 /* Client side: we duplicate the parameters of the master session, then set the transport pointer */ 604 604 605 605 /* For client side, retrieve the master session parameters */ 606 606 if (conn->cc_tls_para.mode == GNUTLS_CLIENT) { … … 614 614 } 615 615 } 616 616 617 617 /* Initialize the session objects and start the handshake in a separate thread */ 618 618 for (i = 1; i < conn->cc_sctp_para.pairs; i++) { 619 619 /* Set credentials and priority */ 620 620 CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, 0, priority, alt_creds) ); 621 621 622 622 /* additional initialization for gnutls 3.x */ 623 623 #ifdef GNUTLS_VERSION_300 … … 644 644 set_resume_callbacks(conn->cc_sctp3436_data.array[i].session, conn); 645 645 } 646 646 647 647 /* Set transport parameters */ 648 648 set_sess_transport(conn->cc_sctp3436_data.array[i].session, &conn->cc_sctp3436_data.array[i]); 649 649 650 650 /* Start the handshake thread */ 651 651 CHECK_POSIX( pthread_create( &conn->cc_sctp3436_data.array[i].thr, NULL, handshake_resume_th, &conn->cc_sctp3436_data.array[i] ) ); 652 652 } 653 653 654 654 /* We can now release the memory of master session data if any */ 655 655 if (conn->cc_tls_para.mode == GNUTLS_CLIENT) { 656 656 GNUTLS_TRACE( gnutls_free(master_data.data) ); 657 657 } 658 658 659 659 /* Now wait for all handshakes to finish */ 660 660 for (i = 1; i < conn->cc_sctp_para.pairs; i++) { … … 666 666 } 667 667 } 668 668 669 669 if (errors) { 670 670 TRACE_DEBUG(INFO, "Handshake failed on %d/%hd stream pairs", errors, conn->cc_sctp_para.pairs); … … 672 672 return ENOTCONN; 673 673 } 674 674 675 675 return 0; 676 676 } … … 680 680 { 681 681 uint16_t i; 682 682 683 683 TRACE_ENTRY("%p", conn); 684 684 CHECK_PARAMS( conn && conn->cc_sctp3436_data.array ); 685 685 686 686 if (others) { 687 687 for (i = 1; i < conn->cc_sctp_para.pairs; i++) { … … 700 700 { 701 701 uint16_t i; 702 702 703 703 CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return ); 704 704 705 705 /* End all TLS sessions, in series (not as efficient as paralel, but simpler) */ 706 706 for (i = 1; i < conn->cc_sctp_para.pairs; i++) { … … 715 715 { 716 716 uint16_t i; 717 717 718 718 TRACE_ENTRY("%p", conn); 719 719 CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return ); 720 720 721 721 for (i = 0; i < conn->cc_sctp_para.pairs; i++) { 722 722 if (conn->cc_sctp3436_data.array[i].thr != (pthread_t)NULL) { … … 732 732 { 733 733 uint16_t i; 734 734 735 735 TRACE_ENTRY("%p", conn); 736 736 CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return ); 737 737 738 738 for (i = 1; i < conn->cc_sctp_para.pairs; i++) { 739 739 if (conn->cc_sctp3436_data.array[i].session) { … … 749 749 { 750 750 uint16_t i; 751 751 752 752 TRACE_ENTRY("%p", conn); 753 753 CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return ); 754 754 755 755 for (i = 0; i < conn->cc_sctp_para.pairs; i++) { 756 756 CHECK_FCT_DO( fd_thr_term(&conn->cc_sctp3436_data.array[i].thr), /* continue */ ); … … 763 763 { 764 764 uint16_t i; 765 765 766 766 CHECK_PARAMS_DO( conn && conn->cc_sctp3436_data.array, return ); 767 767 768 768 /* Terminate all receiving threads in case we did not do it yet */ 769 769 fd_sctp3436_stopthreads(conn); 770 770 771 771 /* Now, stop the demux thread */ 772 772 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); 773 773 774 774 /* Free remaining data in the array */ 775 775 for (i = 0; i < conn->cc_sctp_para.pairs; i++) { … … 782 782 } 783 783 } 784 784 785 785 /* Free the array itself now */ 786 786 free(conn->cc_sctp3436_data.array); 787 787 conn->cc_sctp3436_data.array = NULL; 788 788 789 789 /* Delete the store of sessions */ 790 790 store_destroy(conn); 791 791 792 792 return ; 793 793 }
Note: See TracChangeset
for help on using the changeset viewer.