changeset 1425:b09f1b4c9fad

fd_msg_add_result: add function Add fd_msg_add_result() as a superset of fd_msg_rescode_set() to allow setting of either Result-Code or Experimental-Result (Grouped), depending upon whether the supplied vendor is 0 or not. Reimplement fd_msg_rescode_set() in terms of fd_msg_add_result().
author Luke Mewburn <luke@mewburn.net>
date Wed, 19 Feb 2020 10:26:29 +1100
parents c8057892e56b
children f260953c2c6c
files include/freeDiameter/libfdcore.h libfdcore/messages.c tests/testmesg.c
diffstat 3 files changed, 208 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/include/freeDiameter/libfdcore.h	Wed Feb 19 10:24:13 2020 +1100
+++ b/include/freeDiameter/libfdcore.h	Wed Feb 19 10:26:29 2020 +1100
@@ -552,6 +552,30 @@
 int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout );
 
 /*
+ * FUNCTION:	fd_msg_add_result
+ *
+ * PARAMETERS:
+ *  msg		: A msg object -- it must be an answer.
+ *  vendor	: Vendor. If 0, add Result-Code else add Experimental-Result.
+ *  restype	: DICT_TYPE containing rescode (ex: from TYPE_BY_NAME "Enumerated(Result-Code)").
+ *  rescode	: The name of the returned error code (ex: "DIAMETER_INVALID_AVP").
+ *  errormsg	: (optional) human-readable error message to put in Error-Message AVP.
+ *  optavp	: (optional) If provided, the content will be put inside a Failed-AVP.
+ *  type_id	: 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host.
+ *
+ * DESCRIPTION:
+ *  This function adds a Result-Code AVP (if vendor is 0) or Experimental-Result AVP (vendor is not 0)
+ *  to a message, and optionally
+ *  - sets the 'E' error flag in the header,
+ *  - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs.
+ *
+ * RETURN VALUE:
+ *  0		: Operation complete.
+ *  !0		: an error occurred.
+ */
+int fd_msg_add_result( struct msg * msg, vendor_id_t vendor, struct dict_object * restype, char * rescode, char * errormsg, struct avp * optavp, int type_id );
+
+/*
  * FUNCTION:	fd_msg_rescode_set
  *
  * PARAMETERS:
@@ -561,14 +585,15 @@
  *  optavp	: (optional) If provided, the content will be put inside a Failed-AVP
  *  type_id	: 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host.
  *
- * DESCRIPTION: 
- *   This function adds a Result-Code AVP to a message, and optionally
+ * DESCRIPTION:
+ *  This function adds a Result-Code AVP to a message, and optionally
  *  - sets the 'E' error flag in the header,
  *  - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs.
+ *  Uses fd_msg_add_result with vendor 0 and restype for Enumerated(Result-Code).
  *
  * RETURN VALUE:
- *  0      	: Operation complete.
- *  !0      	: an error occurred.
+ *  0		: Operation complete.
+ *  !0		: an error occurred.
  */
 int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id );
 
--- a/libfdcore/messages.c	Wed Feb 19 10:24:13 2020 +1100
+++ b/libfdcore/messages.c	Wed Feb 19 10:26:29 2020 +1100
@@ -42,6 +42,9 @@
 static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
 static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
 static struct dict_object * dict_avp_RC  = NULL; /* Result-Code */
+static struct dict_object * dict_avp_ER  = NULL; /* Experimental-Result */
+static struct dict_object * dict_avp_VI  = NULL; /* Vendor-Id */
+static struct dict_object * dict_avp_ERC = NULL; /* Experimental-Result-Code */
 struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
 struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
 struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
@@ -63,6 +66,9 @@
 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message",   	&dict_avp_EM  , ENOENT)  );
 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT)  );
 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP",      	&dict_avp_FAVP, ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result", &dict_avp_ER, ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id",		&dict_avp_VI, ENOENT)  );
+	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result-Code", &dict_avp_ERC, ENOENT)  );
 
 	CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", 	&fd_dict_avp_DC , ENOENT)  );
 
@@ -166,30 +172,26 @@
 }
 
 
-/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
-int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
+/* Add Result-Code or Experimental-Result, and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
+int fd_msg_add_result( struct msg * msg, vendor_id_t vendor, struct dict_object * restype, char * rescode, char * errormsg, struct avp * optavp, int type_id )
 {
 	union avp_value val;
-	struct avp * avp_RC  = NULL;
-	struct avp * avp_EM  = NULL;
-	struct avp * avp_ERH = NULL;
-	struct avp * avp_FAVP= NULL;
 	uint32_t rc_val = 0;
 	int set_e_bit=0;
 	int std_err_msg=0;
 
-	TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
+	TRACE_ENTRY("%p %d %p %s %p %p %d", msg, vendor, restype, rescode, errormsg, optavp, type_id);
 
-	CHECK_PARAMS(  msg && rescode  );
+	CHECK_PARAMS(  msg && restype && rescode  );
 
 	/* Find the enum value corresponding to the rescode string, this will give the class of error */
 	{
 		struct dict_object * enum_obj = NULL;
+
+		/* Search in the restype */
 		struct dict_enumval_request req;
 		memset(&req, 0, sizeof(struct dict_enumval_request));
-
-		/* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
-		CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT  )  );
+		req.type_obj = restype;
 
 		/* Now search for the value given as parameter */
 		req.search.enum_name = rescode;
@@ -207,20 +209,58 @@
 		CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
 	}
 
