changeset 111:f5d811b6e2e2

Added callbacks to interpret derived types AVP values in more convenient formats
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 30 Jul 2008 14:58:28 +0900
parents 2fb02c5fdf16
children 51308bdd3c04
files include/waaad/dictionary-api.h include/waaad/message-api.h waaad/dict-base.c waaad/message.c waaad/tests/testmesg.c
diffstat 5 files changed, 550 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/include/waaad/dictionary-api.h	Tue Jul 29 16:11:07 2008 +0900
+++ b/include/waaad/dictionary-api.h	Wed Jul 30 14:58:28 2008 +0900
@@ -362,6 +362,20 @@
  ***************************************************************************
  */
 
+/* Type to store any AVP value */ 
+typedef union {
+	struct {
+		unsigned char *	data;	/* bytes buffer */
+		size_t   	len;	/* length of the data buffer */
+	}           os;		/* Storage for an octet string, data is alloc'd and must be freed */
+	int32_t     i32;	/* integer 32 */
+	int64_t     i64;	/* integer 64 */
+	uint32_t    u32;	/* unsigned 32 */
+	uint64_t    u64;	/* unsigned 64 */
+	float       f32;	/* float 32 */
+	double 	    f64;	/* float 64 */
+} avp_value_t;
+
 /* These are the basic AVP types defined in RFC3588bis */
 typedef enum {
 	AVP_TYPE_GROUPED,
@@ -375,10 +389,52 @@
 #define AVP_TYPE_MAX AVP_TYPE_FLOAT64
 } dict_base_type_t;
 
+/* Callbacks that can be associated with a derived type to further interpret the values. */
+/*
+ * CALLBACK:	type_data_interpret
+ *
+ * PARAMETERS:
+ *   avp_value   : Pointer to the AVP value that must be interpreted.
+ *   interpreted : The result of interpretation is stored here. The format and meaning depends on each type.
+ *
+ * DESCRIPTION: 
+ *   This callback can be provided with a derived type in order to facilitate the interpretation of formated data.
+ *  For example, when an AVP of type "Address" is received, it can be used to convert the octetstring into a struct sockaddr.
+ *  This callback is usually not called directly, but through the message's API msg_avp_value_interpret function.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0 		: An error occurred, the error code is returned.
+ */
+typedef int (*type_data_interpret) (avp_value_t * avp_value, void * interpreted);
+/*
+ * CALLBACK:	type_data_encode
+ *
+ * PARAMETERS:
+ *   data	: The formated data that must be stored in the AVP value.
+ *   avp_value	: Pointer to the AVP value storage area where the data must be stored.
+ *
+ * DESCRIPTION: 
+ *   This callback can be provided with a derived type in order to facilitate the encoding of formated data.
+ *  For example, it can be used to convert a struct sockaddr in an AVP value of type Address.
+ *  This callback is usually not called directly, but through the message's API msg_avp_value_encode function.
+ *  If the callback is defined for an OctetString based type, the created string must be malloc'd. free will be called 
+ *  automatically later.
+ *
+ * RETURN VALUE:
+ *  0      	: Operation complete.
+ *  !0 		: An error occurred, the error code is returned.
+ */
+typedef int (*type_data_encode) (void * data, avp_value_t * avp_value);
+
+
+
 /* Type to hold data associated to a derived AVP data type */
 typedef struct {
 	dict_base_type_t	 type_base;	/* How the data of such AVP must be interpreted */
 	char 			*type_name;	/* The name of this application */
+	type_data_interpret	 type_interpret;/* Convert the AVP value in more comprehensive format */
+	type_data_encode	 type_encode;	/* Convert formatted data into an AVP value */
 } dict_type_data_t;
 
 /* The criteria for searching a type object in the dictionary */
@@ -428,20 +484,6 @@
  ***************************************************************************
  */
 
