Mercurial > hg > waaad
changeset 111:f5d811b6e2e2
Added callbacks to interpret derived types AVP values in more convenient formats
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Wed, 30 Jul 2008 14:58:28 +0900 |
parents | 2fb02c5fdf16 |
children | 51308bdd3c04 |
files | include/waaad/dictionary-api.h include/waaad/message-api.h waaad/dict-base.c waaad/message.c waaad/tests/testmesg.c |
diffstat | 5 files changed, 550 insertions(+), 75 deletions(-) [+] |
line wrap: on
line diff
--- a/include/waaad/dictionary-api.h Tue Jul 29 16:11:07 2008 +0900 +++ b/include/waaad/dictionary-api.h Wed Jul 30 14:58:28 2008 +0900 @@ -362,6 +362,20 @@ *************************************************************************** */ +/* Type to store any AVP value */ +typedef union { + struct { + unsigned char * data; /* bytes buffer */ + size_t len; /* length of the data buffer */ + } os; /* Storage for an octet string, data is alloc'd and must be freed */ + int32_t i32; /* integer 32 */ + int64_t i64; /* integer 64 */ + uint32_t u32; /* unsigned 32 */ + uint64_t u64; /* unsigned 64 */ + float f32; /* float 32 */ + double f64; /* float 64 */ +} avp_value_t; + /* These are the basic AVP types defined in RFC3588bis */ typedef enum { AVP_TYPE_GROUPED, @@ -375,10 +389,52 @@ #define AVP_TYPE_MAX AVP_TYPE_FLOAT64 } dict_base_type_t; +/* Callbacks that can be associated with a derived type to further interpret the values. */ +/* + * CALLBACK: type_data_interpret + * + * PARAMETERS: + * avp_value : Pointer to the AVP value that must be interpreted. + * interpreted : The result of interpretation is stored here. The format and meaning depends on each type. + * + * DESCRIPTION: + * This callback can be provided with a derived type in order to facilitate the interpretation of formated data. + * For example, when an AVP of type "Address" is received, it can be used to convert the octetstring into a struct sockaddr. + * This callback is usually not called directly, but through the message's API msg_avp_value_interpret function. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : An error occurred, the error code is returned. + */ +typedef int (*type_data_interpret) (avp_value_t * avp_value, void * interpreted); +/* + * CALLBACK: type_data_encode + * + * PARAMETERS: + * data : The formated data that must be stored in the AVP value. + * avp_value : Pointer to the AVP value storage area where the data must be stored. + * + * DESCRIPTION: + * This callback can be provided with a derived type in order to facilitate the encoding of formated data. + * For example, it can be used to convert a struct sockaddr in an AVP value of type Address. + * This callback is usually not called directly, but through the message's API msg_avp_value_encode function. + * If the callback is defined for an OctetString based type, the created string must be malloc'd. free will be called + * automatically later. + * + * RETURN VALUE: + * 0 : Operation complete. + * !0 : An error occurred, the error code is returned. + */ +typedef int (*type_data_encode) (void * data, avp_value_t * avp_value); + + + /* Type to hold data associated to a derived AVP data type */ typedef struct { dict_base_type_t type_base; /* How the data of such AVP must be interpreted */ char *type_name; /* The name of this application */ + type_data_interpret type_interpret;/* Convert the AVP value in more comprehensive format */ + type_data_encode type_encode; /* Convert formatted data into an AVP value */ } dict_type_data_t; /* The criteria for searching a type object in the dictionary */ @@ -428,20 +484,6 @@ *************************************************************************** */ -/* Type to store any AVP value */ -typedef union { - struct { - unsigned char * data; /* bytes buffer */ - size_t len; /* length of the data buffer */ - } os; /* Storage for an octet string, data is alloc'd and must be freed */ - int32_t i32; /* integer 32 */ - int64_t i64; /* integer 64 */ - uint32_t u32; /* unsigned 32 */ - uint64_t u64; /* unsigned 64 */ - float f32; /* float 32 */ - double f64; /* float 64 */ -} avp_value_t; - /* Type to hold data of named constants for AVP values */ typedef struct { char *enum_name; /* The name of this constant */
--- a/include/waaad/message-api.h Tue Jul 29 16:11:07 2008 +0900 +++ b/include/waaad/message-api.h Wed Jul 30 14:58:28 2008 +0900 @@ -250,6 +250,43 @@ int msg_avp_setvalue ( msg_avp_t *avp, avp_value_t *value ); /* + * FUNCTION: msg_avp_value_encode + * + * PARAMETERS: + * avp : Pointer to a valid avp object with a NULL avp_data. The model must be known. + * data : Pointer to the data that must be encoded as AVP value and stored in the AVP. + * This is only valid for AVPs of a derived type and which type_data_encode callback is set. + * + * DESCRIPTION: + * Initialize the avp_data field of an AVP object from formatted data, using the AVP's type encode function. + * + * RETURN VALUE: + * 0 : The avp_data has been set. + * EINVAL : A parameter is invalid. + * ENOTSUP : There is no appropriate callback registered with this AVP's type. + */ +int msg_avp_value_encode ( void *data, msg_avp_t *avp ); + +/* + * FUNCTION: msg_avp_value_interpret + * + * PARAMETERS: + * avp : Pointer to a valid avp object with a non-NULL avp_data value. + * data : Upon success, formatted interpretation of the AVP value is stored here. + * + * DESCRIPTION: + * Interpret the content of an AVP of Derived type and store the result in data pointer. The real type + * of the data pointer is dependent on the AVP type. This function calls the type_data_interpret callback + * of the type. + * + * RETURN VALUE: + * 0 : The avp_data has been set. + * EINVAL : A parameter is invalid. + * ENOTSUP : There is no appropriate callback registered with this AVP's type. + */ +int msg_avp_value_interpret ( msg_avp_t *avp, void *data ); + +/* * FUNCTION: msg_avp_add * * PARAMETERS: @@ -360,18 +397,20 @@ int version; /* The version of this API/ABI, must be WAAAD_API_MSG_VER */ /* the remaining is message-specific */ - int (*msg_browse) ( void * reference, msg_dir_t dir, void ** found, int * depth ); - int (*msg_model) ( void * reference, dict_object_t ** model ); - int (*msg_data) ( msg_t *msg, msg_data_t **pdata ); - int (*msg_avp_data) ( msg_avp_t *avp, msg_avp_data_t **pdata ); - int (*msg_new) ( dict_object_t * model, int flags, msg_t ** msg ); - int (*msg_avp_new) ( dict_object_t * model, int flags, msg_avp_t ** avp ); - int (*msg_avp_setvalue) ( msg_avp_t *avp, avp_value_t *value ); - 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, unsigned char **data ); - int (*msg_parse_rules) ( void * object, dict_object_t ** rule); + int (*msg_browse) ( void * reference, msg_dir_t dir, void ** found, int * depth ); + int (*msg_model) ( void * reference, dict_object_t ** model ); + int (*msg_data) ( msg_t *msg, msg_data_t **pdata ); + int (*msg_avp_data) ( msg_avp_t *avp, msg_avp_data_t **pdata ); + int (*msg_new) ( dict_object_t * model, int flags, msg_t ** msg ); + int (*msg_avp_new) ( dict_object_t * model, int flags, msg_avp_t ** avp ); + int (*msg_avp_setvalue) ( msg_avp_t *avp, avp_value_t *value ); + int (*msg_avp_value_encode) ( void *data, msg_avp_t *avp ); + int (*msg_avp_value_interpret) ( msg_avp_t *avp, void *data ); + 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, unsigned char **data ); + int (*msg_parse_rules) ( void * object, dict_object_t ** rule); } api_msg_t; #ifdef IN_EXTENSION @@ -384,18 +423,20 @@ #endif /* DECLARE_API_POINTERS */ /* These defines allow to call functions from extension in the same way as in waaad */ -#define msg_browse g_api_msg->msg_browse -#define msg_model g_api_msg->msg_model -#define msg_data g_api_msg->msg_data -#define msg_avp_data g_api_msg->msg_avp_data -#define msg_new g_api_msg->msg_new -#define msg_avp_new g_api_msg->msg_avp_new -#define msg_avp_setvalue g_api_msg->msg_avp_setvalue -#define msg_avp_add g_api_msg->msg_avp_add -#define msg_free g_api_msg->msg_free -#define msg_update_length g_api_msg->msg_update_length -#define msg_avp_getrawdata g_api_msg->msg_avp_getrawdata -#define msg_parse_rules g_api_msg->msg_parse_rules +#define msg_browse g_api_msg->msg_browse +#define msg_model g_api_msg->msg_model +#define msg_data g_api_msg->msg_data +#define msg_avp_data g_api_msg->msg_avp_data +#define msg_new g_api_msg->msg_new +#define msg_avp_new g_api_msg->msg_avp_new +#define msg_avp_setvalue g_api_msg->msg_avp_setvalue +#define msg_avp_value_encode g_api_msg->msg_avp_value_encode +#define msg_avp_value_interpret g_api_msg->msg_avp_value_interpret +#define msg_avp_add g_api_msg->msg_avp_add +#define msg_free g_api_msg->msg_free +#define msg_update_length g_api_msg->msg_update_length +#define msg_avp_getrawdata g_api_msg->msg_avp_getrawdata +#define msg_parse_rules g_api_msg->msg_parse_rules #else /* IN_EXTENSION */ @@ -406,22 +447,24 @@ # error "You must update API_INIT_MSG also" # endif -#define API_INIT_MSG( api_msg ) \ -{ \ - (api_msg).length = sizeof(api_msg_t); \ - (api_msg).version = WAAAD_API_MSG_VER; \ - (api_msg).msg_browse = msg_browse; \ - (api_msg).msg_model = msg_model; \ - (api_msg).msg_data = msg_data; \ - (api_msg).msg_avp_data = msg_avp_data; \ - (api_msg).msg_new = msg_new; \ - (api_msg).msg_avp_new = msg_avp_new; \ - (api_msg).msg_avp_setvalue = msg_avp_setvalue; \ - (api_msg).msg_avp_add = msg_avp_add; \ - (api_msg).msg_free = msg_free; \ - (api_msg).msg_update_length = msg_update_length; \ - (api_msg).msg_avp_getrawdata = msg_avp_getrawdata; \ - (api_msg).msg_parse_rules = msg_parse_rules; \ +#define API_INIT_MSG( api_msg ) \ +{ \ + (api_msg).length = sizeof(api_msg_t); \ + (api_msg).version = WAAAD_API_MSG_VER; \ + (api_msg).msg_browse = msg_browse; \ + (api_msg).msg_model = msg_model; \ + (api_msg).msg_data = msg_data; \ + (api_msg).msg_avp_data = msg_avp_data; \ + (api_msg).msg_new = msg_new; \ + (api_msg).msg_avp_new = msg_avp_new; \ + (api_msg).msg_avp_setvalue = msg_avp_setvalue; \ + (api_msg).msg_avp_value_encode = msg_avp_value_encode; \ + (api_msg).msg_avp_value_interpret = msg_avp_value_interpret; \ + (api_msg).msg_avp_add = msg_avp_add; \ + (api_msg).msg_free = msg_free; \ + (api_msg).msg_update_length = msg_update_length; \ + (api_msg).msg_avp_getrawdata = msg_avp_getrawdata; \ + (api_msg).msg_parse_rules = msg_parse_rules; \ } #endif /* IN_EXTENSION */
--- a/waaad/dict-base.c Tue Jul 29 16:11:07 2008 +0900 +++ b/waaad/dict-base.c Wed Jul 30 14:58:28 2008 +0900 @@ -42,6 +42,138 @@ #include "waaad-internal.h" #include <string.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <errno.h> + +/* The functions to encode and interpret the derived types defined in the base protocol */ + +/* Address AVP <-> struct sockaddr_storage */ +static int Address_encode(void * data, avp_value_t * avp_value) +{ + struct sockaddr_storage * ss = (struct sockaddr_storage *) data; + uint16_t AddressType = 0; + size_t size = 0; + unsigned char * buf = NULL; + + TRACE_ENTRY("%p %p", data, avp_value); + + if (!data || !avp_value) { + TRACE_DEBUG(INFO, "Received invalid parameter"); + return EINVAL; + } + + switch (ss->ss_family) { + case AF_INET: + { + /* We are encoding a IP address */ + struct sockaddr_in * sin = (struct sockaddr_in *)ss; + + AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */ + size = 6; /* 2 for AddressType + 4 for data */ + + buf = malloc(size); + if (!buf) { + log_error("Failed to allocated memory: %s\n", strerror(errno)); + TRACE_DEBUG(INFO, "malloc failed"); + return ENOMEM; + } + + /* Do we have to change order? */ + /* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */ + memcpy(buf + 2, &sin->sin_addr.s_addr, 4); + } + break; + + case AF_INET6: + { + /* We are encoding a IP address */ + struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)ss; + + AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */ + size = 18; /* 2 for AddressType + 16 for data */ + + buf = malloc(size); + if (!buf) { + log_error("Failed to allocated memory: %s\n", strerror(errno)); + TRACE_DEBUG(INFO, "malloc failed"); + return ENOMEM; + } + + /* The order is already good here */ + memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16); + } + break; + + default: + TRACE_DEBUG(INFO, "Unsupported address familly, EINVAL"); + return EINVAL; + } + + *(uint16_t *)buf = htons(AddressType); + + avp_value->os.len = size; + avp_value->os.data = buf; + return 0; +} + +static int Address_interpret(avp_value_t * avp_value, void * interpreted) +{ + uint16_t AddressType = 0; + size_t size = 0; + unsigned char * buf; + + TRACE_ENTRY("%p %p", avp_value, interpreted); + + if (!avp_value || !interpreted || (avp_value->os.len < 2)) { + TRACE_DEBUG(INFO, "Received invalid parameter"); + return EINVAL; + } + + AddressType = ntohs(*(uint16_t *)avp_value->os.data); + buf = &avp_value->os.data[2]; + + switch (AddressType) { + case 1 /* IP */: + { + struct sockaddr_in * sin = (struct sockaddr_in *)interpreted; + + if (avp_value->os.len != 6) { + TRACE_DEBUG(INFO, "Received invalid buffer size (%d instead of %d)", avp_value->os.len, 6); + return EINVAL; + } + + sin->sin_family = AF_INET; + /* Do we have to switch order here? */ + /* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */ + memcpy(&sin->sin_addr.s_addr, buf, 4); + } + break; + + case 2 /* IP6 */: + { + struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)interpreted; + + if (avp_value->os.len != 18) { + TRACE_DEBUG(INFO, "Received invalid buffer size (%d instead of %d)", avp_value->os.len, 18); + return EINVAL; + } + + sin6->sin6_family = AF_INET6; + /* Stored in network byte order in the sin6 */ + memcpy(&sin6->sin6_addr.s6_addr, buf, 16); + } + break; + + default: + TRACE_DEBUG(INFO, "Unsupported AddressType, EINVAL"); + return EINVAL; + } + + return 0; +} + + #define CHECK_dict_new( _type, _data, _parent, _ref ) { \ int __ret = dict_new( (_type), (_data), (_parent), (_ref)); \ @@ -125,7 +257,7 @@ defined in [IANAADFAM]. The AddressType is used to discriminate the content and format of the remaining octets. */ - dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "Address" }; + dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "Address" , Address_interpret , Address_encode }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -144,7 +276,7 @@ SNTP [RFC4330] describes a procedure to extend the time to 2104. This procedure MUST be supported by all DIAMETER nodes. */ - dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "Time" }; + dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "Time" , NULL , NULL }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -182,7 +314,7 @@ Note that the AVP Length field of an UTF8String is measured in octets, not characters. */ - dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "UTF8String" }; + dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "UTF8String" , NULL , NULL }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -211,7 +343,7 @@ interactions between the Diameter protocol and Internationalized Domain Name (IDNs). */ - dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity" }; + dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity" , NULL , NULL }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -266,7 +398,7 @@ aaa://host.example.com:6666;transport=tcp;protocol=diameter aaa://host.example.com:1813;transport=udp;protocol=radius */ - dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "DiameterURI" }; + dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "DiameterURI" , NULL , NULL }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -328,7 +460,7 @@ supplied rules, for example to protect the access device owner's infrastructure. */ - dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "IPFilterRule" }; + dict_type_data_t data = { AVP_TYPE_OCTETSTRING, "IPFilterRule" , NULL , NULL }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } } @@ -483,7 +615,7 @@ with above result code SHOULD NOT attempt reconnection. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Disconnect-Cause)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Disconnect-Cause)" , NULL, NULL}; dict_type_enum_data_t t_0 = { "REBOOTING", { .i32 = 0 }}; dict_type_enum_data_t t_1 = { "BUSY", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "DO_NOT_WANT_TO_TALK_TO_YOU", { .i32 = 2 }}; @@ -763,7 +895,7 @@ */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Inband-Security-Id)" }; + dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Inband-Security-Id)" , NULL, NULL}; dict_type_enum_data_t t_0 = { "NO_INBAND_SECURITY", { .u32 = 0 }}; dict_type_enum_data_t t_1 = { "TLS", { .u32 = 1 }}; dict_avp_data_t data = { @@ -969,7 +1101,7 @@ 6. ALL_HOST */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Redirect-Host-Usage)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Redirect-Host-Usage)" , NULL, NULL}; dict_type_enum_data_t t_0 = { "DONT_CACHE", { .i32 = 0 }}; dict_type_enum_data_t t_1 = { "ALL_SESSION", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "ALL_REALM", { .i32 = 2 }}; @@ -1057,7 +1189,7 @@ * This is the reason for the "*" in the type name */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Result-Code)" }; + dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Result-Code)" , NULL, NULL}; dict_avp_data_t data = { 268, /* Code */ 0, /* Vendor */ @@ -1558,7 +1690,7 @@ * This is the reason for the "*" in the type name. Vendors will have to define their values. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Experimental-Result-Code)" }; + dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Experimental-Result-Code)" , NULL, NULL}; dict_avp_data_t data = { 298, /* Code */ 0, /* Vendor */ @@ -1605,7 +1737,7 @@ offered. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Auth-Request-Type)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Auth-Request-Type)" , NULL, NULL}; dict_type_enum_data_t t_1 = { "AUTHENTICATE_ONLY", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "AUTHORIZE_ONLY", { .i32 = 2 }}; dict_type_enum_data_t t_3 = { "AUTHORIZE_AUTHENTICATE", { .i32 = 3 }}; @@ -1744,7 +1876,7 @@ Authorization-Lifetime. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Auth-Session-State)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Auth-Session-State)" , NULL, NULL}; dict_type_enum_data_t t_0 = { "STATE_MAINTAINED", { .i32 = 0 }}; dict_type_enum_data_t t_1 = { "NO_STATE_MAINTAINED", { .i32 = 1 }}; dict_avp_data_t data = { @@ -1787,7 +1919,7 @@ expiration of the Authorization-Lifetime. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Re-Auth-Request-Type)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Re-Auth-Request-Type)" , NULL, NULL}; dict_type_enum_data_t t_0 = { "AUTHORIZE_ONLY", { .i32 = 0 }}; dict_type_enum_data_t t_1 = { "AUTHORIZE_AUTHENTICATE", { .i32 = 1 }}; dict_avp_data_t data = { @@ -1914,7 +2046,7 @@ The user's session has timed out, and service has been terminated. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Termination-Cause)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Termination-Cause)" , NULL, NULL}; dict_type_enum_data_t t_1 = { "DIAMETER_LOGOUT", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "DIAMETER_SERVICE_NOT_PROVIDED", { .i32 = 2 }}; dict_type_enum_data_t t_3 = { "DIAMETER_BAD_ANSWER", { .i32 = 3 }}; @@ -2026,7 +2158,7 @@ */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Session-Binding)" }; + dict_type_data_t tdata = { AVP_TYPE_UNSIGNED32, "Enumerated*(Session-Binding)" , NULL, NULL}; dict_type_enum_data_t t_1 = { "RE_AUTH", { .u32 = 1 }}; dict_type_enum_data_t t_2 = { "STR", { .u32 = 2 }}; dict_type_enum_data_t t_4 = { "ACCOUNTING", { .u32 = 4 }}; @@ -2088,7 +2220,7 @@ session. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Session-Server-Failover)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Session-Server-Failover)" , NULL, NULL}; dict_type_enum_data_t t_0 = { "REFUSE_SERVICE", { .i32 = 0 }}; dict_type_enum_data_t t_1 = { "TRY_AGAIN", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "ALLOW_SERVICE", { .i32 = 2 }}; @@ -2220,7 +2352,7 @@ the existing session. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Accounting-Record-Type)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Accounting-Record-Type)" , NULL, NULL}; dict_type_enum_data_t t_1 = { "EVENT_RECORD", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "START_RECORD", { .i32 = 2 }}; dict_type_enum_data_t t_3 = { "INTERIM_RECORD", { .i32 = 3 }}; @@ -2414,7 +2546,7 @@ stored. */ dict_object_t * type; - dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Accounting-Realtime-Required)" }; + dict_type_data_t tdata = { AVP_TYPE_INTEGER32, "Enumerated(Accounting-Realtime-Required)" , NULL, NULL}; dict_type_enum_data_t t_1 = { "DELIVER_AND_GRANT", { .i32 = 1 }}; dict_type_enum_data_t t_2 = { "GRANT_AND_STORE", { .i32 = 2 }}; dict_type_enum_data_t t_3 = { "GRANT_AND_LOSE", { .i32 = 3 }};
--- a/waaad/message.c Tue Jul 29 16:11:07 2008 +0900 +++ b/waaad/message.c Wed Jul 30 14:58:28 2008 +0900 @@ -1871,6 +1871,142 @@ return 0; } +/* Set the value of an AVP, using formatted data */ +int msg_avp_value_encode ( void *data, msg_avp_t *avp ) +{ + dict_base_type_t type = -1; + dict_type_data_t type_data; + int ret = 0; + + TRACE_ENTRY("%p %p", data, avp); + + /* Check parameter */ + if ((!CHECK_AVP(avp)) || (!_A(avp)->avp_model)) { + TRACE_DEBUG(INFO, "Received an invalid reference object. EINVAL"); + return EINVAL; + } + + /* Retrieve information from the AVP model and it's parent type */ + { + dict_object_type_t dicttype = -1; + dict_avp_data_t dictdata; + dict_object_t * parenttype = NULL; + + /* First check the base type of the AVP */ + ret = dict_gettype(_A(avp)->avp_model, &dicttype); + if (ret || (dicttype != DICT_AVP)) { + TRACE_DEBUG(INFO, "Received an invalid reference object. EINVAL"); + return EINVAL; + } + ret = dict_getval(_A(avp)->avp_model, &dictdata); + if (ret != 0) { + TRACE_DEBUG(INFO, "Error getting avp model data"); + return ret; + } + type = dictdata.avp_basetype; + if (type == AVP_TYPE_GROUPED) { + TRACE_DEBUG(INFO, "Cannot set a value to a grouped AVP. EINVAL"); + return EINVAL; + } + + /* Then retrieve information about the parent's type (= derived type) */ + ret = dict_search(DICT_TYPE, TYPE_OF_AVP, _A(avp)->avp_model, &parenttype); + if (ret != 0) { + TRACE_DEBUG(INFO, "Error in dict_search: %s", strerror(ret)); + return ret; + } + if (parenttype == NULL) { + TRACE_DEBUG(INFO, "This AVP is not of derived type, cannot encode the data."); + return EINVAL; + } + ret = dict_getval(parenttype, &type_data); + if (ret != 0) { + TRACE_DEBUG(INFO, "Error getting type model data"); + return ret; + } + if (type_data.type_encode == NULL) { + TRACE_DEBUG(INFO, "This AVP type does not provide a callback to encode formatted data. ENOTSUP."); + return ENOTSUP; + } + } + + /* Ok, now we can encode the value */ + + /* First, clean any previous value */ + if (_A(avp)->avp_mustfreeos != 0) { + free(_A(avp)->avp_storage.os.data); + _A(avp)->avp_mustfreeos = 0; + } + _A(avp)->avp_public.avp_data = NULL; + memset(&_A(avp)->avp_storage, 0, sizeof(avp_value_t)); + + /* Now call the type's callback to encode the data */ + ret = (*type_data.type_encode)(data, &_A(avp)->avp_storage); + if (ret != 0) { + TRACE_DEBUG(INFO, "Failed to encode the data: %s", strerror(ret)); + return ret; + } + + /* If an octetstring has been allocated, let's mark it to be freed */ + if (type == AVP_TYPE_OCTETSTRING) + _A(avp)->avp_mustfreeos = 1; + + /* Set the data pointer of the public part */ + _A(avp)->avp_public.avp_data = &_A(avp)->avp_storage; + + return 0; +} + +/* Interpret the value of an AVP into formatted data */ +int msg_avp_value_interpret ( msg_avp_t *avp, void *data ) +{ + dict_base_type_t type = -1; + dict_type_data_t type_data; + int ret = 0; + + TRACE_ENTRY("%p %p", avp, data); + + /* Check parameter */ + if ((!CHECK_AVP(avp)) || (!_A(avp)->avp_model) || (!_A(avp)->avp_public.avp_data)) { + TRACE_DEBUG(INFO, "Received an invalid AVP object. EINVAL"); + return EINVAL; + } + + /* Retrieve information about the AVP parent type */ + { + dict_object_t * parenttype = NULL; + + ret = dict_search(DICT_TYPE, TYPE_OF_AVP, _A(avp)->avp_model, &parenttype); + if (ret != 0) { + TRACE_DEBUG(INFO, "Error in dict_search: %s", strerror(ret)); + return ret; + } + if (parenttype == NULL) { + TRACE_DEBUG(INFO, "This AVP is not of derived type, cannot interpret the data."); + return EINVAL; + } + ret = dict_getval(parenttype, &type_data); + if (ret != 0) { + TRACE_DEBUG(INFO, "Error getting type model data"); + return ret; + } + if (type_data.type_interpret == NULL) { + TRACE_DEBUG(INFO, "This AVP type does not provide a callback to interpret value in formatted data. ENOTSUP."); + return ENOTSUP; + } + } + + /* Ok, now we can interpret the value */ + + ret = (*type_data.type_interpret)(_A(avp)->avp_public.avp_data, data); + if (ret != 0) { + TRACE_DEBUG(INFO, "Failed to interpret the data: %s", strerror(ret)); + return ret; + } + + return 0; +} + /* Retrieve the address of the msg_public field of a message */ int msg_data ( msg_t *msg, msg_data_t **pdata ) {
--- a/waaad/tests/testmesg.c Tue Jul 29 16:11:07 2008 +0900 +++ b/waaad/tests/testmesg.c Wed Jul 30 14:58:28 2008 +0900 @@ -34,7 +34,8 @@ *********************************************************************************************************/ #include "tests.h" - +#include <netinet/in.h> +#include <arpa/inet.h> extern void dump_dict_object ( void * obj, int parents, int depth, int indent ); @@ -122,7 +123,7 @@ CHECK( 0, diff); } - /* Check that there are no AVP after the prox-info */ + /* Check that there are no AVP after the proxy-info */ CHECK( ENOENT, msg_browse ( pi, MSG_BRW_NEXT, &avp2, NULL) ); /* Test the msg_avp_del function unlinks the object properly */ @@ -833,6 +834,127 @@ } } } + + /* Test the msg_avp_value_interpret and msg_avp_value_encode functions. use the Address type and Host-IP-Address AVPs */ + { + dict_object_t * cer_model = NULL; + msg_t * cer = NULL; + + dict_object_t * hia_model = NULL; + msg_avp_t *avp4, *avp6; + #define TEST_IP4 "192.168.100.101" + char buf4[INET_ADDRSTRLEN]; + #define TEST_IP6 "1111:2222:3333:4444:1234:5678:9abc:def0" + char buf6[INET6_ADDRSTRLEN]; + + struct sockaddr_storage ss; + struct sockaddr_in sin, *psin; + struct sockaddr_in6 sin6, *psin6; + + /* Find the CER dictionary object */ + CHECK( 0, dict_search ( DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &cer_model ) ); + + /* Now find the Host-IP-Address dictionary object */ + CHECK( 0, dict_search ( DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &hia_model ) ); + + /* Create the msg instance */ + CHECK( 0, msg_new ( cer_model, 0, &cer ) ); + + /* Create the avp instances */ + CHECK( 0, msg_avp_new ( hia_model, 0, &avp4 ) ); + CHECK( 0, msg_avp_new ( hia_model, 0, &avp6 ) ); + + /* Set the value of the IP avp */ + sin.sin_family = AF_INET; + CHECK( 1, inet_pton( AF_INET, TEST_IP4, &sin.sin_addr.s_addr ) ); + CHECK( 0, msg_avp_value_encode ( &sin, avp4 ) ); + + /* Set the value of the IP6 avp */ + sin6.sin6_family = AF_INET6; + CHECK( 1, inet_pton( AF_INET6, TEST_IP6, &sin6.sin6_addr.s6_addr ) ); + CHECK( 0, msg_avp_value_encode ( &sin6, avp6 ) ); + + /* Add these AVPs in the message */ + CHECK( 0, msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp4) ); + CHECK( 0, msg_avp_add( cer, MSG_BRW_LAST_CHILD, avp6) ); + + /* Create the buffer for this message */ + CHECK( 0, msg_bufferize( cer, &buf, NULL ) ); + + /* Now free the message, we keep only the buffer. */ + CHECK( 0, msg_free( cer, 1 ) ); + + /* Check the content of the buffer is correct (skip command header) */ + CHECK( 0x00, buf[20] ); /* First AVP (IP4) begins: code 257 = 0x00000101 */ + CHECK( 0x00, buf[21] ); + CHECK( 0x01, buf[22] ); + CHECK( 0x01, buf[23] ); + CHECK( 0x40, buf[24] ); /* flags: M */ + CHECK( 0x00, buf[25] ); /* length: 8+6 = 0x00000e */ + CHECK( 0x00, buf[26] ); + CHECK( 0x0E, buf[27] ); + CHECK( 0x00, buf[28] ); /* Value: AddressType 1 */ + CHECK( 0x01, buf[29] ); + CHECK( 192, buf[30] ); /* 192.168.100.101 */ + CHECK( 168, buf[31] ); + CHECK( 100, buf[32] ); + CHECK( 101, buf[33] ); + + CHECK( 0x00, buf[34] ); /* Padding */ + CHECK( 0x00, buf[35] ); + + CHECK( 0x00, buf[36] ); /* Second AVP (IP6) begins: code 257 = 0x00000101 */ + CHECK( 0x00, buf[37] ); + CHECK( 0x01, buf[38] ); + CHECK( 0x01, buf[39] ); + CHECK( 0x40, buf[40] ); /* flags: M */ + CHECK( 0x00, buf[41] ); /* length: 8+18 = 0x00001a */ + CHECK( 0x00, buf[42] ); + CHECK( 0x1A, buf[43] ); + CHECK( 0x00, buf[44] ); /* Value: AddressType 2 */ + CHECK( 0x02, buf[45] ); + CHECK( 0x11, buf[46] ); /* 1111:2222:3333:4444:1234:5678:9abc:def0 */ + CHECK( 0x11, buf[47] ); + CHECK( 0x22, buf[48] ); + CHECK( 0x22, buf[49] ); + CHECK( 0x33, buf[50] ); + CHECK( 0x33, buf[51] ); + CHECK( 0x44, buf[52] ); + CHECK( 0x44, buf[53] ); + CHECK( 0x12, buf[54] ); + CHECK( 0x34, buf[55] ); + CHECK( 0x56, buf[56] ); + CHECK( 0x78, buf[57] ); + CHECK( 0x9a, buf[58] ); + CHECK( 0xbc, buf[59] ); + CHECK( 0xde, buf[60] ); + CHECK( 0xf0, buf[61] ); + + /* Ok, now let's recreate the message */ + CHECK( 0, msg_parse_buffer( buf, 64, &cer) ); + CHECK( 0, msg_parse_dict( cer ) ); + free(buf); + + /* Get the pointers to the first and last AVP */ + CHECK( 0, msg_browse( cer, MSG_BRW_FIRST_CHILD, &avp4, NULL) ); + CHECK( 0, msg_browse( cer, MSG_BRW_LAST_CHILD, &avp6, NULL) ); + + /* Try and interpret the data in the AVPs */ + CHECK( 0, msg_avp_value_interpret ( avp4, &ss ) ); + psin = (struct sockaddr_in *)&ss; + CHECK( AF_INET, psin->sin_family ); + CHECK( 0, (inet_ntop( AF_INET, &psin->sin_addr.s_addr, buf4, sizeof(buf4) ) == NULL) ? errno : 0 ); + CHECK( 0, strcmp( buf4, TEST_IP4 ) ); + + CHECK( 0, msg_avp_value_interpret ( avp6, &ss ) ); + psin6 = (struct sockaddr_in6 *)&ss; + CHECK( AF_INET6, psin6->sin6_family ); + CHECK( 0, (inet_ntop( AF_INET6, &psin6->sin6_addr.s6_addr, buf6, sizeof(buf6) ) == NULL) ? errno : 0 ); + CHECK( 0, strcasecmp( buf6, TEST_IP6 ) ); + + /* Ok, it's done */ + CHECK( 0, msg_free( cer, 1 ) ); + } /* That's all for the tests yet */ PASSTEST();