changeset 83:ff4e4c50bc84

Completed the messages module; more tests needed
author Sebastien Decugis <sdecugis@nict.go.jp>
date Mon, 14 Jul 2008 18:31:55 +0900
parents 3af3e35edc07
children 2e9d4fc2adc5
files configure.ac include/waaad/message-api.h waaad/dictionary.c waaad/message.c waaad/message.h waaad/tests/testmesg.c
diffstat 6 files changed, 746 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Fri Jul 11 18:28:19 2008 +0900
+++ b/configure.ac	Mon Jul 14 18:31:55 2008 +0900
@@ -142,6 +142,15 @@
 	  [#endif]
 	])
 
+AC_CHECK_DECL(
+	[__BYTE_ORDER],
+	,
+	[AC_MSG_ERROR([This package needs __BYTE_ORDER definition. Please update the configure.ac to support non-standard paths.])],
+	[
+	  [#include <sys/param.h>]
+	])
+	
+
 #######################
 # Checks for library functions.
 
@@ -151,6 +160,9 @@
 # Some POSIX systems may not have the read-write lock support?
 AC_CHECK_FUNC([pthread_rwlock_init], ,[AC_MSG_ERROR([This package needs a pthread implementation with read-write locks support.])])
 
+# When this is not provided by host, we provide a replacement function in the daemon.
+AC_CHECK_FUNC([ntohll], [AC_DEFINE([HAVE_NTOHLL], [1], [Defined if the ntohll function or macro is defined])])
+
 
 
 #######################
--- a/include/waaad/message-api.h	Fri Jul 11 18:28:19 2008 +0900
+++ b/include/waaad/message-api.h	Mon Jul 14 18:31:55 2008 +0900
@@ -316,12 +316,13 @@
  *
  * DESCRIPTION: 
  *   This function provides an access to the data received in an unknown AVP.
+ *  If called on an AVP for which msg_parse_dict was not yet called, or the model was found, the data pointer is set to NULL on return.
  *
  * RETURN VALUE:
  *  0      	: The data parameter now points to the received data.
  *  EINVAL 	: A parameter is invalid for this operation.
  */
-int msg_avp_getrawdata ( msg_avp_t *avp, char **data );
+int msg_avp_getrawdata ( msg_avp_t *avp, unsigned char **data );
 
 
 /***************************************/
@@ -334,7 +335,6 @@
  * PARAMETERS:
  *  object	: A msg_t or msg_avp_t object that must be verified.
  *  rule	: if not NULL, the first conflicting rule will be put here if a conflict is found.
- *		 In case where an AVP with the 'M' bit set is unknown in the dictionary, *rule is NULL on error return.
  *
  * DESCRIPTION: 
  *   Check that the children of the object do not conflict with the dictionary rules.
@@ -370,7 +370,7 @@
 	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, char **data );
+	int (*msg_avp_getrawdata) ( msg_avp_t *avp, unsigned char **data );
 	int (*msg_parse_rules)    ( void * object, dict_object_t ** rule);
 } api_msg_t;
 