-/* Type to store any AVP value */ 
-typedef union {
-	struct {
-		unsigned char *	data;	/* bytes buffer */
-		size_t   	len;	/* length of the data buffer */
-	}           os;		/* Storage for an octet string, data is alloc'd and must be freed */
-	int32_t     i32;	/* integer 32 */
-	int64_t     i64;	/* integer 64 */
-	uint32_t    u32;	/* unsigned 32 */
-	uint64_t    u64;	/* unsigned 64 */
-	float       f32;	/* float 32 */
-	double 	    f64;	/* float 64 */
-} avp_value_t;
-
 /* Type to hold data of named constants for AVP values */
 typedef struct {
 	char 		*enum_name;	/* The name of this constant */
--- a/include/waaad/message-api.h	Tue Jul 29 16:11:07 2008 +0900
+++ b/include/waaad/message-api.h	Wed Jul 30 14:58:28 2008 +0900
@@ -250,6 +250,43 @@
 int msg_avp_setvalue ( msg_avp_t *avp, avp_value_t *value );
 
 /*
+ * FUNCTION:	msg_avp_value_encode
+ *
+ * PARAMETERS:
+ *  avp 	: Pointer to a valid avp object with a NULL avp_data. The model must be known.
+ *  data 	: Pointer to the data that must be encoded as AVP value and stored in the AVP.
+ *		 This is only valid for AVPs of a derived type and which type_data_encode callback is set.
+ *
+ * DESCRIPTION: 
+ *   Initialize the avp_data field of an AVP object from formatted data, using the AVP's type encode function.
+ *
+ * RETURN VALUE:
+ *  0      	: The avp_data has been set.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOTSUP 	: There is no appropriate callback registered with this AVP's type.
+ */
+int msg_avp_value_encode ( void *data, msg_avp_t *avp );
+
+/*
+ * FUNCTION:	msg_avp_value_interpret
+ *
+ * PARAMETERS:
+ *  avp 	: Pointer to a valid avp object with a non-NULL avp_data value.
+ *  data 	: Upon success, formatted interpretation of the AVP value is stored here.
+ *
+ * DESCRIPTION: 
+ *   Interpret the content of an AVP of Derived type and store the result in data pointer. The real type
+ * of the data pointer is dependent on the AVP type. This function calls the type_data_interpret callback 
+ * of the type.
+ *
+ * RETURN VALUE:
+ *  0      	: The avp_data has been set.
+ *  EINVAL 	: A parameter is invalid.
+ *  ENOTSUP 	: There is no appropriate callback registered with this AVP's type.
+ */
+int msg_avp_value_interpret ( msg_avp_t *avp, void *data );
+
+/*
  * FUNCTION:	msg_avp_add
  *
  * PARAMETERS:
@@ -360,18 +397,20 @@
 	int	version;	/* The version of this API/ABI, must be WAAAD_API_MSG_VER */
 	
 	/* the remaining is message-specific */
-	int (*msg_browse)         ( void * reference, msg_dir_t dir, void ** found, int * depth );
-	int (*msg_model)          ( void * reference, dict_object_t ** model );
-	int (*msg_data)           ( msg_t *msg, msg_data_t **pdata );
-	int (*msg_avp_data)       ( msg_avp_t *avp, msg_avp_data_t **pdata );
-	int (*msg_new)            ( dict_object_t * model, int flags, msg_t ** msg );
-	int (*msg_avp_new)        ( dict_object_t * model, int flags, msg_avp_t ** avp );
-	int (*msg_avp_setvalue)   ( msg_avp_t *avp, avp_value_t *value );
-	int (*msg_avp_add)        ( void * reference, msg_dir_t dir, msg_avp_t *avp);
-	int (*msg_free)           ( void * object, int subtree );
-	int (*msg_update_length)  ( void * object );
-	int (*msg_avp_getrawdata) ( msg_avp_t *avp, unsigned char **data );
-	int (*msg_parse_rules)    ( void * object, dict_object_t ** rule);
+	int (*msg_browse)              ( void * reference, msg_dir_t dir, void ** found, int * depth );
+	int (*msg_model)               ( void * reference, dict_object_t ** model );
+	int (*msg_data)                ( msg_t *msg, msg_data_t **pdata );
+	int (*msg_avp_data)            ( msg_avp_t *avp, msg_avp_data_t **pdata );
+	int (*msg_new)                 ( dict_object_t * model, int flags, msg_t ** msg );
+	int (*msg_avp_new)             ( dict_object_t * model, int flags, msg_avp_t ** avp );
+	int (*msg_avp_setvalue)        ( msg_avp_t *avp, avp_value_t *value );
+	int (*msg_avp_value_encode)    ( void *data, msg_avp_t *avp );
+	int (*msg_avp_value_interpret) ( msg_avp_t *avp, void *data );
+	int (*msg_avp_add)             ( void * reference, msg_dir_t dir, msg_avp_t *avp);
+	int (*msg_free)                ( void * object, int subtree );
+	int (*msg_update_length)       ( void * object );
+	int (*msg_avp_getrawdata)      ( msg_avp_t *avp, unsigned char **data );
+	int (*msg_parse_rules)         ( void * object, dict_object_t ** rule);
 } api_msg_t;
 
 #ifdef IN_EXTENSION
@@ -384,18 +423,20 @@
 #endif /* DECLARE_API_POINTERS */
 
 /* These defines allow to call functions from extension in the same way as in waaad */