-	/* Create the Result-Code AVP */
-	CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
+	if (vendor == 0) {
+		/* Vendor 0; create the Result-Code AVP */
+		struct avp * avp_RC  = NULL;
+		CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
+
+		/* Set its value */
+		memset(&val, 0, sizeof(val));
+		val.u32  = rc_val;
+		CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
+	} else {
+		/* Vendor !0; create the Experimental-Result AVP */
+		struct avp * avp_ER  = NULL;
+		CHECK_FCT( fd_msg_avp_new( dict_avp_ER, 0, &avp_ER ) );
+
+		/* Create the Vendor-Id AVP and add to Experimental-Result */
+		{
+			struct avp * avp_VI  = NULL;
+			CHECK_FCT( fd_msg_avp_new( dict_avp_VI, 0, &avp_VI ) );
 
-	/* Set its value */
-	memset(&val, 0, sizeof(val));
-	val.u32  = rc_val;
-	CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
+			/* Set Vendor-Id value to vendor */
+			memset(&val, 0, sizeof(val));
+			val.u32  = vendor;
+			CHECK_FCT( fd_msg_avp_setvalue( avp_VI, &val ) );
+
+			/* Add it to Experimental-Result */
+			CHECK_FCT( fd_msg_avp_add( avp_ER, MSG_BRW_LAST_CHILD, avp_VI ) );
+		}
 
-	/* Add it to the message */
-	CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
+		/* Create the Experimental-Result-Code AVP and add to Experimental-Result */
+		{
+			struct avp * avp_ERC  = NULL;
+			CHECK_FCT( fd_msg_avp_new( dict_avp_ERC, 0, &avp_ERC ) );
+
+			/* Set Experimental-Result-Code value to rc_val */
+			memset(&val, 0, sizeof(val));
+			val.u32  = rc_val;
+			CHECK_FCT( fd_msg_avp_setvalue( avp_ERC, &val ) );
+
+			/* Add it to Experimental-Result */
+			CHECK_FCT( fd_msg_avp_add( avp_ER, MSG_BRW_LAST_CHILD, avp_ERC ) );
+		}
+
+		/* Add it to the message */
+		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ER ) );
+	}
 
 	if (type_id == 2) {
 		/* Add the Error-Reporting-Host AVP */
-
+		struct avp * avp_ERH = NULL;
 		CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
 
 		/* Set its value */
@@ -231,11 +271,11 @@
 
 		/* Add it to the message */
 		CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
-
 	}
 
-	/* Now add the optavp in a FailedAVP if provided */
+	/* Now add the optavp in a Failed-AVP if provided */
 	if (optavp) {
+		struct avp * avp_FAVP= NULL;
 		struct avp * optavp_cpy = NULL;
 		struct avp_hdr *opt_hdr, *optcpy_hdr;
 		struct dict_object * opt_model = NULL;
@@ -309,7 +349,7 @@
 
 	if (std_err_msg || errormsg) {
 		/* Add the Error-Message AVP */
-
+		struct avp * avp_EM  = NULL;
 		CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
 
 		/* Set its value */
@@ -331,6 +371,13 @@
 	return 0;
 }
 
+int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
+{
+	struct dict_object * restype = NULL;
+	CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &restype, ENOENT ) );
+	return fd_msg_add_result(msg, 0, restype, rescode, errormsg, optavp, type_id);
+}
+
 static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
 {
 	struct msg_hdr *hdr;
--- a/tests/testmesg.c	Wed Feb 19 10:24:13 2020 +1100
+++ b/tests/testmesg.c	Wed Feb 19 10:26:29 2020 +1100
@@ -282,6 +282,11 @@
 			
 			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Application test", &application, ENOENT ) );
 			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_COMMAND, &cmd_data , application, &command ) );
+			ADD_RULE(command, 0, "Session-Id",			RULE_FIXED_HEAD, 1, 1, 1);
+			ADD_RULE(command, 0, "Result-Code",			RULE_OPTIONAL,   0, 1, 0);
+			ADD_RULE(command, 0, "Experimental-Result",		RULE_OPTIONAL,   0, 1, 0);
+			ADD_RULE(command, 0, "Origin-Host",			RULE_REQUIRED,   1, 1, 0);
+			ADD_RULE(command, 0, "Origin-Realm",			RULE_REQUIRED,   1, 1, 0);
 		}
 		
 		{
@@ -316,7 +321,19 @@
 			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 ) );
 		}