--- a/waaad/dictionary.c	Fri Jul 11 18:28:19 2008 +0900
+++ b/waaad/dictionary.c	Mon Jul 14 18:31:55 2008 +0900
@@ -1401,7 +1401,6 @@
 /* Get the type of an object */
 int dict_gettype ( dict_object_t * object, dict_object_type_t * type)
 {
-	TRACE_DEBUG (FULL, "dict_gettype (%p, %p).", object, type );
 	if (!type || verify_object(_O(object))) {
 		TRACE_DEBUG (FULL, "EINVAL." );
 		return EINVAL;
@@ -1415,8 +1414,6 @@
 /* Get the data associated to an object */
 int dict_getval ( dict_object_t * object, void * val)
 {
-	TRACE_DEBUG (FULL, "dict_getval (%p, %p).", object, val );
-
 	if (!val || verify_object(_O(object))) {
 		TRACE_DEBUG (FULL, "EINVAL." );
 		return EINVAL;
--- a/waaad/message.c	Fri Jul 11 18:28:19 2008 +0900
+++ b/waaad/message.c	Mon Jul 14 18:31:55 2008 +0900
@@ -42,6 +42,7 @@
 #include <errno.h>
 #include <string.h>
 #include <assert.h>
+#include <sys/param.h>
 
 #include "waaad-internal.h"
 
@@ -111,7 +112,9 @@
 	dict_object_t 	*avp_model;		/* If not NULL, pointer to the dictionary object of this avp */
 	msg_avp_data_t	 avp_public;		/* AVP data that can be managed by extensions */
 	
-	char		*avp_source;		/* If the message was parsed from a buffer, pointer to the AVP start in the buffer. */
+	unsigned char	*avp_source;		/* If the message was parsed from a buffer, pointer to the AVP data start in the buffer. */
+	unsigned char	*avp_rawdata;		/* when the data can not be interpreted, the raw data is copied here. The header is not part of it. */
+	size_t		 avp_rawlen;		/* The length of the raw buffer. */
 	avp_value_t	 avp_storage;		/* To avoid many alloc/free, store the integer values here and set avp_public.avp_data to &storage */
 	int		 avp_mustfreeos;	/* 1 if an octetstring is malloc'd in avp_storage and must be freed. */
 } _msg_avp_t;
@@ -119,7 +122,7 @@
 /* Macro to compute the AVP header size */
 #define AVPHDRSZ_NOVEND	8
 #define AVPHDRSZ_VENDOR	12
-#define GETAVPHDRSZ( _vend_flag ) ((_vend_flag) ? AVPHDRSZ_VENDOR : AVPHDRSZ_NOVEND)
+#define GETAVPHDRSZ( _flag ) ((_flag & AVP_FLAG_VENDOR) ? AVPHDRSZ_VENDOR : AVPHDRSZ_NOVEND)
 
 /* Macro to cast a msg_avp_t */
 #define _A(_x) ((_msg_avp_t *)(_x))
@@ -133,7 +136,6 @@
 	dict_object_t 	*msg_model;		/* If not NULL, pointer to the dictionary object of this message */
 	msg_data_t	 msg_public;		/* Message data that can be managed by extensions. */
 	
-	char		*msg_source;		/* If the message was parsed from a buffer, pointer to the message start in the buffer. */
 	int		 msg_routable;		/* Is this a routable message? (0: undef, 1: routable, 2: non routable) */
 } _msg_t;
 
@@ -165,6 +167,47 @@
 #define CHECK_BASETYPE( _type ) ( (_type <= AVP_TYPE_MAX) && (_type >= 0) )
 #define GETINITIALSIZE( _type, _vend ) (avp_value_sizes[ CHECK_BASETYPE(_type) ? _type : 0] + GETAVPHDRSZ(_vend))
 
+/* This macro will pad a size to the next multiple of 4. */
+#define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) )
+
+/***************************************************************************************************************/
+
+/* We provide macros to convert 64 bit values to and from network byte-order, on systems where it is not already provided. */
+
+#ifndef HAVE_NTOHLL	/* Defined in config.h, if the ntohll symbol is defined on the system */
+# ifdef __BYTE_ORDER
+#  if __BYTE_ORDER == __LITTLE_ENDIAN
+
+/* For these systems, we must reverse the bytes. Use ntohl and htonl on sub-32 blocs, and inverse these blocs. */
+#define ntohll(x) (typeof (x))( (((uint64_t)ntohl( (uint32_t)(x))) << 32 ) | ((uint64_t) ntohl( ((uint64_t)(x)) >> 32 ))) 
+#define htonll(x) (typeof (x))( (((uint64_t)htonl( (uint32_t)(x))) << 32 ) | ((uint64_t) htonl( ((uint64_t)(x)) >> 32 ))) 
+
+#  else /* ! __LITTLE_ENDIAN */
+#   if __BYTE_ORDER == __BIG_ENDIAN
+
+/* In big-endian systems, we don't have to change the values, since the order is the same as network */
+#   define ntohll(x) (x)
+#   define htonll(x) (x)
+
+#   else /* ! __BIG_ENDIAN */
+#    if __BYTE_ORDER == __PDP_ENDIAN
+
+/* We don't support these systems currently */
+#     error "Unsupported byte order: PDP. Please provide implementation for ntohll and htonll"
+
+#    else /* ! __PDP_ENDIAN */
+
+/* What is the byte order then??? */
+#     error "Unknown byte order"
+
+#    endif /* __PDP_ENDIAN */
+#   endif /* __BIG_ENDIAN */
+#  endif /* __LITTLE_ENDIAN */
+# else /* __BYTE_ORDER */
+#  error "__BYTE_ORDER is not defined, and configure did not detect that."
+# endif /* __BYTE_ORDER */
+#endif /* HAVE_NTOHLL */
+   
 /***************************************************************************************************************/
 
 static void item_unlink(_msg_list_t * item);
@@ -224,6 +267,10 @@
 	if ((obj->type == _MSG_AVP) && (_A(obj)->avp_mustfreeos == 1)) {
 		free(_A(obj)->avp_storage.os.data);
 	}
+	/* Free the rawdata if needed */
+	if ((obj->type == _MSG_AVP) && (_A(obj)->avp_rawdata != NULL)) {
+		free(_A(obj)->avp_rawdata);
+	}
 	
 	/* free the object */
 	free(obj);
@@ -377,7 +424,7 @@
 			DUMP_CMDFL_val(dictdata.cmd_flag_val), DUMP_CMDFL_val(dictdata.cmd_flag_mask), dictdata.cmd_code, dictdata.cmd_name);
 	}
 public:	
-	log_debug(INOBJHDR "public: V:%d L:%d fl:" DUMP_CMDFL_str " CC:%u A:%d hi:%d ei:%d\n", INOBJHDRVAL, 
+	log_debug(INOBJHDR "public: V:%d L:%d fl:" DUMP_CMDFL_str " CC:%u A:%d hi:%x ei:%x\n", INOBJHDRVAL, 
 		msg->msg_public.msg_version,
 		msg->msg_public.msg_length,
 		DUMP_CMDFL_val(msg->msg_public.msg_flags),
@@ -386,7 +433,7 @@
 		msg->msg_public.msg_hbhid,
 		msg->msg_public.msg_eteid
 		);
-	log_debug(INOBJHDR "intern: src:%p rt:%d\n", INOBJHDRVAL, msg->msg_source, msg->msg_routable);
+	log_debug(INOBJHDR "intern: rt:%d\n", INOBJHDRVAL, msg->msg_routable);
 }
 #define DUMP_VALUE(_format, _parms...)   log_debug(INOBJHDR "value : t:'%s' v:'" _format "'\n", INOBJHDRVAL, typename, ## _parms);
 #define ASCII(_c) ( ((_c < 32) || (_c > 127)) ? ( _c ? '?' : ' ' ) : _c )
@@ -519,7 +566,7 @@
 		type = dictdata.avp_basetype;
 	}
 public:	
-	log_debug(INOBJHDR "public: C:%d fl:" DUMP_AVPFL_str " L:%d V:%d  data:@%p\n", INOBJHDRVAL, 
+	log_debug(INOBJHDR "public: C:%u fl:" DUMP_AVPFL_str " L:%d V:%u  data:@%p\n", INOBJHDRVAL, 
 		avp->avp_public.avp_code,
 		DUMP_AVPFL_val(avp->avp_public.avp_flags),
 		avp->avp_public.avp_len,
@@ -569,7 +616,7 @@
 		}
 	}
 end:	
-	log_debug(INOBJHDR "intern: src:%p mf:%d\n", INOBJHDRVAL, avp->avp_source, avp->avp_mustfreeos);
+	log_debug(INOBJHDR "intern: src:%p mf:%d raw:%p(%d)\n", INOBJHDRVAL, avp->avp_source, avp->avp_mustfreeos, avp->avp_rawdata, avp->avp_rawlen);
 }
 /* Dump a single object content */
 static void msg_dump_intern ( void * obj, int indent )
@@ -594,7 +641,470 @@
 	}
 }
 