-#define msg_browse	   g_api_msg->msg_browse
-#define msg_model	   g_api_msg->msg_model
-#define msg_data	   g_api_msg->msg_data
-#define msg_avp_data	   g_api_msg->msg_avp_data
-#define msg_new		   g_api_msg->msg_new
-#define msg_avp_new	   g_api_msg->msg_avp_new
-#define msg_avp_setvalue   g_api_msg->msg_avp_setvalue
-#define msg_avp_add	   g_api_msg->msg_avp_add
-#define msg_free	   g_api_msg->msg_free
-#define msg_update_length  g_api_msg->msg_update_length
-#define msg_avp_getrawdata g_api_msg->msg_avp_getrawdata
-#define msg_parse_rules	   g_api_msg->msg_parse_rules
+#define msg_browse	          g_api_msg->msg_browse
+#define msg_model	          g_api_msg->msg_model
+#define msg_data	          g_api_msg->msg_data
+#define msg_avp_data	          g_api_msg->msg_avp_data
+#define msg_new		          g_api_msg->msg_new
+#define msg_avp_new	          g_api_msg->msg_avp_new
+#define msg_avp_setvalue          g_api_msg->msg_avp_setvalue
+#define msg_avp_value_encode      g_api_msg->msg_avp_value_encode
+#define msg_avp_value_interpret   g_api_msg->msg_avp_value_interpret
+#define msg_avp_add	          g_api_msg->msg_avp_add
+#define msg_free	          g_api_msg->msg_free
+#define msg_update_length         g_api_msg->msg_update_length
+#define msg_avp_getrawdata        g_api_msg->msg_avp_getrawdata
+#define msg_parse_rules	          g_api_msg->msg_parse_rules
 
 
 #else /* IN_EXTENSION */
@@ -406,22 +447,24 @@
 #  error "You must update API_INIT_MSG also"
 # endif
 
