# HG changeset patch # User Sebastien Decugis # Date 1474202211 -28800 # Node ID 7a2ab0087788e028eddbae0b6732a072f2bc642b # Parent 84a3c9c4b834e35f5e1af468bcf97583f9660a2a# Parent 0d15dad33f0b7ec1a6966325a48cd9a6c820d271 Remerged proposed branch diff -r 0d15dad33f0b -r 7a2ab0087788 .hgtags --- a/.hgtags Mon Jun 29 09:37:35 2015 +0200 +++ b/.hgtags Sun Sep 18 20:36:51 2016 +0800 @@ -68,3 +68,6 @@ 08e72b858f03a77869792cb8ad8e0acbc83c2590 1.2.0-rc2 f937feb727347445f8afb7759a97e34c76c0ba7b 1.2.0 ab6457399be2762b3e85bd173ba754ff758ef060 1.2.1-rc1 +043b894b0511b6beb155576e9e2c509a21be8360 proposed_merged +0000000000000000000000000000000000000000 proposed_merged +13948c684c354d8c891ca5e2a5f1d76d1f176afe 1.2.1-rc2 diff -r 0d15dad33f0b -r 7a2ab0087788 contrib/debian/changelog --- a/contrib/debian/changelog Mon Jun 29 09:37:35 2015 +0200 +++ b/contrib/debian/changelog Sun Sep 18 20:36:51 2016 +0800 @@ -1,3 +1,22 @@ +freediameter (1.2.1) UNRELEASED; urgency=low + + * New extension: rt_randomize (load-balancing on possible destinations) + * New contrib: Gx extension. + * rt_redirect.fdx and rt_load_balance.fdx improvement: use a hash table. Thanks Thomas. + * New hook HOOK_MESSAGE_SENDING to give a last chance to edit messages before they are sent. + This will be mostly used for enforcing interoperability constraints on non-compliant peers. + * Simplified log output. Old output can be recovered with DEBUG_WITH_META build option. + * Updated the internal counters for finer control on the load. + * Fixes in message expiry mechanism. + * Bug fixes in error messages generation. + * Improvements on shutdown sequence handling. + * Improvements to the dict_dcca_* extensions. + * Improved default CMake configuration. + * Fixes for a few newer operating systems compatibility. + * Fixed default secure Diameter port number 5868 instead of5658 (errata of RFC 6733) + + -- Sebastien Decugis Sat, 30 Jan 2016 23:38:03 +0800 + freediameter (1.2.0) UNRELEASED; urgency=low * Major changes in the logging system to be more syslog and production friendly diff -r 0d15dad33f0b -r 7a2ab0087788 doc/freediameter.conf.sample --- a/doc/freediameter.conf.sample Mon Jun 29 09:37:35 2015 +0200 +++ b/doc/freediameter.conf.sample Sun Sep 18 20:36:51 2016 +0800 @@ -31,8 +31,8 @@ # The port this peer is listening on for incoming TLS-protected connections (TCP and SCTP). # See TLS_old_method for more information about TLS flavours. # Note: we use TLS/SCTP instead of DTLS/SCTP at the moment. This will change in future version of freeDiameter. -# Default: 5658. Use 0 to disable. -#SecPort = 5658; +# Default: 5868. Use 0 to disable. +#SecPort = 5868; # Use RFC3588 method for TLS protection, where TLS is negociated after CER/CEA exchange is completed # on the unsecure connection. The alternative is RFC6733 mechanism, where TLS protects also the @@ -237,7 +237,7 @@ # Parameters that can be specified in the peer's parameter list: # No_TCP; No_SCTP; No_IP; No_IPv6; Prefer_TCP; TLS_old_method; # No_TLS; # assume transparent security instead of TLS. DTLS is not supported yet (will change in future versions). -# Port = 5658; # The port to connect to +# Port = 5868; # The port to connect to # TcTimer = 30; # TwTimer = 30; # ConnectTo = "202.249.37.5"; diff -r 0d15dad33f0b -r 7a2ab0087788 extensions/app_radgw/md5.c --- a/extensions/app_radgw/md5.c Mon Jun 29 09:37:35 2015 +0200 +++ b/extensions/app_radgw/md5.c Sun Sep 18 20:36:51 2016 +0800 @@ -292,7 +292,7 @@ MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); os_memcpy(digest, ctx->buf, 16); - os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + os_memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ diff -r 0d15dad33f0b -r 7a2ab0087788 extensions/app_sip/md5.c --- a/extensions/app_sip/md5.c Mon Jun 29 09:37:35 2015 +0200 +++ b/extensions/app_sip/md5.c Sun Sep 18 20:36:51 2016 +0800 @@ -315,7 +315,7 @@ MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); os_memcpy(digest, ctx->buf, 16); - os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + os_memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ diff -r 0d15dad33f0b -r 7a2ab0087788 extensions/dbg_msg_dumps/dbg_msg_dumps.c --- a/extensions/dbg_msg_dumps/dbg_msg_dumps.c Mon Jun 29 09:37:35 2015 +0200 +++ b/extensions/dbg_msg_dumps/dbg_msg_dumps.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -104,6 +104,10 @@ LOG_E("PARSING ERROR: %zdB msg from '%s': %s", rcv_data->length, peer_name, buf); } break; + case HOOK_MESSAGE_PARSING_ERROR2: + LOG_E("PARSING ERROR, returning:"); + LOG_SPLIT(FD_LOG_ERROR, " ", buf, NULL); + break; case HOOK_MESSAGE_ROUTING_ERROR: LOG_E("ROUTING ERROR '%s' for: ", (char *)other); LOG_SPLIT(FD_LOG_ERROR, " ", buf, NULL); @@ -118,6 +122,10 @@ LOG_N("RCV from '%s':", peer_name); LOG_SPLIT(FD_LOG_NOTICE, " ", buf, NULL); break; + case HOOK_MESSAGE_SENDING: + LOG_N("SNDING to '%s':", peer_name); + LOG_SPLIT(FD_LOG_NOTICE, " ", buf, NULL); + break; case HOOK_MESSAGE_SENT: LOG_N("SND to '%s':", peer_name); LOG_SPLIT(FD_LOG_NOTICE, " ", buf, NULL); @@ -193,6 +201,9 @@ LOG_E("PARSING ERROR: %zdB msg from '%s': %s", rcv_data->length, peer_name, buf); } break; + case HOOK_MESSAGE_PARSING_ERROR2: + LOG_E("PARSING ERROR, returning: %s", buf); + break; case HOOK_MESSAGE_ROUTING_ERROR: LOG_E("ROUTING ERROR '%s' for: %s", (char *)other, buf); break; @@ -204,6 +215,9 @@ case HOOK_MESSAGE_RECEIVED: LOG_N("RCV from '%s': %s", peer_name, buf); break; + case HOOK_MESSAGE_SENDING: + LOG_N("SNDING to '%s': %s", peer_name, buf); + break; case HOOK_MESSAGE_SENT: LOG_N("SND to '%s': %s", peer_name, buf); break; @@ -272,6 +286,9 @@ LOG_E("PARSING ERROR: %zdB msg from '%s': %s", rcv_data->length, peer_name, buf); } break; + case HOOK_MESSAGE_PARSING_ERROR2: + LOG_E("PARSING ERROR, returning: %s", buf); + break; case HOOK_MESSAGE_ROUTING_ERROR: LOG_E("ROUTING ERROR '%s' for: %s", (char *)other, buf); break; @@ -283,6 +300,9 @@ case HOOK_MESSAGE_RECEIVED: LOG_N("RCV from '%s': %s", peer_name, buf); break; + case HOOK_MESSAGE_SENDING: + LOG_N("SNDING to '%s': %s", peer_name, buf); + break; case HOOK_MESSAGE_SENT: LOG_N("SND to '%s': %s", peer_name, buf); break; @@ -340,8 +360,8 @@ return EINVAL; }); } - mask_errors = HOOK_MASK( HOOK_MESSAGE_FAILOVER, HOOK_MESSAGE_PARSING_ERROR, HOOK_MESSAGE_ROUTING_ERROR, HOOK_MESSAGE_DROPPED ); - mask_sndrcv = HOOK_MASK( HOOK_MESSAGE_RECEIVED, HOOK_MESSAGE_SENT ); + mask_errors = HOOK_MASK( HOOK_MESSAGE_FAILOVER, HOOK_MESSAGE_PARSING_ERROR, HOOK_MESSAGE_PARSING_ERROR2, HOOK_MESSAGE_ROUTING_ERROR, HOOK_MESSAGE_DROPPED ); + mask_sndrcv = HOOK_MASK( HOOK_MESSAGE_RECEIVED, HOOK_MESSAGE_SENT ); /* We don t access SENDING hook here */ mask_routing= HOOK_MASK( HOOK_MESSAGE_LOCAL, HOOK_MESSAGE_ROUTING_FORWARD, HOOK_MESSAGE_ROUTING_LOCAL ); mask_peers = HOOK_MASK( HOOK_PEER_CONNECT_FAILED, HOOK_PEER_CONNECT_SUCCESS ); diff -r 0d15dad33f0b -r 7a2ab0087788 extensions/test_app/ta_bench.c --- a/extensions/test_app/ta_bench.c Mon Jun 29 09:37:35 2015 +0200 +++ b/extensions/test_app/ta_bench.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -36,16 +36,66 @@ /* Create and send a message, and receive it */ #include "test_app.h" +#include +#ifndef __APPLE__ /* they deprecated the semaphore there... */ #include -#include + +#define my_sem_t sem_t +#define my_sem_init sem_init +#define my_sem_destroy sem_destroy +#define my_sem_timedwait sem_timedwait +#define my_sem_post sem_post + +#else // on APPLE +#include +#include + +#define my_sem_t dispatch_semaphore_t + +static int my_sem_init(my_sem_t * s, int pshared, unsigned int value ) { + *s = dispatch_semaphore_create(value); + if (*s == NULL) + return ENOMEM; + return 0; +} + +static int my_sem_destroy(my_sem_t *s) { + dispatch_release(*s); + *s = NULL; + return 0; +} + +static int my_sem_timedwait(my_sem_t * s, struct timespec *ts) { + struct timespec tsn; + int64_t nsec; + dispatch_time_t when; + + CHECK_SYS( clock_gettime(CLOCK_REALTIME, &tsn) ); + + nsec = (ts->tv_sec * 1000000000) + ts->tv_nsec + - (tsn.tv_sec * 1000000000) - tsn.tv_nsec; + + when = dispatch_time ( DISPATCH_TIME_NOW, nsec ); + + return dispatch_semaphore_wait ( *s, when ) ? ETIMEDOUT : 0; +} + +static int my_sem_post(my_sem_t *s) { + dispatch_semaphore_signal(*s); + return 0; +} + +#endif // APPLE + + struct ta_mess_info { int32_t randval; /* a random value to store in Test-AVP */ struct timespec ts; /* Time of sending the message */ }; -static sem_t ta_sem; /* To handle the concurrency */ +static my_sem_t ta_sem; /* To handle the concurrency */ /* Cb called when an answer is received */ static void ta_cb_ans(void * data, struct msg ** msg) @@ -109,7 +159,7 @@ free(mi); /* Post the semaphore */ - CHECK_SYS_DO( sem_post(&ta_sem), ); + CHECK_SYS_DO( my_sem_post(&ta_sem), ); return; } @@ -213,7 +263,7 @@ /* Now loop until timeout is reached */ do { /* Do not create more that NB_CONCURRENT_MESSAGES in paralel */ - int ret = sem_timedwait(&ta_sem, &end_time); + int ret = my_sem_timedwait(&ta_sem, &end_time); if (ret == -1) { ret = errno; if (ret != ETIMEDOUT) { @@ -269,7 +319,7 @@ int ta_bench_init(void) { - CHECK_SYS( sem_init( &ta_sem, 0, ta_conf->bench_concur) ); + CHECK_SYS( my_sem_init( &ta_sem, 0, ta_conf->bench_concur) ); CHECK_FCT( fd_event_trig_regcb(ta_conf->signal, "test_app.bench", ta_bench_start ) ); @@ -280,7 +330,7 @@ { // CHECK_FCT_DO( fd_sig_unregister(ta_conf->signal), /* continue */ ); - CHECK_SYS_DO( sem_destroy(&ta_sem), ); + CHECK_SYS_DO( my_sem_destroy(&ta_sem), ); return; }; diff -r 0d15dad33f0b -r 7a2ab0087788 extensions/test_app/ta_cli.c --- a/extensions/test_app/ta_cli.c Mon Jun 29 09:37:35 2015 +0200 +++ b/extensions/test_app/ta_cli.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -70,7 +70,6 @@ ASSERT( new == 0 ); CHECK_FCT_DO( fd_sess_state_retrieve( ta_cli_reg, sess, &mi ), return ); - TRACE_DEBUG( INFO, "%p %p", mi, data); ASSERT( (void *)mi == data ); } diff -r 0d15dad33f0b -r 7a2ab0087788 freeDiameterd/main.c --- a/freeDiameterd/main.c Mon Jun 29 09:37:35 2015 +0200 +++ b/freeDiameterd/main.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -171,7 +171,7 @@ /* Loop on arguments */ while (1) { - c = getopt_long (argc, argv, "hVc:dql:M:", long_options, &option_index); + c = getopt_long (argc, argv, "hVc:dql:f:F:g:", long_options, &option_index); if (c == -1) break; /* Exit from the loop. */ @@ -207,6 +207,7 @@ case 'f': /* Full debug for the function with this name. */ #ifdef DEBUG fd_debug_one_function = optarg; + fd_g_debug_lvl = FD_LOG_DEBUG; #else /* DEBUG */ fprintf(stderr, "Error: must compile with DEBUG support to use --dbg_func feature!\n"); return EINVAL; @@ -216,6 +217,7 @@ case 'F': /* Full debug for the file with this name. */ #ifdef DEBUG fd_debug_one_file = basename(optarg); + fd_g_debug_lvl = FD_LOG_DEBUG; #else /* DEBUG */ fprintf(stderr, "Error: must compile with DEBUG support to use --dbg_file feature!\n"); return EINVAL; diff -r 0d15dad33f0b -r 7a2ab0087788 include/freeDiameter/CMakeLists.txt --- a/include/freeDiameter/CMakeLists.txt Mon Jun 29 09:37:35 2015 +0200 +++ b/include/freeDiameter/CMakeLists.txt Sun Sep 18 20:36:51 2016 +0800 @@ -15,8 +15,8 @@ # Find TODO items in the code easily ? OPTION(ERRORS_ON_TODO "(development) Generate compilation errors on TODO items ?" OFF) -# In DEBUG mode, each log contains pid, calling function and file for easy debug. Set to ON to strip this information. -OPTION(DEBUG_WITHOUT_META "Strip calling location in logs?" OFF) +# In DEBUG mode, each log can contain pid, calling function and file for easy debug. Set to ON to display this information. +OPTION(DEBUG_WITH_META "Show calling location in logs?" OFF) # Create the absolute path for searching extensions SET(DEFAULT_EXTENSIONS_PATH ${CMAKE_INSTALL_PREFIX}/${INSTALL_EXTENSIONS_SUFFIX}) @@ -36,7 +36,7 @@ # compliancy of their implementation with the Diameter RFC... 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) -MARK_AS_ADVANCED(DISABLE_SCTP DEBUG_SCTP SCTP_USE_MAPPED_ADDRESSES ERRORS_ON_TODO DEBUG_WITHOUT_META DIAMID_IDNA_IGNORE DIAMID_IDNA_REJECT DISABLE_PEER_EXPIRY WORKAROUND_ACCEPT_INVALID_VSAI) +MARK_AS_ADVANCED(DISABLE_SCTP DEBUG_SCTP SCTP_USE_MAPPED_ADDRESSES ERRORS_ON_TODO DEBUG_WITH_META DIAMID_IDNA_IGNORE DIAMID_IDNA_REJECT DISABLE_PEER_EXPIRY WORKAROUND_ACCEPT_INVALID_VSAI) ######################## ### System checks part diff -r 0d15dad33f0b -r 7a2ab0087788 include/freeDiameter/freeDiameter-host.h.in --- a/include/freeDiameter/freeDiameter-host.h.in Mon Jun 29 09:37:35 2015 +0200 +++ b/include/freeDiameter/freeDiameter-host.h.in Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -53,7 +53,7 @@ #cmakedefine DISABLE_SCTP #cmakedefine DEBUG_SCTP -#cmakedefine DEBUG_WITHOUT_META +#cmakedefine DEBUG_WITH_META #cmakedefine SCTP_USE_MAPPED_ADDRESSES #cmakedefine SCTP_CONNECTX_4_ARGS #cmakedefine SKIP_DLCLOSE diff -r 0d15dad33f0b -r 7a2ab0087788 include/freeDiameter/libfdcore.h --- a/include/freeDiameter/libfdcore.h Mon Jun 29 09:37:35 2015 +0200 +++ b/include/freeDiameter/libfdcore.h Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2016, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -127,7 +127,7 @@ unsigned int cnf_timer_tw; /* The value in seconds of the default Tw timer */ uint16_t cnf_port; /* the local port for legacy Diameter (default: 3868) in host byte order */ - uint16_t cnf_port_tls; /* the local port for Diameter/TLS (default: 5658) in host byte order */ + uint16_t cnf_port_tls; /* the local port for Diameter/TLS (default: 5868) in host byte order */ uint16_t cnf_port_3436; /* Open an additional server port to listen to old TLS/SCTP clients (RFC3436, freeDiameter versions < 1.2.0) */ uint16_t cnf_sctp_str; /* default max number of streams for SCTP associations (def: 30) */ struct fd_list cnf_endpoints; /* the local endpoints to bind the server to. list of struct fd_endpoint. default is empty (bind all). After servers are started, this is the actual list of endpoints including port information. */ @@ -964,6 +964,16 @@ - {permsgdata} points to a new empty structure allocated for this request (cf. fd_hook_data_hdl) */ + HOOK_MESSAGE_SENDING, + /* Hook called when a message is about to be sent to a peer. The message pointer cannot be modified here, but the content of the message + could still be changed (for example add or remove some AVP. This is the last chance. + - {msg} points to the message. Some objects may not have been dictionary resolved. If you + try to call fd_msg_parse_dict, it will slow down the operation of the instance. + - {peer} is the one the message is being sent to. + - {other} is NULL. + - {permsgdata} points to existing structure if any, or a new structure otherwise. + */ + HOOK_MESSAGE_SENT, /* Hook called when a message has been sent to a peer. The message might be freed as soon as the hook function returns, so it is not safe to store the pointer for asynchronous processing. @@ -992,7 +1002,7 @@ - {other} is a char * pointer to the error message (human-readable) if {msg} is not NULL, a pointer to struct fd_cnx_rcvdata containing the received buffer otherwise. - {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered). */ - + HOOK_MESSAGE_ROUTING_ERROR, /* Hook called when a message being processed by the routing thread meets an error such as no remaining available peer for sending, based on routing callbacks decisions (maybe after retries). - {msg} points to the message. Again, the objects may not have been dictionary resolved. If you @@ -1048,7 +1058,16 @@ - {permsgdata} is always NULL for this hook. */ -#define HOOK_LAST HOOK_PEER_CONNECT_SUCCESS + HOOK_MESSAGE_PARSING_ERROR2, + /* Hook called after an error message has been generated due to a dictionary parsing error. + - {msg} points to the error message that has been generated. + - {peer} is NULL. You can still retrieve the source from the message itself. + - {other} is NULL + - {permsgdata} points to existing structure associated with this message (or new structure if no previous hook was registered). + Use this hook if you need to populate more data in the error being returned, from the error message. + (e.g. some AVP need to be added to replies even if error case. + */ +#define HOOK_LAST HOOK_MESSAGE_PARSING_ERROR2 }; diff -r 0d15dad33f0b -r 7a2ab0087788 include/freeDiameter/libfdproto.h --- a/include/freeDiameter/libfdproto.h Mon Jun 29 09:37:35 2015 +0200 +++ b/include/freeDiameter/libfdproto.h Sun Sep 18 20:36:51 2016 +0800 @@ -109,7 +109,7 @@ /*============================================================*/ #define DIAMETER_PORT 3868 -#define DIAMETER_SECURE_PORT 5658 +#define DIAMETER_SECURE_PORT 5868 /*============================================================*/ @@ -289,7 +289,7 @@ #define __PRETTY_FUNCTION__ __func__ #endif /* __PRETTY_FUNCTION__ */ -/* A version of __FILE__ without the full path */ +/* A version of __FILE__ without the full path. This is specific to each C file being compiled */ static char * file_bname = NULL; static char * file_bname_init(char * full) { file_bname = basename(full); return file_bname; } #define __STRIPPED_FILE__ (file_bname ?: file_bname_init((char *)__FILE__)) @@ -297,13 +297,13 @@ /* In DEBUG mode, we add meta-information along each trace. This makes multi-threading problems easier to debug. */ -#if (defined(DEBUG) && !defined(DEBUG_WITHOUT_META)) +#if (defined(DEBUG) && defined(DEBUG_WITH_META)) # define STD_TRACE_FMT_STRING "pid:%s in %s@%s:%d: " # define STD_TRACE_FMT_ARGS , ((char *)pthread_getspecific(fd_log_thname) ?: "unnamed"), __PRETTY_FUNCTION__, __STRIPPED_FILE__, __LINE__ -#else /* DEBUG && !DEBUG_WITHOUT_META */ +#else /* DEBUG && DEBUG_WITH_META */ # define STD_TRACE_FMT_STRING "" # define STD_TRACE_FMT_ARGS -#endif /* DEBUG && !DEBUG_WITHOUT_META */ +#endif /* DEBUG && DEBUG_WITH_META */ /************************* The general debug macro @@ -1258,6 +1258,28 @@ */ typedef int (*dict_avpdata_encode) (void * data, union avp_value * val); +/* + * CALLBACK: dict_avpdata_check + * + * PARAMETERS: + * val : Pointer to the AVP value that was received and needs to be sanity checked. + * data : a parameter stored in the type structure (to enable more generic check functions) + * error_msg: upon erroneous value, a string describing the error can be returned here (it will be strcpy by caller). This description will be returned in the error message, if any. + * + * DESCRIPTION: + * This callback can be provided with a derived type in order to improve the operation of the + * fd_msg_parse_dict function. When this callback is present, the value of the AVP that has + * been parsed is passed to this function for finer granularity check. For example for some + * speccific AVP, the format of an OCTETSTRING value can be further checked, or the + * interger value can be verified. + * + * RETURN VALUE: + * 0 : The value is valid. + * !0 : An error occurred, the error code is returned. It is advised to return EINVAL on incorrect val + */ +typedef int (*dict_avpdata_check) (void * data, union avp_value * val, char ** error_msg); + + /* Type to hold data associated to a derived AVP data type */ struct dict_type_data { @@ -1266,6 +1288,8 @@ dict_avpdata_interpret type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */ dict_avpdata_encode type_encode; /* cb to convert formatted data into an AVP value (or NULL) */ DECLARE_FD_DUMP_PROTOTYPE((*type_dump), union avp_value * val); /* cb called by fd_msg_dump_* for this type of data (if != NULL). Returned string must be freed. */ + dict_avpdata_check type_check; + void * type_check_param; }; /* The criteria for searching a type object in the dictionary */ @@ -1293,6 +1317,9 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Time_dump, union avp_value * avp_value); +/* For string AVP, the following type_check function provides simple basic check for specific characters presence, e.g. use "@." for trivial email address check */ +int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg); + /****/ diff -r 0d15dad33f0b -r 7a2ab0087788 libfdcore/hooks.c --- a/libfdcore/hooks.c Mon Jun 29 09:37:35 2015 +0200 +++ b/libfdcore/hooks.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -344,6 +344,11 @@ break; } + case HOOK_MESSAGE_SENDING: { + LOG_A("SENDING message to '%s'", peer ? peer->p_hdr.info.pi_diamid : ""); + break; + } + case HOOK_MESSAGE_SENT: { CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break); LOG_D("SENT to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "", hook_default_buf); @@ -377,6 +382,14 @@ break; } + case HOOK_MESSAGE_PARSING_ERROR2: { + CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break); + + LOG_E("Returning following message after parsing error:"); + LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL); + break; + } + case HOOK_MESSAGE_ROUTING_ERROR: { CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break); LOG_E("Routing error: '%s' for the following message:", (char *)other); diff -r 0d15dad33f0b -r 7a2ab0087788 libfdcore/p_out.c --- a/libfdcore/p_out.c Mon Jun 29 09:37:35 2015 +0200 +++ b/libfdcore/p_out.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -165,6 +165,8 @@ TRACE_ENTRY("%p %p %p", msg, cnx, peer); CHECK_PARAMS( msg && *msg && (cnx || (peer && peer->p_cnxctx))); + + fd_hook_call(HOOK_MESSAGE_SENDING, *msg, peer, NULL, fd_msg_pmdl_get(*msg)); if (update_reqin_cnt && peer) { CHECK_FCT( fd_msg_hdr(*msg, &hdr) ); diff -r 0d15dad33f0b -r 7a2ab0087788 libfdcore/routing_dispatch.c --- a/libfdcore/routing_dispatch.c Mon Jun 29 09:37:35 2015 +0200 +++ b/libfdcore/routing_dispatch.c Sun Sep 18 20:36:51 2016 +0800 @@ -453,6 +453,7 @@ fd_msg_free(msgptr); } else { if (!msgptr) { + fd_hook_call(HOOK_MESSAGE_PARSING_ERROR2, error, NULL, NULL, fd_msg_pmdl_get(error)); /* error now contains the answer message to send back */ CHECK_FCT( fd_fifo_post(fd_g_outgoing, &error) ); } else if (!error) { diff -r 0d15dad33f0b -r 7a2ab0087788 libfdproto/dictionary_functions.c --- a/libfdproto/dictionary_functions.c Mon Jun 29 09:37:35 2015 +0200 +++ b/libfdproto/dictionary_functions.c Sun Sep 18 20:36:51 2016 +0800 @@ -358,3 +358,30 @@ return *buf; } +/* Check that a given AVP value contains all the characters from data in the same order */ +static char error_message[80]; +int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg) +{ + char * inChar = data; + char * inData = (char *)val->os.data; + int i = 0; + CHECK_PARAMS(data); + while (*inChar != '\0') { + while (i < val->os.len) { + if (*inChar == inData[i++]) { + inChar++; + break; + } + } + if (i >= val->os.len) + break; + } + if (*inChar == '\0') + return 0; + + if (error_msg) { + snprintf(error_message, sizeof(error_message), "Could not find '%c' in AVP", *inChar); + *error_msg = error_message; + } + return EBADMSG; +} diff -r 0d15dad33f0b -r 7a2ab0087788 libfdproto/log.c --- a/libfdproto/log.c Mon Jun 29 09:37:35 2015 +0200 +++ b/libfdproto/log.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -95,11 +95,11 @@ /* add timestamp */ printf("%s ", fd_log_time(NULL, buf, sizeof(buf), -#if (defined(DEBUG) && !defined(DEBUG_WITHOUT_META)) +#if (defined(DEBUG) && defined(DEBUG_WITH_META)) 1, 1 -#else /* (defined(DEBUG) && !defined(DEBUG_WITHOUT_META)) */ +#else /* (defined(DEBUG) && defined(DEBUG_WITH_META)) */ 0, 0 -#endif /* (defined(DEBUG) && !defined(DEBUG_WITHOUT_META)) */ +#endif /* (defined(DEBUG) && defined(DEBUG_WITH_META)) */ )); /* Use colors on stdout ? */ if (!use_colors) { diff -r 0d15dad33f0b -r 7a2ab0087788 libfdproto/messages.c --- a/libfdproto/messages.c Mon Jun 29 09:37:35 2015 +0200 +++ b/libfdproto/messages.c Sun Sep 18 20:36:51 2016 +0800 @@ -2,7 +2,7 @@ * Software License Agreement (BSD License) * * Author: Sebastien Decugis * * * -* Copyright (c) 2013, WIDE Project and NICT * +* Copyright (c) 2015, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * @@ -231,7 +231,7 @@ if (model) { struct dict_avp_data dictdata; - CHECK_FCT( fd_dict_getval(model, &dictdata) ); + CHECK_FCT_DO( fd_dict_getval(model, &dictdata), { free(new); return __ret__; } ); new->avp_model = model; new->avp_public.avp_code = dictdata.avp_code; @@ -247,7 +247,7 @@ if (flags & AVPFL_SET_RAWDATA_FROM_AVP) { new->avp_rawlen = (*avp)->avp_public.avp_len - GETAVPHDRSZ( (*avp)->avp_public.avp_flags ); if (new->avp_rawlen) { - CHECK_MALLOC( new->avp_rawdata = malloc(new->avp_rawlen) ); + CHECK_MALLOC_DO( new->avp_rawdata = malloc(new->avp_rawlen), { free(new); return __ret__; } ); memset(new->avp_rawdata, 0x00, new->avp_rawlen); } } @@ -285,18 +285,18 @@ struct dict_cmd_data dictdata; struct dict_object *dictappl; - CHECK_FCT( fd_dict_getdict(model, &dict) ); - CHECK_FCT( fd_dict_getval(model, &dictdata) ); + CHECK_FCT_DO( fd_dict_getdict(model, &dict), { free(new); return __ret__; } ); + CHECK_FCT_DO( fd_dict_getval(model, &dictdata), { free(new); return __ret__; } ); new->msg_model = model; new->msg_public.msg_flags = dictdata.cmd_flag_val; new->msg_public.msg_code = dictdata.cmd_code; /* Initialize application from the parent, if any */ - CHECK_FCT( fd_dict_search( dict, DICT_APPLICATION, APPLICATION_OF_COMMAND, model, &dictappl, 0) ); + CHECK_FCT_DO( fd_dict_search( dict, DICT_APPLICATION, APPLICATION_OF_COMMAND, model, &dictappl, 0), { free(new); return __ret__; } ); if (dictappl != NULL) { struct dict_application_data appdata; - CHECK_FCT( fd_dict_getval(dictappl, &appdata) ); + CHECK_FCT_DO( fd_dict_getval(dictappl, &appdata), { free(new); return __ret__; } ); new->msg_public.msg_appl = appdata.application_id; } } @@ -364,16 +364,16 @@ union avp_value val; if (!sess_id_avp) { - CHECK_FCT( fd_dict_search( dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &sess_id_avp, ENOENT) ); + CHECK_FCT_DO( fd_dict_search( dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &sess_id_avp, ENOENT), { free(ans); return __ret__; } ); } - CHECK_FCT( fd_sess_getsid ( sess, &sid, &sidlen ) ); - CHECK_FCT( fd_msg_avp_new ( sess_id_avp, 0, &avp ) ); + CHECK_FCT_DO( fd_sess_getsid ( sess, &sid, &sidlen ), { free(ans); return __ret__; } ); + CHECK_FCT_DO( fd_msg_avp_new ( sess_id_avp, 0, &avp ), { free(ans); return __ret__; } ); val.os.data = sid; val.os.len = sidlen; - CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); - CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ) ); + CHECK_FCT_DO( fd_msg_avp_setvalue( avp, &val ), { free(avp); free(ans); return __ret__; } ); + CHECK_FCT_DO( fd_msg_avp_add( ans, MSG_BRW_FIRST_CHILD, avp ), { free(avp); free(ans); return __ret__; } ); ans->msg_sess = sess; - CHECK_FCT( fd_sess_ref_msg(sess) ); + CHECK_FCT_DO( fd_sess_ref_msg(sess), { free(ans); return __ret__; } ); } /* Add all Proxy-Info AVPs from the query if any */ @@ -382,7 +382,7 @@ struct fd_pei pei; struct fd_list avpcpylist = FD_LIST_INITIALIZER(avpcpylist); - CHECK_FCT( fd_msg_browse(qry, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + CHECK_FCT_DO( fd_msg_browse(qry, MSG_BRW_FIRST_CHILD, &avp, NULL) , { free(ans); return __ret__; } ); while (avp) { if ( (avp->avp_public.avp_code == AC_PROXY_INFO) && (avp->avp_public.avp_vendor == 0) ) { @@ -393,15 +393,15 @@ size_t offset = 0; /* Create a buffer with the content of the AVP. This is easier than going through the list */ - CHECK_FCT( fd_msg_update_length(avp) ); - CHECK_MALLOC( buf = malloc(avp->avp_public.avp_len) ); - CHECK_FCT( bufferize_avp(buf, avp->avp_public.avp_len, &offset, avp) ); + CHECK_FCT_DO( fd_msg_update_length(avp), { free(ans); return __ret__; } ); + CHECK_MALLOC_DO( buf = malloc(avp->avp_public.avp_len), { free(ans); return __ret__; } ); + CHECK_FCT_DO( bufferize_avp(buf, avp->avp_public.avp_len, &offset, avp), { free(buf); free(ans); return __ret__; } ); /* Now we parse this buffer to create a copy AVP */ - CHECK_FCT( parsebuf_list(buf, avp->avp_public.avp_len, &avpcpylist) ); + CHECK_FCT_DO( parsebuf_list(buf, avp->avp_public.avp_len, &avpcpylist), { free(buf); free(ans); return __ret__; } ); /* Parse dictionary objects now to remove the dependency on the buffer */ - CHECK_FCT( parsedict_do_chain(dict, &avpcpylist, 0, &pei) ); + CHECK_FCT_DO( parsedict_do_chain(dict, &avpcpylist, 0, &pei), { /* leaking the avpcpylist -- this should never happen anyway */ free(buf); free(ans); return __ret__; } ); /* Done for this AVP */ free(buf); @@ -410,7 +410,7 @@ fd_list_move_end(&ans->msg_chain.children, &avpcpylist); } /* move to next AVP in the message, we can have several Proxy-Info instances */ - CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); + CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { free(ans); return __ret__; } ); } } @@ -2036,6 +2036,8 @@ static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mandatory, struct fd_pei *error_info) { struct dict_avp_data dictdata; + struct dict_type_data derivedtypedata; + struct dict_object * avp_derived_type = NULL; uint8_t * source; TRACE_ENTRY("%p %p %d %p", dict, avp, mandatory, error_info); @@ -2218,6 +2220,33 @@ } + /* Is there a derived type check function ? */ + CHECK_FCT ( fd_dict_search ( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &avp_derived_type, 0) ); + if (avp_derived_type) { + CHECK_FCT( fd_dict_getval(avp_derived_type, &derivedtypedata) ); + if (derivedtypedata.type_check != NULL) { + char * err; + int ret = (*derivedtypedata.type_check)( derivedtypedata.type_check_param, &avp->avp_storage, &err ); + + if (ret != 0) { + TRACE_DEBUG(INFO, "The AVP failed to pass the dictionary validation"); + if (error_info) { + error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE"; + error_info->pei_avp = avp; + strncpy(error_message, err, sizeof(error_message)); + error_info->pei_message = error_message; + } else { + char * buf = NULL; + size_t buflen; + CHECK_MALLOC(fd_msg_dump_treeview(&buf, &buflen, NULL, avp, NULL, 0, 0)); + LOG_E("Invalid AVP: %s", buf); + free(buf); + } + return ret; /* should we just return EBADMSG? */ + } + } + } + /* The value is now set, so set the data pointer and return 0 */ avp->avp_public.avp_value = &avp->avp_storage; return 0; diff -r 0d15dad33f0b -r 7a2ab0087788 tests/testmesg.c --- a/tests/testmesg.c Mon Jun 29 09:37:35 2015 +0200 +++ b/tests/testmesg.c Sun Sep 18 20:36:51 2016 +0800 @@ -308,6 +308,15 @@ fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, gavp)); #endif } + + { + struct dict_object * type = NULL; + struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test2", NULL, NULL, NULL, fd_dictfct_CharInOS_check, "@." }; + struct dict_avp_data avp_data = { 73575, 73565, "AVP Test - os2", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING }; + CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) ); + CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) ); + } + #if 0 { fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, vendor)); @@ -754,6 +763,48 @@ CHECK( 0, fd_msg_free ( msg ) ); } + /* Test with a type verifier */ + { + struct fd_pei error_info; + CPYBUF(); + buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */ + + /* Check that we cannot support this message now */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) ); + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + + CPYBUF(); + buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */ + + /* Check error reporting works */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, &error_info ) ); + + #if 1 + fd_log_debug("Error reported: %s\n in AVP: %s", error_info.pei_message, fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, error_info.pei_avp, fd_g_config->cnf_dict, 0, 1)); + #endif + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + + CPYBUF(); + buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */ + buf_cpy[130] = '@'; + buf_cpy[140] = '.'; /* now we comply to the constraints */ + + /* Check that we cannot support this message now */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) ); + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + + + } + { unsigned char * buftmp = NULL; struct msg * error; @@ -1398,7 +1449,6 @@ } } - /* That's all for the tests yet */ PASSTEST(); }