# HG changeset patch # User Sebastien Decugis # Date 1440868315 -28800 # Node ID 3f1e79e1273ef22f3fa240c716699207b72bbe89 # Parent 035f489b845b8c9b900f0bd80864d22516cf9bef Added new callbacks in the derived types definitions to improve value checks during message parsing. Thanks Ranjith for the suggestion. diff -r 035f489b845b -r 3f1e79e1273e include/freeDiameter/libfdproto.h --- a/include/freeDiameter/libfdproto.h Sat Jul 04 23:06:37 2015 +0800 +++ b/include/freeDiameter/libfdproto.h Sun Aug 30 01:11:55 2015 +0800 @@ -1258,6 +1258,28 @@ */ typedef int (*dict_avpdata_encode) (void * data, union avp_value * val); +/* + * CALLBACK: dict_avpdata_check + * + * PARAMETERS: + * val : Pointer to the AVP value that was received and needs to be sanity checked. + * data : a parameter stored in the type structure (to enable more generic check functions) + * error_msg: upon erroneous value, a string describing the error can be returned here (it will be strcpy by caller). This description will be returned in the error message, if any. + * + * DESCRIPTION: + * This callback can be provided with a derived type in order to improve the operation of the + * fd_msg_parse_dict function. When this callback is present, the value of the AVP that has + * been parsed is passed to this function for finer granularity check. For example for some + * speccific AVP, the format of an OCTETSTRING value can be further checked, or the + * interger value can be verified. + * + * RETURN VALUE: + * 0 : The value is valid. + * !0 : An error occurred, the error code is returned. It is advised to return EINVAL on incorrect val + */ +typedef int (*dict_avpdata_check) (void * data, union avp_value * val, char ** error_msg); + + /* Type to hold data associated to a derived AVP data type */ struct dict_type_data { @@ -1266,6 +1288,8 @@ dict_avpdata_interpret type_interpret;/* cb to convert the AVP value in more comprehensive format (or NULL) */ dict_avpdata_encode type_encode; /* cb to convert formatted data into an AVP value (or NULL) */ DECLARE_FD_DUMP_PROTOTYPE((*type_dump), union avp_value * val); /* cb called by fd_msg_dump_* for this type of data (if != NULL). Returned string must be freed. */ + dict_avpdata_check type_check; + void * type_check_param; }; /* The criteria for searching a type object in the dictionary */ @@ -1293,6 +1317,9 @@ DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Time_dump, union avp_value * avp_value); +/* For string AVP, the following type_check function provides simple basic check for specific characters presence, e.g. use "@." for trivial email address check */ +int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg); + /****/ diff -r 035f489b845b -r 3f1e79e1273e libfdproto/dictionary_functions.c --- a/libfdproto/dictionary_functions.c Sat Jul 04 23:06:37 2015 +0800 +++ b/libfdproto/dictionary_functions.c Sun Aug 30 01:11:55 2015 +0800 @@ -358,3 +358,30 @@ return *buf; } +/* Check that a given AVP value contains all the characters from data in the same order */ +static char error_message[80]; +int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg) +{ + char * inChar = data; + char * inData = (char *)val->os.data; + int i = 0; + CHECK_PARAMS(data); + while (*inChar != '\0') { + while (i < val->os.len) { + if (*inChar == inData[i++]) { + inChar++; + break; + } + } + if (i >= val->os.len) + break; + } + if (*inChar == '\0') + return 0; + + if (error_msg) { + snprintf(error_message, sizeof(error_message), "Could not find '%c' in AVP", *inChar); + *error_msg = error_message; + } + return EBADMSG; +} diff -r 035f489b845b -r 3f1e79e1273e libfdproto/messages.c --- a/libfdproto/messages.c Sat Jul 04 23:06:37 2015 +0800 +++ b/libfdproto/messages.c Sun Aug 30 01:11:55 2015 +0800 @@ -2036,6 +2036,8 @@ static int parsedict_do_avp(struct dictionary * dict, struct avp * avp, int mandatory, struct fd_pei *error_info) { struct dict_avp_data dictdata; + struct dict_type_data derivedtypedata; + struct dict_object * avp_derived_type = NULL; uint8_t * source; TRACE_ENTRY("%p %p %d %p", dict, avp, mandatory, error_info); @@ -2218,6 +2220,33 @@ } + /* Is there a derived type check function ? */ + CHECK_FCT ( fd_dict_search ( dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &avp_derived_type, 0) ); + if (avp_derived_type) { + CHECK_FCT( fd_dict_getval(avp_derived_type, &derivedtypedata) ); + if (derivedtypedata.type_check != NULL) { + char * err; + int ret = (*derivedtypedata.type_check)( derivedtypedata.type_check_param, &avp->avp_storage, &err ); + + if (ret != 0) { + TRACE_DEBUG(INFO, "The AVP failed to pass the dictionary validation"); + if (error_info) { + error_info->pei_errcode = "DIAMETER_INVALID_AVP_VALUE"; + error_info->pei_avp = avp; + strncpy(error_message, err, sizeof(error_message)); + error_info->pei_message = error_message; + } else { + char * buf = NULL; + size_t buflen; + CHECK_MALLOC(fd_msg_dump_treeview(&buf, &buflen, NULL, avp, NULL, 0, 0)); + LOG_E("Invalid AVP: %s", buf); + free(buf); + } + return ret; /* should we just return EBADMSG? */ + } + } + } + /* The value is now set, so set the data pointer and return 0 */ avp->avp_public.avp_value = &avp->avp_storage; return 0; diff -r 035f489b845b -r 3f1e79e1273e tests/testmesg.c --- a/tests/testmesg.c Sat Jul 04 23:06:37 2015 +0800 +++ b/tests/testmesg.c Sun Aug 30 01:11:55 2015 +0800 @@ -308,6 +308,15 @@ fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, gavp)); #endif } + + { + struct dict_object * type = NULL; + struct dict_type_data type_data = { AVP_TYPE_OCTETSTRING, "OS test2", NULL, NULL, NULL, fd_dictfct_CharInOS_check, "@." }; + struct dict_avp_data avp_data = { 73575, 73565, "AVP Test - os2", AVP_FLAG_VENDOR, AVP_FLAG_VENDOR, AVP_TYPE_OCTETSTRING }; + CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_TYPE, &type_data , NULL, &type ) ); + CHECK( 0, fd_dict_new ( fd_g_config->cnf_dict, DICT_AVP, &avp_data , type, NULL ) ); + } + #if 0 { fd_log_debug("%s", fd_dict_dump_object(FD_DUMP_TEST_PARAMS, vendor)); @@ -754,6 +763,48 @@ CHECK( 0, fd_msg_free ( msg ) ); } + /* Test with a type verifier */ + { + struct fd_pei error_info; + CPYBUF(); + buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */ + + /* Check that we cannot support this message now */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) ); + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + + CPYBUF(); + buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */ + + /* Check error reporting works */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( EBADMSG, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, &error_info ) ); + + #if 1 + fd_log_debug("Error reported: %s\n in AVP: %s", error_info.pei_message, fd_msg_dump_treeview(FD_DUMP_TEST_PARAMS, error_info.pei_avp, fd_g_config->cnf_dict, 0, 1)); + #endif + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + + CPYBUF(); + buf_cpy[103] = 0x67; /* Replaced AVP code = 0x00011F67, OS test2 type in the dictionary */ + buf_cpy[130] = '@'; + buf_cpy[140] = '.'; /* now we comply to the constraints */ + + /* Check that we cannot support this message now */ + CHECK( 0, fd_msg_parse_buffer( &buf_cpy, 344, &msg) ); + CHECK( 0, fd_msg_parse_dict( msg, fd_g_config->cnf_dict, NULL ) ); + + /* reset */ + CHECK( 0, fd_msg_free ( msg ) ); + + + } + { unsigned char * buftmp = NULL; struct msg * error; @@ -1398,7 +1449,6 @@ } } - /* That's all for the tests yet */ PASSTEST(); }