changeset 1307:7a2ab0087788

Remerged proposed branch
author Sebastien Decugis <sdecugis@freediameter.net>
date Sun, 18 Sep 2016 20:36:51 +0800
parents 84a3c9c4b834 (diff) 0d15dad33f0b (current diff)
children 19d01728f26f
files extensions/dbg_msg_dumps/dbg_msg_dumps.c libfdcore/hooks.c
diffstat 20 files changed, 321 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 <sdecugis@freediameter.net>  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
--- 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";
--- 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 */
--- 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 */
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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 );
 	
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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 <stdio.h>
 
+#ifndef __APPLE__ /* they deprecated the semaphore there... */
 #include <semaphore.h>
-#include <stdio.h>
+
+#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 <sched.h>
+#include <dispatch/dispatch.h>
+
+#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;
 };
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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 );
 	}
 	
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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;
--- 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
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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
 };
 
 
--- 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);
+
 
 /****/
 
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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 : "<unknown>");
+				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 : "<unknown>", 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);
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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) );
--- 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) {
--- 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;
+}
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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) {
--- 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 <sdecugis@freediameter.net>							 *
 *													 *
-* 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;
--- 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();
 } 
"Welcome to our mercurial repository"