Mercurial > hg > waaad
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 */