-#define API_INIT_MSG( api_msg ) 				\
-{								\
-	(api_msg).length             = sizeof(api_msg_t);	\
-	(api_msg).version            = WAAAD_API_MSG_VER;	\
-	(api_msg).msg_browse         = msg_browse;		\
-	(api_msg).msg_model          = msg_model;		\
-	(api_msg).msg_data           = msg_data;		\
-	(api_msg).msg_avp_data       = msg_avp_data;		\
-	(api_msg).msg_new            = msg_new;			\
-	(api_msg).msg_avp_new        = msg_avp_new;		\
-	(api_msg).msg_avp_setvalue   = msg_avp_setvalue;	\
-	(api_msg).msg_avp_add        = msg_avp_add;		\
-	(api_msg).msg_free           = msg_free;		\
-	(api_msg).msg_update_length  = msg_update_length;	\
-	(api_msg).msg_avp_getrawdata = msg_avp_getrawdata;	\
-	(api_msg).msg_parse_rules    = msg_parse_rules;		\
+#define API_INIT_MSG( api_msg ) 					\
+{									\
+	(api_msg).length                    = sizeof(api_msg_t);	\
+	(api_msg).version                   = WAAAD_API_MSG_VER;	\
+	(api_msg).msg_browse                = msg_browse;		\
+	(api_msg).msg_model                 = msg_model;		\
+	(api_msg).msg_data                  = msg_data;			\
+	(api_msg).msg_avp_data              = msg_avp_data;		\
+	(api_msg).msg_new                   = msg_new;			\
+	(api_msg).msg_avp_new               = msg_avp_new;		\
+	(api_msg).msg_avp_setvalue          = msg_avp_setvalue;		\
+	(api_msg).msg_avp_value_encode      = msg_avp_value_encode;	\
+	(api_msg).msg_avp_value_interpret   = msg_avp_value_interpret;	\
+	(api_msg).msg_avp_add               = msg_avp_add;		\
+	(api_msg).msg_free                  = msg_free;			\
+	(api_msg).msg_update_length         = msg_update_length;	\
+	(api_msg).msg_avp_getrawdata        = msg_avp_getrawdata;	\
+	(api_msg).msg_parse_rules           = msg_parse_rules;		\
 }							
 
 #endif /* IN_EXTENSION */
--- a/waaad/dict-base.c	Tue Jul 29 16:11:07 2008 +0900
+++ b/waaad/dict-base.c	Wed Jul 30 14:58:28 2008 +0900
@@ -42,6 +42,138 @@
 
 #include "waaad-internal.h"
 #include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+/* The functions to encode and interpret the derived types defined in the base protocol */
+
+/* Address AVP <-> struct sockaddr_storage */
+static int Address_encode(void * data, avp_value_t * avp_value)
+{
+	struct sockaddr_storage * ss = (struct sockaddr_storage *) data;
+	uint16_t AddressType = 0;
+	size_t	size = 0;
+	unsigned char * buf = NULL;
+	
+	TRACE_ENTRY("%p %p", data, avp_value);
+	
+	if (!data || !avp_value) {
+		TRACE_DEBUG(INFO, "Received invalid parameter");
+		return EINVAL;
+	}
+	
+	switch (ss->ss_family) {
+		case AF_INET:
+			{
+				/* We are encoding a IP address */
+				struct sockaddr_in * sin = (struct sockaddr_in *)ss;
+				
+				AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */
+				size = 6;	/* 2 for AddressType + 4 for data */
+				
+				buf = malloc(size);
+				if (!buf) {
+					log_error("Failed to allocated memory: %s\n", strerror(errno));
+					TRACE_DEBUG(INFO, "malloc failed");
+					return ENOMEM;
+				}
+				
+				/* Do we have to change order? */
+				/* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */
+				memcpy(buf + 2, &sin->sin_addr.s_addr, 4);
+			}
+			break;
+				
+		case AF_INET6:
+			{
+				/* We are encoding a IP address */
+				struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)ss;
+				
+				AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */
+				size = 18;	/* 2 for AddressType + 16 for data */
+				
+				buf = malloc(size);
+				if (!buf) {
+					log_error("Failed to allocated memory: %s\n", strerror(errno));
+					TRACE_DEBUG(INFO, "malloc failed");
+					return ENOMEM;
+				}
+				
+				/* The order is already good here */
+				memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16);
+			}
+			break;
+				
+		default:
+			TRACE_DEBUG(INFO, "Unsupported address familly, EINVAL");
+			return EINVAL;
+	}
+	
+	*(uint16_t *)buf = htons(AddressType);
+
+	avp_value->os.len = size;
+	avp_value->os.data = buf;
+	return 0;
+}
+
+static int Address_interpret(avp_value_t * avp_value, void * interpreted)
+{
+	uint16_t AddressType = 0;
+	size_t	size = 0;
+	unsigned char * buf;
+	
+	TRACE_ENTRY("%p %p", avp_value, interpreted);
+	
+	if (!avp_value || !interpreted || (avp_value->os.len < 2)) {
+		TRACE_DEBUG(INFO, "Received invalid parameter");
+		return EINVAL;
+	}
+	
+	AddressType = ntohs(*(uint16_t *)avp_value->os.data);
+	buf = &avp_value->os.data[2];
+	
+	switch (AddressType) {
+		case 1 /* IP */:
+			{
+				struct sockaddr_in * sin = (struct sockaddr_in *)interpreted;
+				
+				if (avp_value->os.len != 6) {
+					TRACE_DEBUG(INFO, "Received invalid buffer size (%d instead of %d)", avp_value->os.len, 6);
+					return EINVAL;
+				}
+				
+				sin->sin_family = AF_INET;
+				/* Do we have to switch order here? */
+				/* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */
+				memcpy(&sin->sin_addr.s_addr, buf, 4);
+			}
+			break;
+				
+		case 2 /* IP6 */:
+			{
+				struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)interpreted;
+				
+				if (avp_value->os.len != 18) {
+					TRACE_DEBUG(INFO, "Received invalid buffer size (%d instead of %d)", avp_value->os.len, 18);
+					return EINVAL;
+				}
+				
+				sin6->sin6_family = AF_INET6;
+				/* Stored in network byte order in the sin6 */
+				memcpy(&sin6->sin6_addr.s6_addr, buf, 16);
+			}
+			break;
+				
+		default:
+			TRACE_DEBUG(INFO, "Unsupported AddressType, EINVAL");
+			return EINVAL;
+	}
+	
+	return 0;
+}
+
+
 
 #define CHECK_dict_new( _type, _data, _parent, _ref ) {				\
 	int __ret = dict_new( (_type), (_data), (_parent), (_ref));		\
@@ -125,7 +257,7 @@
 				defined in [IANAADFAM].  The AddressType is used to discriminate
 				the content and format of the remaining octets.
 			*/
-			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"Address"		};
+			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"Address"		, Address_interpret	, Address_encode	};
 			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
 		}
 		
@@ -144,7 +276,7 @@
 				SNTP [RFC4330] describes a procedure to extend the time to 2104.
 				This procedure MUST be supported by all DIAMETER nodes.
 			*/
-			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"Time"			};
+			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"Time"			, NULL			, NULL			};
 			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
 		}
 		
@@ -182,7 +314,7 @@
 				Note that the AVP Length field of an UTF8String is measured in
 				octets, not characters.
 			*/
-			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"UTF8String"		};
+			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"UTF8String"		, NULL			, NULL			};
 			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
 		}
 		
