changeset 1300:3f1e79e1273e

Added new callbacks in the derived types definitions to improve value checks during message parsing. Thanks Ranjith for the suggestion.
author Sebastien Decugis <sdecugis@freediameter.net>
date Sun, 30 Aug 2015 01:11:55 +0800
parents 035f489b845b
children 50fb308e84f7
files include/freeDiameter/libfdproto.h libfdproto/dictionary_functions.c libfdproto/messages.c tests/testmesg.c
diffstat 4 files changed, 134 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/include/freeDiameter/libfdproto.h	Sat Jul 04 23:06:37 2015 +0800
+++ b/include/freeDiameter/libfdproto.h	Sun Aug 30 01:11:55 2015 +0800
@@ -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/libfdproto/dictionary_functions.c	Sat Jul 04 23:06:37 2015 +0800
+++ b/libfdproto/dictionary_functions.c	Sun Aug 30 01:11:55 2015 +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/messages.c	Sat Jul 04 23:06:37 2015 +0800
+++ b/libfdproto/messages.c	Sun Aug 30 01:11:55 2015 +0800
@@ -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	Sat Jul 04 23:06:37 2015 +0800
+++ b/tests/testmesg.c	Sun Aug 30 01:11:55 2015 +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"