changeset 79:7871aac19438

Implementation of msg_parse_rules (prototype changed), couple of bug fixes.
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 11 Jul 2008 18:05:12 +0900
parents b9bc2d2a12a6
children 4349f4ec01dd
files include/waaad/message-api.h waaad/message.c
diffstat 2 files changed, 228 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/include/waaad/message-api.h	Fri Jul 11 18:03:56 2008 +0900
+++ b/include/waaad/message-api.h	Fri Jul 11 18:05:12 2008 +0900
@@ -334,8 +334,7 @@
  * 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 also NULL on error return.
- *  avp		: if not NULL, the avp that trigged the conflict or error will be put here.
+ *		 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.
@@ -346,7 +345,7 @@
  *  EINVAL 	: the msg parameter is invalid for this operation.
  *  ENOMEM	: Unable to allocate enough memory to complete the operation.
  */
-int msg_parse_rules ( void * object, dict_object_t ** rule, msg_avp_t **avp);
+int msg_parse_rules ( void * object, dict_object_t ** rule);
 
 
 #endif /* ! IN_EXTENSION */
@@ -372,7 +371,7 @@
 	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_parse_rules)    ( void * object, dict_object_t ** rule, msg_avp_t **avp);
+	int (*msg_parse_rules)    ( void * object, dict_object_t ** rule);
 } api_msg_t;
 
 #ifdef IN_EXTENSION
--- a/waaad/message.c	Fri Jul 11 18:03:56 2008 +0900
+++ b/waaad/message.c	Fri Jul 11 18:05:12 2008 +0900
@@ -404,7 +404,7 @@
 				memset(buf, 0, sizeof(buf));
 				memcpy(buf, value->os.data, value->os.len < sizeof(buf)-1 ? value->os.len : sizeof(buf)-1 );
 				DUMP_VALUE("l:%d, v:%02.2X %02.2X %02.2X %02.2X  %02.2X %02.2X %02.2X %02.2X  "
-						   "%02.2X %02.2X %02.2X %02.2X  %02.2X %02.2X %02.2X %02.2X ... (%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c ...)", 
+						   "%02.2X %02.2X %02.2X %02.2X  %02.2X %02.2X %02.2X %02.2X ... (%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c...)", 
 						value->os.len,
 						buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], 
 						buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15], 
@@ -445,7 +445,7 @@
 	}
 }
 /* Dump an AVP value that is a constant */
-#define DUMP_CONST(_format, _parms...)   log_debug(INOBJHDR "value : t:'%s' v: %s ( " _format " )\n", INOBJHDRVAL, typename, value->enum_name, ## _parms);
+#define DUMP_CONST(_format, _parms...)   log_debug(INOBJHDR "value : t:'%s' v:'%s' ( " _format " )\n", INOBJHDRVAL, typename, value->enum_name, ## _parms);
 static void dump_constant_type(dict_type_enum_data_t * value, dict_base_type_t type, char * typename, int indent)
 {
 	switch (type) {
@@ -593,6 +593,183 @@
 			assert(0);
 	}
 }