@@ -211,7 +343,7 @@
 				interactions between the Diameter protocol and Internationalized
 				Domain Name (IDNs).
 			*/
-			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"DiameterIdentity"	};
+			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"DiameterIdentity"	, NULL			, NULL			};
 			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
 		}
 		
@@ -266,7 +398,7 @@
 				aaa://host.example.com:6666;transport=tcp;protocol=diameter
 				aaa://host.example.com:1813;transport=udp;protocol=radius
 			*/
-			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"DiameterURI"		};
+			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"DiameterURI"		, NULL			, NULL			};
 			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
 		}
 		
@@ -328,7 +460,7 @@
 				supplied rules, for example to protect the access device owner's
 				infrastructure.
 			*/
-			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"IPFilterRule"	};
+			dict_type_data_t data = { AVP_TYPE_OCTETSTRING,	"IPFilterRule"		, NULL			, NULL			};
 			CHECK_dict_new( DICT_TYPE, &data , NULL, NULL);
 		}
 	}
@@ -483,7 +615,7 @@
 				with above result code SHOULD NOT attempt reconnection.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Disconnect-Cause)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Disconnect-Cause)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_0 = { "REBOOTING", 			{ .i32 = 0 }};
 			dict_type_enum_data_t 	t_1 = { "BUSY", 			{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "DO_NOT_WANT_TO_TALK_TO_YOU", 	{ .i32 = 2 }};
@@ -763,7 +895,7 @@
 			 */
 			
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Inband-Security-Id)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Inband-Security-Id)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_0 = { "NO_INBAND_SECURITY", 		{ .u32 = 0 }};
 			dict_type_enum_data_t 	t_1 = { "TLS", 			{ .u32 = 1 }};
 			dict_avp_data_t 	data = { 
@@ -969,7 +1101,7 @@
 				6.  ALL_HOST
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Redirect-Host-Usage)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Redirect-Host-Usage)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_0 = { "DONT_CACHE", 			{ .i32 = 0 }};
 			dict_type_enum_data_t 	t_1 = { "ALL_SESSION", 			{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "ALL_REALM", 			{ .i32 = 2 }};
@@ -1057,7 +1189,7 @@
 			 * This is the reason for the "*" in the type name
 			 */
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Result-Code)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Result-Code)"	, NULL, NULL};
 			dict_avp_data_t 	data = { 
 					268, 					/* Code */
 					0, 					/* Vendor */
@@ -1558,7 +1690,7 @@
 			 * This is the reason for the "*" in the type name. Vendors will have to define their values.
 			 */
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Experimental-Result-Code)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Experimental-Result-Code)"	, NULL, NULL};
 			dict_avp_data_t data = { 
 					298, 					/* Code */
 					0, 					/* Vendor */
@@ -1605,7 +1737,7 @@
 				offered.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Request-Type)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Request-Type)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_1 = { "AUTHENTICATE_ONLY", 		{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "AUTHORIZE_ONLY", 		{ .i32 = 2 }};
 			dict_type_enum_data_t 	t_3 = { "AUTHORIZE_AUTHENTICATE", 	{ .i32 = 3 }};
@@ -1744,7 +1876,7 @@
 				Authorization-Lifetime.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Session-State)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Auth-Session-State)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_0 = { "STATE_MAINTAINED", 		{ .i32 = 0 }};
 			dict_type_enum_data_t 	t_1 = { "NO_STATE_MAINTAINED", 		{ .i32 = 1 }};
 			dict_avp_data_t 	data = { 
@@ -1787,7 +1919,7 @@
 				expiration of the Authorization-Lifetime.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Re-Auth-Request-Type)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Re-Auth-Request-Type)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_0 = { "AUTHORIZE_ONLY", 		{ .i32 = 0 }};
 			dict_type_enum_data_t 	t_1 = { "AUTHORIZE_AUTHENTICATE",	{ .i32 = 1 }};
 			dict_avp_data_t 	data = { 
@@ -1914,7 +2046,7 @@
 				The user's session has timed out, and service has been terminated.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Termination-Cause)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Termination-Cause)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_1 = { "DIAMETER_LOGOUT",			{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "DIAMETER_SERVICE_NOT_PROVIDED", 	{ .i32 = 2 }};
 			dict_type_enum_data_t 	t_3 = { "DIAMETER_BAD_ANSWER",			{ .i32 = 3 }};
@@ -2026,7 +2158,7 @@
 			 */
 			
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Session-Binding)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_UNSIGNED32,	"Enumerated*(Session-Binding)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_1 = { "RE_AUTH", 		{ .u32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "STR", 			{ .u32 = 2 }};
 			dict_type_enum_data_t 	t_4 = { "ACCOUNTING", 		{ .u32 = 4 }};