+/***************************************************************************************************************/
+/* Creating a buffer from memory objects */
 
+/* Following macros are used to store 32 and 64 bit fields into a buffer in network byte order */
+#define PUT_in_buf_32( _u32data, _bufptr ) {							\
+	*(uint32_t *)(_bufptr) = htonl((uint32_t)(_u32data));					\
+}
+#define PUT_in_buf_64( _u64data, _bufptr ) {							\
+	*(uint64_t *)(_bufptr) = htonll((uint64_t)(_u64data));					\
+}
+
+/* Write a message header in the buffer */
+static int _msg_buf_msg(unsigned char * buffer, size_t buflen, size_t * offset, _msg_t * msg)
+{
+	if ((buflen - *offset) < GETMSGHDRSZ())
+		return ENOSPC;
+	
+	if (*offset & 0x3)
+		return EFAULT;	/* We are supposed to start on 32 bit boundaries */
+	
+	PUT_in_buf_32(msg->msg_public.msg_length, buffer + *offset);
+	buffer[*offset] = msg->msg_public.msg_version;
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_code, buffer + *offset);
+	buffer[*offset] = msg->msg_public.msg_flags;
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_appl, buffer + *offset);
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_hbhid, buffer + *offset);
+	*offset += 4;
+	
+	PUT_in_buf_32(msg->msg_public.msg_eteid, buffer + *offset);
+	*offset += 4;
+	
+	return 0;
+}
+
+static int _msg_buf_chain(unsigned char * buffer, size_t buflen, size_t * offset, _msg_list_t * list);
+
+/* Write an AVP in the buffer */
+static int _msg_buf_avp(unsigned char * buffer, size_t buflen, size_t * offset,  _msg_avp_t * avp)
+{
+	dict_avp_data_t dictdata;
+	int ret = 0;
+	
+	if ((buflen - *offset) < avp->avp_public.avp_len)
+		return ENOSPC;
+	
+	if (ret = dict_getval(avp->avp_model, &dictdata)) {
+		TRACE_DEBUG(INFO, "dict_getval failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Write the header */
+	PUT_in_buf_32(avp->avp_public.avp_code, buffer + *offset);
+	*offset += 4;
+	
+	PUT_in_buf_32(avp->avp_public.avp_len, buffer + *offset);
+	buffer[*offset] = avp->avp_public.avp_flags;
+	*offset += 4;
+	
+	
+	if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
+		PUT_in_buf_32(avp->avp_public.avp_vendor, buffer + *offset);
+		*offset += 4;
+	}
+	
+	/* Then we must write the AVP value */
+	switch (dictdata.avp_basetype) {
+		case AVP_TYPE_GROUPED:
+			return _msg_buf_chain(buffer, buflen, offset, &_C(avp)->children);
+		
+		case AVP_TYPE_OCTETSTRING:
+			memcpy(&buffer[*offset], avp->avp_public.avp_data->os.data, avp->avp_public.avp_data->os.len);
+			*offset += avp->avp_public.avp_data->os.len;
+			break;
+		
+		case AVP_TYPE_INTEGER32:
+			PUT_in_buf_32(avp->avp_public.avp_data->i32, buffer + *offset);
+			*offset += 4;
+			break;
+		
+		case AVP_TYPE_INTEGER64:
+			PUT_in_buf_64(avp->avp_public.avp_data->i64, buffer + *offset);
+			*offset += 8;
+			break;
+		
+		case AVP_TYPE_UNSIGNED32:
+			PUT_in_buf_32(avp->avp_public.avp_data->u32, buffer + *offset);
+			*offset += 4;
+			break;
+		
+		case AVP_TYPE_UNSIGNED64:
+			PUT_in_buf_64(avp->avp_public.avp_data->u64, buffer + *offset);
+			*offset += 8;
+			break;
+		
+		case AVP_TYPE_FLOAT32:
+			PUT_in_buf_32(avp->avp_public.avp_data->u32, buffer + *offset);
+			*offset += 4;
+			break;
+		
+		case AVP_TYPE_FLOAT64:
+			PUT_in_buf_64(avp->avp_public.avp_data->u64, buffer + *offset);
+			*offset += 8;
+			break;
+		
+		default:
+			assert(0);
+	}
+	return 0;
+}
+			
+/* Write a chain of AVPs in the buffer */
+static int _msg_buf_chain(unsigned char * buffer, size_t buflen, size_t * offset, _msg_list_t * list)
+{
+	int ret = 0;
+	_msg_list_t * avpch;
+	for (avpch = list->next; avpch != list; avpch = avpch->next) {
+		/* Re-align the offset if needed */
+		*offset = PAD4(*offset);
+		
+		/* Bufferize the AVP */
+		ret = _msg_buf_avp(buffer, buflen, offset, _A(avpch->top));
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "_msg_buf_avp returned: %s", strerror(ret));
+			return ret;
+		}
+	}
+	return 0;
+}
+
+/***************************************************************************************************************/
+/* Parsing buffers and building AVP objects lists */
+
+/* note: the _mpb prefix stands for "msg_parse_buffer" */
+
+/* Parse a buffer containing a supposed list of AVPs */
+static int _mpb_list(unsigned char * buf, size_t buflen, _msg_list_t * list)
+{
+	size_t offset = 0;
+	
+	while (offset < buflen) {
+		_msg_avp_t * avp;
+		
+		if (buflen - offset <= AVPHDRSZ_NOVEND) {
+			TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes", buflen - offset);
+			return EBADMSG;
+		}
+		
+		/* Create a new AVP object */
+		avp = (_msg_avp_t *) malloc (sizeof(_msg_avp_t));
+		if (!avp) {
+			log_error("Unable to allocate enough memory: %s\n", strerror(errno));
+			TRACE_DEBUG (FULL, "malloc failed.");
+			return ENOMEM;
+		}
+		
+		init_avp(avp);
+
+		avp->avp_public.avp_code    = ntohl(*(uint32_t *)(buf + offset));
+		avp->avp_public.avp_flags   = buf[offset + 4];
+		avp->avp_public.avp_len     = ((uint32_t)buf[offset+5]) << 16 |  ((uint32_t)buf[offset+6]) << 8 |  ((uint32_t)buf[offset+7]) ;
+		
+		offset += 8;
+		
+		if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
+			if (buflen - offset <= 4) {
+				TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for vendor and data", buflen - offset);
+				free(avp);
+				return EBADMSG;
+			}
+			avp->avp_public.avp_vendor  = ntohl(*(uint32_t *)(buf + offset));
+			offset += 4;
+		}
+		
+		/* Check there is enough remaining data in the buffer */
+		if (buflen - offset < avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags)) {
+			TRACE_DEBUG(INFO, "truncated buffer: remaining only %d bytes for data, and avp data size is %d", 
+					buflen - offset, 
+					avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
+			free(avp);
+			return EBADMSG;
+		}
+		
+		/* buf[offset] is now the beginning of the data */
+		avp->avp_source = &buf[offset];
+		
+		/* Now eat the data and eventual padding */
+		offset += PAD4(avp->avp_public.avp_len - GETAVPHDRSZ(avp->avp_public.avp_flags));
+		
+		/* And insert this avp in the list, at the end */
+		INSERT_before( list, &_C(avp)->chaining );
+	}
+	
+	return 0;
+}
+		
+	
+	
+	
+
+/***************************************************************************************************************/
+/* Parsing messages and AVP for dictionary compliance */
+
+/* Resolve dictionary objects of the cmd and avp instances, from their headers.
+ * When the model is found, the data is interpreted from the avp_source buffer and copied to avp_storage.
+ * When the model is not found, the raw data is copied and saved.
+ * Therefore, after this function has been called, the source buffer can be freed.
+ * For command, if the dictionary model is not found, an error is returned.
+ */
+
+/* note: the _mpd prefix stands for "msg_parse_dict" */
+
+static int _mpd_do_chain(_msg_list_t * head, int recheck, int mandatory);
+
+/* Process an AVP. If we are not in recheck, the avp_source must be set. */
+static int _mpd_do_avp(_msg_avp_t * avp, int recheck, int mandatory)
+{
+	int ret = 0;
+	dict_avp_data_t dictdata;
+	
+	/* First check we received an AVP as input */
+	if (!CHECK_AVP(avp)) {
+		TRACE_DEBUG(FULL, "The parameter is not an AVP");
+		return EINVAL;
+	}
+	
+	if (avp->avp_model != NULL) {
+		if (recheck == 0) {
+			TRACE_DEBUG(FULL, "The AVP model is already set.");
+			return EINVAL;
+		} else {
+			/* the model has already been resolved. we do check it is still valid */
+			
+			ret = dict_getval(avp->avp_model, &dictdata);
+			if (ret != 0) {
+				TRACE_DEBUG(INFO, "Error getting the avp model data from the dictionary: %s", strerror(ret));
+				return ret;
+			}
+			
+			if (avp->avp_public.avp_code != dictdata.avp_code) {
+				TRACE_DEBUG(INFO, "The model is not in sync with the AVP public data, => invalid AVP.");
+				return EINVAL;
+			}
+			
+			/* Ok then just process the children if any */
+			return _mpd_do_chain(&_C(avp)->children, recheck, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY));
+		}
+	}
+	
+	/* Now try and resolve the model from the avp code and vendor */
+	if (avp->avp_public.avp_flags & AVP_FLAG_VENDOR) {
+		dict_avp_request_t avpreq;
+		avpreq.avp_vendor = avp->avp_public.avp_vendor;
+		avpreq.avp_code = avp->avp_public.avp_code;
+		ret = dict_search ( DICT_AVP, AVP_BY_CODE_AND_VENDOR, &avpreq, &avp->avp_model);
+	} else {
+		/* no vendor */
+		ret = dict_search ( DICT_AVP, AVP_BY_CODE_REF, &avp->avp_public.avp_code, &avp->avp_model);
+	}
+	
+	/* First handle the case where we have not found this AVP in the dictionary */
+	if (ret == ENOENT) {
+		
+		if (mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
+			TRACE_DEBUG(FULL, "Unsupported mandatory AVP found.");
+			return ENOTSUP;
+		}
+		
+		if (recheck) {
+			/* In this case, the data is supposed to be in the raw internal buffer */
+			if (avp->avp_rawdata == NULL) {
+				TRACE_DEBUG(FULL, "Invalid buffer state in the AVP object.");
+				return EINVAL;
+			}
+			TRACE_DEBUG(FULL, "Unsupported optional AVP found, source data already in avp_rawdata.");
+		} else {
+			/* we must copy the data from the source to the internal buffer area */
+			if ((avp->avp_source == NULL) || (avp->avp_rawdata != NULL)) {
+				TRACE_DEBUG(FULL, "Invalid buffers states in the AVP object.");
+				return EINVAL;
+			}
+			
+			avp->avp_rawdata = malloc(avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ));
+			if (!avp->avp_rawdata) {
+				log_error("Memory allocation failed. %s", strerror(errno));
+				TRACE_DEBUG(FULL, "Malloc failed.");
+				return ENOMEM;
+			}
+			
+			memcpy(avp->avp_rawdata, avp->avp_source, avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ));
+			avp->avp_source = NULL;
+			
+			TRACE_DEBUG(FULL, "Unsupported optional AVP found, source data saved in avp_rawdata.");
+		}
+		
+		return 0;
+	}
+	
+	/* Handle other errors */
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Unexpected error in dict_search: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Ok we have resolved the object. Now we need to interpret its content. */
+	
+	ret = dict_getval(avp->avp_model, &dictdata);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Error getting the avp model data from the dictionary: %s", strerror(ret));
+		return ret;
+	}
+	
+	if (recheck) {
+		/* The data is supposed to be in avp->avp_rawdata. We set source to the same location so that the remaining is the same */
+		avp->avp_source = avp->avp_rawdata;
+	}
+	
+	/* Check the size is valid */
+	assert(CHECK_BASETYPE(dictdata.avp_basetype));
+	if ((avp_value_sizes[dictdata.avp_basetype] != 0) &&
+	    (avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ) != avp_value_sizes[dictdata.avp_basetype])) {
+		TRACE_DEBUG(INFO, "The AVP size is not conform to the type. EBADMSG.");
+		return EBADMSG;
+	}
+	
+	/* Now get the value inside */
+	switch (dictdata.avp_basetype) {
+		case AVP_TYPE_GROUPED:
+			/* This is a grouped AVP, so let's parse the list of AVPs inside */
+			ret = _mpb_list(avp->avp_source, avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags ), &_C(avp)->children);
+			if (ret != 0) {
+				TRACE_DEBUG(INFO, "Error parsing the grouped AVP content buffer: %s", strerror(ret));
+				return ret;
+			}
+			
+			return _mpd_do_chain(&_C(avp)->children, 0, mandatory && (avp->avp_public.avp_flags & AVP_FLAG_MANDATORY));
+			
+		case AVP_TYPE_OCTETSTRING:
+			/* We just have to copy the string into the storage area */
+			avp->avp_storage.os.len = avp->avp_public.avp_len - GETAVPHDRSZ( avp->avp_public.avp_flags );
+			avp->avp_storage.os.data = malloc(avp->avp_storage.os.len);
+			if (!avp->avp_storage.os.data) {
+				log_error("Memory allocation failed. %s", strerror(errno));
+				TRACE_DEBUG(FULL, "Malloc failed.");
+				return ENOMEM;
+			}
+			avp->avp_mustfreeos = 1;
+			memcpy(avp->avp_storage.os.data, avp->avp_source, avp->avp_storage.os.len);
+			break;
+		
+		case AVP_TYPE_INTEGER32:
+			avp->avp_storage.i32 = (int32_t)ntohl(*(uint32_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_INTEGER64:
+			avp->avp_storage.i64 = (int64_t)ntohll(*(uint64_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_UNSIGNED32:
+			avp->avp_storage.u32 = (uint32_t)ntohl(*(uint32_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_UNSIGNED64:
+			avp->avp_storage.u64 = (uint64_t)ntohll(*(uint64_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_FLOAT32:
+			avp->avp_storage.f32 = (double)ntohl(*(uint32_t *)avp->avp_source);
+			break;
+	
+		case AVP_TYPE_FLOAT64:
+			avp->avp_storage.f64 = (long double)ntohll(*(uint64_t *)avp->avp_source);
+			break;
+	
+	}
+	
+	/* The value is now set, so set the data pointer and return 0 */
+	avp->avp_public.avp_data = &avp->avp_storage;
+	return 0;
+}
+
+/* Process a list of AVPs */
+static int _mpd_do_chain(_msg_list_t * head, int recheck, int mandatory)
+{
+	int ret;
+	_msg_list_t * avpch;
+	
+	/* Sanity check */
+	assert ( head == head->head );
+	
+	/* Now process the list */
+	for (avpch=head->next; avpch != head; avpch = avpch->next) {
+		ret = _mpd_do_avp(_A(avpch->top), recheck, mandatory);
+		if (ret != 0) {
+			TRACE_DEBUG(FULL, "_mpd for child %p failed: %s", avpch->top, strerror(ret));
+			return ret;
+		}
+	}
+	
+	/* Done */
+	return 0;
+}
+
+/* Process a msg header. */
+static int _mpd_do_msg(_msg_t * msg, int recheck)
+{
+	int ret = 0;
+	
+	if (! CHECK_MSG(msg) ) {
+		TRACE_DEBUG (FULL, "Invalid message received.");
+		return EINVAL;
+	}
+	
+	if (recheck)
+		msg->msg_model = NULL;
+	
+	if ( msg->msg_model != NULL) {
+		TRACE_DEBUG (FULL, "MSG already parsed, we do not process again.");
+		return EINVAL;
+	}
+	
+	/* Now look for the model from the header */
+	ret = dict_search ( DICT_COMMAND, 
+			(msg->msg_public.msg_flags & CMD_FLAG_REQUEST) ? CMD_BY_CODE_R_REF : CMD_BY_CODE_A_REF,
+			&msg->msg_public.msg_code,
+			&msg->msg_model);
+	if (ret != 0) {
+		TRACE_DEBUG (FULL, "Looking for command object in the dictionary failed: %s.", strerror(ret));
+		if (ret == ENOENT)
+			ret = ENOTSUP;
+		return ret;
+	}
+	
+	/* Then process the children */
+	return _mpd_do_chain(&_C(msg)->children, recheck, 1);
+}
+
+static int _mpd_do(_msg_avp_chain_t * obj, int recheck)
+{
+	int ret = 0;
+	
+	if (!VALIDATE_OBJ(obj)) {
+		TRACE_DEBUG(FULL, "Received an invalid object. EINVAL");
+		return EINVAL;
+	}
+	
+	switch (_C(obj)->type) {
+		case _MSG_MSG:
+			return _mpd_do_msg(_M(obj), recheck);
+		
+		case _MSG_AVP:
+			return _mpd_do_avp(_A(obj), recheck, 0);
+		
+		default:
+			assert(0);
+	}
+	return EINVAL;
+}	
+	
 /***************************************************************************************************************/
 /* Parsing messages and AVP for rule compliance */
 
@@ -606,8 +1116,8 @@
 	dict_object_t * ruleavp;   /* If the rule conflicts, save it's rule_avp here (we don't have direct access to the rule) */
 } _mpr_t;
 
-/* This function is used to count the number of AVP instances of a given model */
-static void _mpr_count_avps( dict_object_t * model_avp, _msg_list_t *list, int * count, int * firstpos, int * lastpos) 
+/* This function is used to get stats on AVP instances of a given model in a chain of AVP */
+static void _mpr_stat_avps( dict_object_t * model_avp, _msg_list_t *list, int * count, int * firstpos, int * lastpos) 
 {
 	_msg_list_t * li;
 	int curpos = 0; /* The current position in the list */
@@ -646,11 +1156,9 @@
 	int ret = 0, count, first, last, min;
 	_mpr_t * _data = (_mpr_t *) data;
 	
-	TRACE_DEBUG(FULL, "enter (%p, %p)", _data, rule);
-	
 	/* Get statistics of the AVP concerned by this rule in the message instance */
-	_mpr_count_avps( rule->rule_avp, _data->sentinel, &count, &first, &last);
-	TRACE_DEBUG(FULL, "Found %d instances of the AVP, first occurrence is the %d AVP, last is %d starting from the end.", count, first, last);
+	_mpr_stat_avps( rule->rule_avp, _data->sentinel, &count, &first, &last);
+	TRACE_DEBUG(FULL, "Found %d instances, first is %d, last is %d (from end).", count, first, last);
 	
 	/* Now check the rule is not conflicting */
 	
@@ -688,14 +1196,16 @@
 				ret = EBADMSG;
 				goto end;
 			}
+			break;
 	
 		case RULE_FIXED_TAIL:
 			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *before* its fixed position */
-			if (last > rule->rule_order) {
-				TRACE_DEBUG(FULL, "Conflicting rule: the FIXED_HEAD AVP appears last in (%d) position, the rule requires (%d).", last, rule->rule_order);
+			if (last > rule->rule_order) {	/* We have a ">" here because we count in reverse order (i.e. from the end) */
+				TRACE_DEBUG(FULL, "Conflicting rule: the FIXED_TAIL AVP appears last in (%d) position, the rule requires (%d).", last, rule->rule_order);
 				ret = EBADMSG;
 				goto end;
 			}
+			break;
 		
 		default:
 			/* What is this position ??? */
@@ -714,7 +1224,7 @@
 }
 
 /* The recursive version of msg_parse_rules */
-static int _mpr_do ( void * object, dict_object_t ** rule)
+static int _mpr_do ( void * object, dict_object_t ** rule, int mandatory)
 {
 	int ret = 0;
 	_mpr_t data;
@@ -730,9 +1240,9 @@
 			return EINVAL;
 		}
 
-		/* AVP with the 'M' flag must also be recognized in the dictionary */
+		/* AVP with the 'M' flag must also be recognized in the dictionary -- except inside an optional grouped AVP */
 		if (CHECK_AVP(object) && ((model = _A(object)->avp_model) == NULL)) {
-			if ( _A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY) {
+			if ( mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) {
 				/* Return an error in this case */
 				TRACE_DEBUG(FULL, "Mandatory AVP with no dictionary model. EBADMSG");
 				return EBADMSG;
@@ -746,11 +1256,29 @@
 	
 	/* Now "model" is set and points to the object's model */
 	
+	/* If we are an AVP with no children, just return OK */
+	if (CHECK_AVP(object)) {
+		dict_avp_data_t	dictdata;
+		ret = dict_getval(model, &dictdata);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "Error in dict_getval: %s", strerror(ret));
+			return ret;
+		}
+		if (dictdata.avp_basetype != AVP_TYPE_GROUPED) {
+			/* This object has no children and no rules */
+			return 0;
+		}
+	}
+	
 	/* If this object has children, first check the rules for all its children */
 	{
+		int is_child_mand = 0;
 		_msg_list_t * ch = NULL;
+		if (  CHECK_MSG(object) 
+		   || (mandatory && (_A(object)->avp_public.avp_flags & AVP_FLAG_MANDATORY)) )
+			is_child_mand = 1;
 		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
-			ret = _mpr_do ( ch->top, rule );
+			ret = _mpr_do ( ch->top, rule, is_child_mand );
 			if (ret != 0) {
 				TRACE_DEBUG(FULL, "_msg_parse_rules failed in child %p", ch->top);
 				return ret;
@@ -764,8 +1292,19 @@
 	ret = dict_iterate_rules ( model, &data, _mpr_check_rule );
 	
 	/* Save the reference to the eventual conflicting rule; otherwise set to NULL */
-	if (rule)
-		*rule = data.ruleavp;
+	if (rule && data.ruleavp) {
+		/* data.ruleavp contains the AVP, and model is the parent */
+		int _ret = 0;
+		dict_object_t * conflictrule = NULL;
+		dict_rule_request_t req = { model, data.ruleavp };
+		
+		_ret = dict_search ( DICT_RULE, RULE_BY_AVP_AND_PARENT, &req, &conflictrule);
+		if (_ret == 0) {
+			*rule = conflictrule;
+		} else {
+			TRACE_DEBUG(FULL, "dict_search failed to find the rule: %s", strerror(_ret));
+		}
+	}
 	
 	return ret;
 }
@@ -822,15 +1361,10 @@
 	return 0;
 }
 
-/* Resolve dictionary objects for instances, from their data.
- * When the model is found, the data is interpreted from the avp_source buffer and copied to avp_storage.
- * When the model is not found, the raw data is copied and saved.
- * Therefore, after this function has been called, the source buffer can be freed.
- */
+/* Resolve dictionary objects of message instances. See _mpd_* functions in this file. */
 int msg_parse_dict ( msg_t * msg )
 {
-	TRACE_DEBUG (FULL, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	return _mpd_do(_C(msg), 0);
 }
 
 
@@ -941,7 +1475,7 @@
 	
 	new->avp_public.avp_code    = dictavpdata.avp_code;
 	new->avp_public.avp_flags   = dictavpdata.avp_flag_val;
-	new->avp_public.avp_len = GETINITIALSIZE(dictavpdata.avp_basetype, dictavpdata.avp_flag_val & AVP_FLAG_VENDOR);
+	new->avp_public.avp_len = GETINITIALSIZE(dictavpdata.avp_basetype, dictavpdata.avp_flag_val );
 	new->avp_public.avp_vendor  = dictavpdata.avp_vendor;
 	
 	/* Create the children from template if needed */
@@ -1293,8 +1827,16 @@
 	return 0;
 }
 
-/* This macro will pad a size to the next multiple of 4. */
-#define PAD4(_x) ((_x) + ( (4 - (_x)) & 3 ) )
+/* Retrieve the raw data of an AVP for which the model was not resolved. */
+int msg_avp_getrawdata ( msg_avp_t *avp, unsigned char **data )
+{
+	if ((!CHECK_AVP(avp)) || (!data)) {
+		TRACE_DEBUG(FULL, "Received an invalid reference object. EINVAL");
+		return EINVAL;
+	}
+	*data = _A(avp)->avp_rawdata;
+	return 0;
+}
 
 /* Compute the lengh of an object and its subtree. */
 int msg_update_length ( void * object )
@@ -1334,7 +1876,7 @@
 			return EINVAL;
 		}
 		
-		sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags & AVP_FLAG_VENDOR );
+		sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
 		
 		switch (dictdata.avpdata.avp_basetype) {
 			case AVP_TYPE_OCTETSTRING:
@@ -1361,7 +1903,7 @@
 		
 		/* Compute the header size first */
 		if (_C(object)->type == _MSG_AVP) 
-			sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags & AVP_FLAG_VENDOR );
+			sz = GETAVPHDRSZ( _A(object)->avp_public.avp_flags );
 		else
 			sz = GETMSGHDRSZ( );
 		
@@ -1393,14 +1935,14 @@
 	int ret = 0;
 	
 	/* Resolve the dictionary objects when missing. This also validates the object. */
-	ret = msg_parse_dict( object );
+	ret = _mpd_do( _C(object), 1 );
 	if (ret != 0) {
 		TRACE_DEBUG(FULL, "msg_parse_dict failed.");
 		return ret;
 	}
 	
 	/* Call the recursive function */
-	return _mpr_do (object, rule);
+	return _mpr_do (object, rule, 1);
 }
 
 
@@ -1410,9 +1952,12 @@
  * to and from network byte-order.
  */
 
-int msg_bufferize ( msg_t * msg, char ** buffer, int * is_routable )
+/* Create the message buffer, in network-byte order. We browse the tree twice, this could be probably improved if needed */
+int msg_bufferize ( msg_t * msg, unsigned char ** buffer, int * is_routable )
 {
 	int ret = 0;
+	unsigned char * buf = NULL;
+	size_t offset = 0;
 	
 	/* Check the parameters */
 	if ( (buffer == NULL) || (! CHECK_MSG(msg) ) ) {
@@ -1427,34 +1972,96 @@
 		return ret;
 	}
 	
-#warning "to do..."
+	/* Now allocate a buffer to store the message */
+	buf = malloc(_M(msg)->msg_public.msg_length);
+	if (!buf) {
+		log_error("Memory allocation failed: %s\n", strerror(errno));
+		TRACE_DEBUG(FULL, "malloc failed");
+		return ENOMEM;
+	}
 	
+	/* Write the message header in the buffer */
+	ret = _msg_buf_msg(buf, _M(msg)->msg_public.msg_length, &offset, _M(msg));
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Error bufferizing the header: %s", strerror(ret));
+		free(buf);
+		return ret;
+	}
+	
+	/* Write the list of AVPs */
+	ret = _msg_buf_chain(buf, _M(msg)->msg_public.msg_length, &offset, &_C(msg)->children);
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Error bufferizing the header: %s", strerror(ret));
+		free(buf);
+		return ret;
+	}
+	
+	assert(offset == _M(msg)->msg_public.msg_length); /* or the msg_update_length is buggy */
+		
 	/* To define if a message is routable, we rely on the "PXY" command flag yet. */
 	if (is_routable) {
 		*is_routable = (_M(msg)->msg_public.msg_flags & CMD_FLAG_PROXIABLE) ? 1 : 0;
 	}
 	
-	TRACE_DEBUG (FULL, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	*buffer = buf;
+	return 0;
 }
 
-int msg_parse_buffer ( char * buffer, msg_t ** msg )
+int msg_parse_buffer ( unsigned char * buffer, size_t buflen, msg_t ** msg )
 {
+	_msg_t * new = NULL;
+	int ret = 0;
+	
+	if ((!buffer) || (!msg) || (buflen < GETMSGHDRSZ())) {
+		TRACE_DEBUG(INFO, "Invalid parameter (%p, %d, %p)", buffer, buflen, msg);
+		return EINVAL;
+	}
+	if ( buffer[0] != MSG_VERSION) {
+		TRACE_DEBUG(INFO, "Invalid version in message: %d (supported: %d)", buffer[0], MSG_VERSION);
+		return EBADMSG;
+	}
+	if ( buflen != ((ntohl(*(uint32_t *)buffer)) & 0x00ffffff)) {
+		TRACE_DEBUG(INFO, "Size in message is different from buffer size: %d / %d", buflen, ((ntohl(*(uint32_t *)buffer)) & 0x00ffffff));
+		return EBADMSG;
+	}
 	
-	TRACE_DEBUG (FULL, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	/* Create a new object */
+	new = (_msg_t *) malloc (sizeof(_msg_t));
+	if (!new) {
+		log_error("Unable to allocate enough memory: %s\n", strerror(errno));
+		TRACE_DEBUG (FULL, "malloc failed.");
+		return ENOMEM;
+	}
+	
+	/* Initialize the fields */
+	init_msg(new);
+	
+	/* Now read from the buffer */
+	new->msg_public.msg_version = buffer[0];
+	new->msg_public.msg_length = ntohl(*(uint32_t *)buffer) & 0x00ffffff;
+
+	new->msg_public.msg_flags = buffer[4];
+	new->msg_public.msg_code = ntohl(*(uint32_t *)(buffer+4)) & 0x00ffffff;
+	
+	new->msg_public.msg_appl = ntohl(*(uint32_t *)(buffer+8));
+	new->msg_public.msg_hbhid = ntohl(*(uint32_t *)(buffer+12));
+	new->msg_public.msg_eteid = ntohl(*(uint32_t *)(buffer+16));
+	
+	/* Parse the AVP list */
+	ret = _mpb_list(buffer + GETMSGHDRSZ(), buflen - GETMSGHDRSZ(), &_C(new)->children);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "Failed to parse the AVP list in the buffer: %s", strerror(ret));
+		destroy_tree(_C(new));
+		return ret;
+	}
+	
+	*msg = new;
+	return 0;
 }
 
 
 
 
 
-int msg_avp_getrawdata ( msg_avp_t *avp, char **data )
-{
-	TRACE_DEBUG (FULL, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
-}
 
 
-
-
--- a/waaad/message.h	Fri Jul 11 18:28:19 2008 +0900
+++ b/waaad/message.h	Mon Jul 14 18:31:55 2008 +0900
@@ -99,6 +99,7 @@
  *
  * PARAMETERS:
  *  buffer 	: Pointer to a buffer containing a message received from the network. 
+ *  buflen	: the size in bytes of the buffer.
  *  msg		: Upon success, this points to a valid msg_t object. No AVP value is resolved in this object, nor grouped AVP.
  *
  * DESCRIPTION: 
@@ -108,10 +109,11 @@
  *
  * RETURN VALUE:
  *  0      	: The location has been written.
- *  EINVAL 	: The buffer does not contain a valid Diameter message.
  *  ENOMEM	: Unable to allocate enough memory to create the msg_t object.
+ *  EBADMSG	: The buffer does not contain a valid Diameter message.
+ *  EINVAL 	: A parameter is invalid.
  */
-int msg_parse_buffer ( char * buffer, msg_t ** msg );
+int msg_parse_buffer ( unsigned char * buffer, size_t buflen, msg_t ** msg );
 
 /*
  * FUNCTION:	msg_bufferize
@@ -133,7 +135,7 @@
  *  EINVAL 	: The buffer does not contain a valid Diameter message.
  *  ENOMEM	: Unable to allocate enough memory to create the msg_t object.
  */
-int msg_bufferize ( msg_t * msg, char ** buffer, int * is_routable );
+int msg_bufferize ( msg_t * msg, unsigned char ** buffer, int * is_routable );
 
 /*
  * FUNCTION:	msg_parse_dict
@@ -155,6 +157,7 @@
  *  0      	: The message has been fully parsed as described.
  *  EINVAL 	: the msg parameter is invalid for this operation.
  *  ENOMEM	: Unable to allocate enough memory to complete the operation.
+ *  ENOTSUP	: No dictionary definition for the command or one of the mandatory AVP.
  */
 int msg_parse_dict ( msg_t * msg );
 
--- a/waaad/tests/testmesg.c	Fri Jul 11 18:28:19 2008 +0900
+++ b/waaad/tests/testmesg.c	Mon Jul 14 18:31:55 2008 +0900
@@ -44,6 +44,7 @@
 {
 	msg_t * acr = NULL;
 	msg_avp_t * pi = NULL, *avp1, *avp2;
+	unsigned char * buf = NULL;
 	
 	/* First, initialize the daemon modules */
 	INIT_WAAAD();
@@ -549,13 +550,80 @@
 			msg_dump_walk(msg);
 			#endif
 			CHECK( 343, msgdata->msg_length );
+			
+			/* Set the application to the test application: 73566 */
+			msgdata->msg_appl = 73566;
+			
+			/* Set the hop-by-hop ID to a random value: 0x4b44b41d */
+			msgdata->msg_hbhid = 0x4b44b41d;
+			/* Set the end-to-end ID to a random value: 0xe2ee2e1d */
+			msgdata->msg_eteid = 0xe2ee2e1d;
+		}
+		
+		/* Test the msg_bufferize function */
+		{
+			
+			CHECK( 0, msg_bufferize( msg, &buf, NULL ) );
+			
+			/* Test the first bytes */
+			CHECK( 0x01, buf[0] ); /* Version */
+			CHECK( 0x00, buf[1] ); /* Length: 343 = 0x000157 */
+			CHECK( 0x01, buf[2] );
+			CHECK( 0x57, buf[3] );
+			CHECK( 0x80, buf[4] ); /* flags: only "R" is set. */
+			CHECK( 0x01, buf[5] ); /* Command code: 73573 = 0x011F65 */
+			CHECK( 0x1F, buf[6] );
+			CHECK( 0x65, buf[7] );
+			CHECK( 0x00, buf[8] ); /* App ID: 73566 = 0x00011F5E */
+			CHECK( 0x01, buf[9] ); 
+			CHECK( 0x1F, buf[10] );
+			CHECK( 0x5E, buf[11] );
+			CHECK( 0x4b, buf[12] ); /* hop-by-hop id: 0x4b44b41d */
+			CHECK( 0x44, buf[13] );
+			CHECK( 0xb4, buf[14] );
+			CHECK( 0x1d, buf[15] );
+			CHECK( 0xe2, buf[16] ); /* end-to-end id: 0xe2ee2e1d */
+			CHECK( 0xee, buf[17] );
+			CHECK( 0x2e, buf[18] );
+			CHECK( 0x1d, buf[19] );
+			
+			CHECK( 0x00, buf[20] ); /* First AVP (AVP Test - no vendor - f32) begin: code 73567 = 0x00011F5F */
+			CHECK( 0x01, buf[21] );
+			CHECK( 0x1F, buf[22] );
+			CHECK( 0x5F, buf[23] );
+			CHECK( 0x00, buf[24] ); /* flags: 0 */
+			CHECK( 0x00, buf[25] ); /* length: 12 = 0x00000c */
+			CHECK( 0x00, buf[26] );
+			CHECK( 0x0C, buf[27] );
+			/* The remaining will be tested by successful parsing... */
+		}
+		
+		/* Now free the message, we keep only the buffer. */
+		CHECK( 0, msg_free( msg, 1 ) );
+		
+	}
 	
+	/* Test the parsing of buffers */
+	{
+		msg_t * msg;
+		
+		/* Test the msg_parse_buffer function */
+		{
+			CHECK( 0, msg_parse_buffer( buf, 343, &msg) );
+			#if 0
+			msg_dump_walk(msg);
+			#endif
 		}
 		
 		/* Test the msg_parse_dict function */
 		{
-			
 			CHECK( 0, msg_parse_dict( msg ) );
+			#if 0
+			msg_dump_walk(msg);
+			#endif
+			/* Now we can free the buffer. */
+			memset(buf, 0, 343);
+			free(buf);
 		}
 		
 		/* Now test the msg_parse_rule function */
"Welcome to our mercurial repository"