+
+
+/***************************************************************************************************************/
+/* Parsing messages and AVP for rule compliance */
+
+/* note: the _mpr prefix stands for "msg_parse_rule" */
+
+/* Type of the "void *" pointer passed to the _msg_check_rule function by dict_iterate_rules */
+typedef struct {
+	_msg_list_t   * sentinel;  /* Sentinel of the list of children AVP */
+	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) 
+{
+	_msg_list_t * li;
+	int curpos = 0; /* The current position in the list */
+	
+	*count = 0;	/* number of instances found */
+	*firstpos = 0;	/* position of the first instance */
+	*lastpos = 0;	/* position of the last instance, starting from the end */
+	
+	for (li = list->next; li != list; li = li->next) {
+		/* Increment the current position counter */
+		curpos++;
+		
+		/* If we previously saved a "lastpos" information, increment it */
+		if (*lastpos != 0)
+			(*lastpos)++;
+		
+		/* Check the type of the next AVP. We can compare the references directly, it is safe. */
+		if (_A(li->top)->avp_model == model_avp) {
+			
+			/* This AVP is of the type we are searching */
+			(*count)++;
+			
+			/* If we don't have yet a "firstpos", save it */
+			if (*firstpos == 0)
+				*firstpos = curpos;
+			
+			/* Reset the lastpos */
+			(*lastpos) = 1;
+		}
+	}
+}
+
+/* Check that a list of AVPs is compliant with a given rule */
+static int _mpr_check_rule(void * data, dict_rule_data_t *rule)
+{
+	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);
+	
+	/* Now check the rule is not conflicting */
+	
+	/* Check the "min" value */
+	if (rule->rule_min == -1) {
+		if (rule->rule_position == RULE_OPTIONAL)
+			min = 0;
+		else
+			min = 1;
+	}
+	if (count < min) {
+		TRACE_DEBUG(FULL, "Conflicting rule: the number of occurences (%d) is < the rule min (%d).", count, min);
+		ret = EBADMSG;
+		goto end;
+	}
+	
+	/* Check the "max" value */
+	if ((rule->rule_max != -1) && (count > rule->rule_max)) {
+		TRACE_DEBUG(FULL, "Conflicting rule: the number of occurences (%d) is > the rule max (%d).", count, rule->rule_max);
+		ret = EBADMSG;
+		goto end;
+	}
+		
+	/* Check the position and order (if relevant) */
+	switch (rule->rule_position) {
+		case RULE_OPTIONAL:
+		case RULE_REQUIRED:
+			/* No special position constraints */
+			break;
+		
+		case RULE_FIXED_HEAD:
+			/* Since "0*1<fixed>" is a valid rule specifier, we only reject cases where the AVP appears *after* its fixed position */
+			if (first > rule->rule_order) {
+				TRACE_DEBUG(FULL, "Conflicting rule: the FIXED_HEAD AVP appears first in (%d) position, the rule requires (%d).", first, rule->rule_order);
+				ret = EBADMSG;
+				goto end;
+			}
+	
+		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);
+				ret = EBADMSG;
+				goto end;
+			}
+		
+		default:
+			/* What is this position ??? */
+			assert(0);
+			ret = ENOTSUP;
+	}
+	
+	/* We found no conflict, this rule is respected */
+	ret = 0;
+	
+end:
+	if (ret == EBADMSG)
+		_data->ruleavp = rule->rule_avp;
+
+	return ret;
+}
+
+/* The recursive version of msg_parse_rules */
+static int _mpr_do ( void * object, dict_object_t ** rule)
+{
+	int ret = 0;
+	_mpr_t data;
+	dict_object_t * model;
+	
+	/* object has already been checked and dict-parsed when we are called. */
+	
+	/* First, handle the cases where there is no model */
+	{
+		/* Commands MUST be supported in the dictionary */
+		if (CHECK_MSG(object) && ((model = _M(object)->msg_model) == NULL)) {
+			TRACE_DEBUG(FULL, "Message with no dictionary model. EINVAL");
+			return EINVAL;
+		}
+
+		/* AVP with the 'M' flag must also be recognized in the dictionary */
+		if (CHECK_AVP(object) && ((model = _A(object)->avp_model) == NULL)) {
+			if ( _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;
+			} else {
+				/* We don't know any rule for this object, so assume OK */
+				TRACE_DEBUG(FULL, "Unknown informational AVP, ignoring...");
+				return 0;
+			}
+		}
+	}
+	
+	/* Now "model" is set and points to the object's model */
+	
+	/* If this object has children, first check the rules for all its children */
+	{
+		_msg_list_t * ch = NULL;
+		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
+			ret = _mpr_do ( ch->top, rule );
+			if (ret != 0) {
+				TRACE_DEBUG(FULL, "_msg_parse_rules failed in child %p", ch->top);
+				return ret;
+			}
+		}
+	}
+	
+	/* Now check all rules of this object */
+	data.sentinel = &_C(object)->children;
+	data.ruleavp  = NULL;
+	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;
+	
+	return ret;
+}
+
 /***************************************************************************************************************/
 
 /* Functions exported to other files in the daemon. More information in message.h */