@@ -2088,7 +2220,7 @@
 				session.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Session-Server-Failover)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Session-Server-Failover)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_0 = { "REFUSE_SERVICE", 		{ .i32 = 0 }};
 			dict_type_enum_data_t 	t_1 = { "TRY_AGAIN",			{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "ALLOW_SERVICE", 		{ .i32 = 2 }};
@@ -2220,7 +2352,7 @@
 				the existing session.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Record-Type)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Record-Type)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_1 = { "EVENT_RECORD",			{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "START_RECORD", 		{ .i32 = 2 }};
 			dict_type_enum_data_t 	t_3 = { "INTERIM_RECORD",		{ .i32 = 3 }};
@@ -2414,7 +2546,7 @@
 				stored.
 			*/
 			dict_object_t 	* 	type;
-			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Realtime-Required)"	};
+			dict_type_data_t 	tdata = { AVP_TYPE_INTEGER32,	"Enumerated(Accounting-Realtime-Required)"	, NULL, NULL};
 			dict_type_enum_data_t 	t_1 = { "DELIVER_AND_GRANT",		{ .i32 = 1 }};
 			dict_type_enum_data_t 	t_2 = { "GRANT_AND_STORE", 		{ .i32 = 2 }};
 			dict_type_enum_data_t 	t_3 = { "GRANT_AND_LOSE",		{ .i32 = 3 }};
--- a/waaad/message.c	Tue Jul 29 16:11:07 2008 +0900
+++ b/waaad/message.c	Wed Jul 30 14:58:28 2008 +0900
@@ -1871,6 +1871,142 @@
 	return 0;		
 }
 
+/* Set the value of an AVP, using formatted data */
+int msg_avp_value_encode ( void *data, msg_avp_t *avp )
+{
+	dict_base_type_t type = -1;
+	dict_type_data_t type_data;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p", data, avp);
+	
+	/* Check parameter */
+	if ((!CHECK_AVP(avp)) || (!_A(avp)->avp_model)) {
+		TRACE_DEBUG(INFO, "Received an invalid reference object. EINVAL");
+		return EINVAL;
+	}
+	
+	/* Retrieve information from the AVP model and it's parent type */
+	{
+		dict_object_type_t dicttype = -1;
+		dict_avp_data_t dictdata;
+		dict_object_t * parenttype = NULL;
+		
+		/* First check the base type of the AVP */
+		ret = dict_gettype(_A(avp)->avp_model, &dicttype);
+		if (ret || (dicttype != DICT_AVP)) {
+			TRACE_DEBUG(INFO, "Received an invalid reference object. EINVAL");
+			return EINVAL;
+		}
+		ret = dict_getval(_A(avp)->avp_model, &dictdata);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Error getting avp model data");
+			return ret;
+		}
+		type = dictdata.avp_basetype;
+		if (type == AVP_TYPE_GROUPED) {
+			TRACE_DEBUG(INFO, "Cannot set a value to a grouped AVP. EINVAL");
+			return EINVAL;
+		}
+		
+		/* Then retrieve information about the parent's type (= derived type) */
+		ret = dict_search(DICT_TYPE, TYPE_OF_AVP, _A(avp)->avp_model, &parenttype);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Error in dict_search: %s", strerror(ret));
+			return ret;
+		}
+		if (parenttype == NULL) {
+			TRACE_DEBUG(INFO, "This AVP is not of derived type, cannot encode the data.");
+			return EINVAL;
+		}
+		ret = dict_getval(parenttype, &type_data);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Error getting type model data");
+			return ret;
+		}
+		if (type_data.type_encode == NULL) {
+			TRACE_DEBUG(INFO, "This AVP type does not provide a callback to encode formatted data. ENOTSUP.");
+			return ENOTSUP;
+		}
+	}
+	
+	/* Ok, now we can encode the value */
+	
+	/* First, clean any previous value */
+	if (_A(avp)->avp_mustfreeos != 0) {
+		free(_A(avp)->avp_storage.os.data);
+		_A(avp)->avp_mustfreeos = 0;
+	}
+	_A(avp)->avp_public.avp_data = NULL;
+	memset(&_A(avp)->avp_storage, 0, sizeof(avp_value_t));
+	
+	/* Now call the type's callback to encode the data */
+	ret = (*type_data.type_encode)(data, &_A(avp)->avp_storage);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to encode the data: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* If an octetstring has been allocated, let's mark it to be freed */
+	if (type == AVP_TYPE_OCTETSTRING)
+		_A(avp)->avp_mustfreeos = 1;
+	
+	/* Set the data pointer of the public part */
+	_A(avp)->avp_public.avp_data = &_A(avp)->avp_storage;
+	
+	return 0;		
+}
+
+/* Interpret the value of an AVP into formatted data */
+int msg_avp_value_interpret ( msg_avp_t *avp, void *data )
+{
+	dict_base_type_t type = -1;
+	dict_type_data_t type_data;
+	int ret = 0;
+	
+	TRACE_ENTRY("%p %p", avp, data);
+	
+	/* Check parameter */
+	if ((!CHECK_AVP(avp)) || (!_A(avp)->avp_model) || (!_A(avp)->avp_public.avp_data)) {
+		TRACE_DEBUG(INFO, "Received an invalid AVP object. EINVAL");
+		return EINVAL;
+	}
+	
+	/* Retrieve information about the AVP parent type */
+	{
+		dict_object_t * parenttype = NULL;
+		
+		ret = dict_search(DICT_TYPE, TYPE_OF_AVP, _A(avp)->avp_model, &parenttype);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Error in dict_search: %s", strerror(ret));
+			return ret;
+		}
+		if (parenttype == NULL) {
+			TRACE_DEBUG(INFO, "This AVP is not of derived type, cannot interpret the data.");
+			return EINVAL;
+		}
+		ret = dict_getval(parenttype, &type_data);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Error getting type model data");
+			return ret;
+		}
+		if (type_data.type_interpret == NULL) {
+			TRACE_DEBUG(INFO, "This AVP type does not provide a callback to interpret value in formatted data. ENOTSUP.");
+			return ENOTSUP;
+		}
+	}
+	
+	/* Ok, now we can interpret the value */
+	
+	ret = (*type_data.type_interpret)(_A(avp)->avp_public.avp_data, data);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to interpret the data: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;		
+}
+
 /* Retrieve the address of the msg_public field of a message */
 int msg_data ( msg_t *msg, msg_data_t **pdata )
 {
--- a/waaad/tests/testmesg.c	Tue Jul 29 16:11:07 2008 +0900
+++ b/waaad/tests/testmesg.c	Wed Jul 30 14:58:28 2008 +0900
@@ -34,7 +34,8 @@
 *********************************************************************************************************/
 
 #include "tests.h"
-
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 extern void dump_dict_object ( void * obj, int parents, int depth, int indent );
 
@@ -122,7 +123,7 @@
 		CHECK( 0, diff);
 	}
 	
