Mercurial > hg > waaad
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; -} -