@@ -999,7 +1176,7 @@
 		return EINVAL;
 	}
 	
-	/* Dump the object */
+	/* copy the model reference */
 	switch (_C(reference)->type) {
 		case _MSG_AVP:
 			*model = _A(reference)->avp_model;
@@ -1021,11 +1198,13 @@
 {
 	dict_base_type_t type = -1;
 	
+	/* Check parameter */
 	if ((!CHECK_AVP(avp)) || (!_A(avp)->avp_model)) {
 		TRACE_DEBUG(FULL, "Received an invalid reference object. EINVAL");
 		return EINVAL;
 	}
 	
+	/* Retrieve information from the AVP model */
 	{
 		dict_object_type_t dicttype = -1;
 		dict_avp_data_t dictdata;
@@ -1055,6 +1234,7 @@
 	
 	memset(&_A(avp)->avp_storage, 0, sizeof(avp_value_t));
 	
+	/* If the request is to delete a value: */
 	if (!value) {
 		_A(avp)->avp_public.avp_data = NULL;
 		return 0;
@@ -1179,7 +1359,7 @@
 		for (ch = _C(object)->children.next; ch != &_C(object)->children; ch = ch->next) {
 			ret = msg_update_length ( ch->top );
 			if (ret != 0) {
-				TRACE_DEBUG(FULL, "msg_update_length failed in child %p", ch->next->top);
+				TRACE_DEBUG(FULL, "msg_update_length failed in child %p", ch->top);
 				return ret;
 			}
 			
@@ -1197,20 +1377,59 @@
 	return 0;
 }
 
+/* Check that an object instance does not break a rule */
+int msg_parse_rules ( void * object, dict_object_t ** rule)
+{
+	int ret = 0;
+	
+	/* Resolve the dictionary objects when missing. This also validates the object. */
+	ret = msg_parse_dict( object );
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "msg_parse_dict failed.");
+		return ret;
+	}
+	
+	/* Call the recursive function */
+	return _mpr_do (object, rule);
+}
+
+
+
 /**********************************************************************************************************/
 /* The following functions are used to parse messages from and to buffers. It is responsible for converting 
  * to and from network byte-order.
  */
 
-int msg_parse_buffer ( char * buffer, msg_t ** msg )
+int msg_bufferize ( msg_t * msg, char ** buffer, int * is_routable )
 {
+	int ret = 0;
+	
+	/* Check the parameters */
+	if ( (buffer == NULL) || (! CHECK_MSG(msg) ) ) {
+		TRACE_DEBUG(FULL, "Invalid parameter. EINVAL");
+		return EINVAL;
+	}
+	/* Update the length. This also checks that all AVP have their values set */
+	ret = msg_update_length(msg);
+	if (ret != 0) {
+		TRACE_DEBUG(FULL, "Error updating lengths: %s", strerror(ret));
+		return ret;
+	}
+	
+	
+	
+	/* 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;
 }
 
-int msg_bufferize ( msg_t * msg, char ** buffer, int * is_routable )
+int msg_parse_buffer ( char * buffer, msg_t ** msg )
 {
+	
 	TRACE_DEBUG (FULL, "@@@ %s: not implemented yet.", __FUNCTION__ );
 	return ENOTSUP;
 }
@@ -1233,11 +1452,5 @@
 }
 
 
-int msg_parse_rules ( void * object, dict_object_t ** rule, msg_avp_t **avp)
-{
-	TRACE_DEBUG (FULL, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
-}
 
 
-
"Welcome to our mercurial repository"