-	/* Check that there are no AVP after the prox-info */
+	/* Check that there are no AVP after the proxy-info */
 	CHECK( ENOENT, msg_browse ( pi, MSG_BRW_NEXT, &avp2, NULL) );
 	
 	/* Test the msg_avp_del function unlinks the object properly */
@@ -833,6 +834,127 @@
 			}
 		}
 	}
+	
+	/* Test the msg_avp_value_interpret and msg_avp_value_encode functions. use the Address type and Host-IP-Address AVPs */
+	{
+		dict_object_t * cer_model = NULL;
+		msg_t * cer = NULL;
+		
+		dict_object_t * hia_model = NULL;
+		msg_avp_t *avp4, *avp6;
+		#define TEST_IP4 "192.168.100.101"
+		char buf4[INET_ADDRSTRLEN];
+		#define	TEST_IP6 "1111:2222:3333:4444:1234:5678:9abc:def0"
+		char buf6[INET6_ADDRSTRLEN];
+		
+		struct sockaddr_storage ss;
+		struct sockaddr_in  sin,  *psin;
+		struct sockaddr_in6 sin6, *psin6;
+		
+		/* Find the CER dictionary object */
+		CHECK( 0, dict_search ( DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model ) );
+
+		/* Now find the Host-IP-Address dictionary object */
+		CHECK( 0, dict_search ( DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &hia_model ) );
+
+		/* Create the msg instance */
+		CHECK( 0, msg_new ( cer_model, 0, &cer ) );
+
+		/* Create the avp instances */
+		CHECK( 0, msg_avp_new ( hia_model, 0, &avp4 ) );
+		CHECK( 0, msg_avp_new ( hia_model, 0, &avp6 ) );
+		
+		/* Set the value of the IP avp */
+		sin.sin_family = AF_INET;
+		CHECK( 1, inet_pton( AF_INET, TEST_IP4, &sin.sin_addr.s_addr ) );
+		CHECK( 0, msg_avp_value_encode ( &sin, avp4 ) );
+		
+		/* Set the value of the IP6 avp */
+		sin6.sin6_family = AF_INET6;
+		CHECK( 1, inet_pton( AF_INET6, TEST_IP6, &sin6.sin6_addr.s6_addr ) );
+		CHECK( 0, msg_avp_value_encode ( &sin6, avp6 ) );
+		
+		/* Add these AVPs in the message */
+		CHECK( 0, msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp4) );
+		CHECK( 0, msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp6) );
+		
+		/* Create the buffer for this message */
+		CHECK( 0, msg_bufferize( cer, &buf, NULL ) );
+		
+		/* Now free the message, we keep only the buffer. */
+		CHECK( 0, msg_free( cer, 1 ) );
+		
+		/* Check the content of the buffer is correct (skip command header) */
+		CHECK( 0x00, buf[20] ); /* First AVP (IP4) begins: code 257 = 0x00000101 */
+		CHECK( 0x00, buf[21] );
+		CHECK( 0x01, buf[22] );
+		CHECK( 0x01, buf[23] );
+		CHECK( 0x40, buf[24] ); /* flags: M */
+		CHECK( 0x00, buf[25] ); /* length: 8+6 = 0x00000e */
+		CHECK( 0x00, buf[26] );
+		CHECK( 0x0E, buf[27] );
+		CHECK( 0x00, buf[28] ); /* Value: AddressType 1 */
+		CHECK( 0x01, buf[29] ); 
+		CHECK(  192, buf[30] ); /* 192.168.100.101 */
+		CHECK(  168, buf[31] ); 
+		CHECK(  100, buf[32] ); 
+		CHECK(  101, buf[33] );
+		
+		CHECK( 0x00, buf[34] ); /* Padding */
+		CHECK( 0x00, buf[35] );
+		
+		CHECK( 0x00, buf[36] ); /* Second AVP (IP6) begins: code 257 = 0x00000101 */
+		CHECK( 0x00, buf[37] );
+		CHECK( 0x01, buf[38] );
+		CHECK( 0x01, buf[39] );
+		CHECK( 0x40, buf[40] ); /* flags: M */
+		CHECK( 0x00, buf[41] ); /* length: 8+18 = 0x00001a */
+		CHECK( 0x00, buf[42] );
+		CHECK( 0x1A, buf[43] );
+		CHECK( 0x00, buf[44] ); /* Value: AddressType 2 */
+		CHECK( 0x02, buf[45] ); 
+		CHECK( 0x11, buf[46] ); /* 1111:2222:3333:4444:1234:5678:9abc:def0 */
+		CHECK( 0x11, buf[47] ); 
+		CHECK( 0x22, buf[48] ); 
+		CHECK( 0x22, buf[49] );
+		CHECK( 0x33, buf[50] );
+		CHECK( 0x33, buf[51] );
+		CHECK( 0x44, buf[52] );
+		CHECK( 0x44, buf[53] );
+		CHECK( 0x12, buf[54] );
+		CHECK( 0x34, buf[55] );
+		CHECK( 0x56, buf[56] );
+		CHECK( 0x78, buf[57] );
+		CHECK( 0x9a, buf[58] );
+		CHECK( 0xbc, buf[59] );
+		CHECK( 0xde, buf[60] );
+		CHECK( 0xf0, buf[61] );
+		
+		/* Ok, now let's recreate the message */
+		CHECK( 0, msg_parse_buffer( buf, 64, &cer) );
+		CHECK( 0, msg_parse_dict( cer ) );
+		free(buf);
+		
+		/* Get the pointers to the first and last AVP */
+		CHECK( 0, msg_browse( cer, MSG_BRW_FIRST_CHILD, &avp4, NULL) );
+		CHECK( 0, msg_browse( cer, MSG_BRW_LAST_CHILD,  &avp6, NULL) );
+		
+		/* Try and interpret the data in the AVPs */
+		CHECK( 0, msg_avp_value_interpret ( avp4, &ss ) );
+		psin = (struct sockaddr_in *)&ss;
+		CHECK( AF_INET, psin->sin_family );
+		CHECK( 0, (inet_ntop( AF_INET, &psin->sin_addr.s_addr, buf4, sizeof(buf4) ) == NULL) ? errno : 0 );
+		CHECK( 0, strcmp( buf4, TEST_IP4 ) );
+		
+		CHECK( 0, msg_avp_value_interpret ( avp6, &ss ) );
+		psin6 = (struct sockaddr_in6 *)&ss;
+		CHECK( AF_INET6, psin6->sin6_family );
+		CHECK( 0, (inet_ntop( AF_INET6, &psin6->sin6_addr.s6_addr, buf6, sizeof(buf6) ) == NULL) ? errno : 0 );
+		CHECK( 0, strcasecmp( buf6, TEST_IP6 ) );
+		
+		/* Ok, it's done */
+		CHECK( 0, msg_free( cer, 1 ) );
+	}
 
 	/* That's all for the tests yet */
 	PASSTEST();
"Welcome to our mercurial repository"