-		
+
+		{
+			struct dict_object     * type = NULL;
+			struct dict_type_data    type_data = { AVP_TYPE_UNSIGNED32, "Enumerated(73565/Experimental-Result-Code)" };
+			struct dict_avp_data     avp_data = { 73576, 73565, "Experimental-Result-Code", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_UNSIGNED32 };
+			struct dict_enumval_data val1 = { "DIAMETER_TEST_RESULT_1000", { .u32 = 1000 } };
+			struct dict_enumval_data val2 = { "DIAMETER_TEST_RESULT_5000", { .u32 = 5000 } };
+			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 ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val1 , type, NULL ) );
+			CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_ENUMVAL, &val2 , type, NULL ) );
+		}
+
 		#if 0
 		{
 			fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, vendor));
@@ -1477,6 +1494,97 @@
 		}
 	}
 
+	/* Test the fd_msg_add_result function for Result-Code */
+	{
+		struct msg		* msg = NULL;
+		struct dict_object	* avp_model = NULL;
+		struct avp		* rc = NULL;
+		struct avp_hdr		* avpdata = NULL;
+
+		{
+			struct dict_object * cmd_model = NULL;
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Answer", &cmd_model, ENOENT ) );
+
+			/* Create a message */
+			CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+
+			/* Add a session id */
+			CHECK( 0, fd_msg_new_session( msg, (os0_t)"tm2", strlen("tm2") ) );
+
+			/* Find the DICT_TYPE Enumerated(Result-Code) */
+			struct dict_object * restype = NULL;
+			CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_BY_NAME, "Enumerated(Result-Code)", &restype, ENOENT ) );
+
+			/* Now test the behavior of fd_msg_add_result for Result-Code AVP */
+			CHECK( 0, fd_msg_add_result(msg, 0, restype, "DIAMETER_SUCCESS", NULL, NULL, 1) );
+
+			LOG_D("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+		}
+
+		/* Ensure Result-Code is present */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &avp_model, ENOENT ) );
+		CHECK( 0, fd_msg_search_avp( msg, avp_model, &rc ) );
+
+		/* Check the Result-Code AVP value is DIAMETER_SUCCESS */
+		CHECK( 0, fd_msg_avp_hdr ( rc, &avpdata ) );
+		CHECK( ER_DIAMETER_SUCCESS, avpdata->avp_value->u32 );
+
+		/* Ensure Experimental-Result is missing */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result", &avp_model, ENOENT ) );
+		CHECK( ENOENT, fd_msg_search_avp( msg, avp_model, NULL ) );
+
+		/* Free msg */
+		CHECK( 0, fd_msg_free( msg ) );
+	}
+
+	/* Test the fd_msg_add_result function for Experimental-Result */
+	{
+		struct msg		* msg = NULL;
+		struct dict_object	* avp_model = NULL;
+		struct avp		* er = NULL;
+		struct avp		* erc = NULL;
+		struct avp_hdr		* avpdata = NULL;
+
+		{
+			struct dict_object * cmd_model = NULL;
+			CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Test-Command-Answer", &cmd_model, ENOENT ) );
+
+			/* Create a message */
+			CHECK( 0, fd_msg_new ( cmd_model, 0, &msg ) );
+
+			/* Add a session id */
+			CHECK( 0, fd_msg_new_session( msg, (os0_t)"tm2", strlen("tm2") ) );
+
+			/* Find the DICT_TYPE Enumerated(73565/Experimental-Result-Code) */
+			struct dict_object * restype = NULL;
+			CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_BY_NAME, "Enumerated(73565/Experimental-Result-Code)", &restype, ENOENT ) );
+
+			/* Now test the behavior of fd_msg_add_result for Experimental-Result AVP */
+			CHECK( 0, fd_msg_add_result(msg, 73565, restype, "DIAMETER_TEST_RESULT_5000", NULL, NULL, 1) );
+
+			LOG_D("%s", fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, msg, fd_g_config->cnf_dict, 0, 1));
+		}
+
+		/* Ensure Result-Code is missing */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &avp_model, ENOENT ) );
+		CHECK( ENOENT, fd_msg_search_avp( msg, avp_model, NULL ) );
+
+		/* Ensure Experimental-Result is present */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result", &avp_model, ENOENT ) );
+		CHECK( 0, fd_msg_search_avp( msg, avp_model, &er ) );
+
+		/* Ensure Experimental-Result-Code is present */
+		CHECK( 0, fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Experimental-Result-Code", &avp_model, ENOENT ) );
+		CHECK( 0, fd_msg_search_avp( er, avp_model, &erc ) );
+
+		/* Check the Experimental-Result-Code AVP value is 5000 */
+		CHECK( 0, fd_msg_avp_hdr ( erc, &avpdata ) );
+		CHECK( 5000, avpdata->avp_value->u32 );
+
+		/* Free msg */
+		CHECK( 0, fd_msg_free( msg ) );
+	}
+
 	/* Check IPv4 -> IPv6 and IPv6->IPv4 mapping */
 	{
 		struct in_addr i4;
"Welcome to our mercurial repository"