# HG changeset patch # User Sebastien Decugis # Date 1360853016 -3600 # Node ID c7bf1a7a4e90b4d8fb8a1a332f14aa69c36d2d1a # Parent a0ab56aa089facb5340ba2cc06578710f58ef97d Split the encoders/interpreters for the dictionary types into a different file for better reusability, add decoder/interpreter for Time type based on code from Thomas Klausner diff -r a0ab56aa089f -r c7bf1a7a4e90 include/freeDiameter/libfdproto.h --- a/include/freeDiameter/libfdproto.h Thu Feb 14 14:12:23 2013 +0100 +++ b/include/freeDiameter/libfdproto.h Thu Feb 14 15:43:36 2013 +0100 @@ -1163,6 +1163,26 @@ TYPE_OF_AVP /* "what" points to a struct dict_object containing an AVP object. */ }; +/**** + Callbacks defined in libfdproto/dictionary_functions.c file -- see that file for usage. + */ + +/* Convert an Address type AVP into a struct sockaddr_storage */ +int fd_dictfct_Address_encode(void * data, union avp_value * avp_value); +int fd_dictfct_Address_interpret(union avp_value * avp_value, void * interpreted); +char * fd_dictfct_Address_dump(union avp_value * avp_value); + +/* Display the content of an AVP of type UTF8String in the log file */ +char * fd_dictfct_UTF8String_dump(union avp_value * avp_value); + +/* For Time AVPs, map with time_t value directly */ +int fd_dictfct_Time_encode(void * data, union avp_value * avp_value); +int fd_dictfct_Time_interpret(union avp_value * avp_value, void * interpreted); +char * fd_dictfct_Time_dump(union avp_value * avp_value); + + + +/****/ /*** * API usage : @@ -1193,7 +1213,7 @@ } */ - + /* *************************************************************************** * diff -r a0ab56aa089f -r c7bf1a7a4e90 libfdcore/dict_base_proto.c --- a/libfdcore/dict_base_proto.c Thu Feb 14 14:12:23 2013 +0100 +++ b/libfdcore/dict_base_proto.c Thu Feb 14 15:43:36 2013 +0100 @@ -44,169 +44,6 @@ /* The pointer for the global dictionary (initialized from main) */ struct dictionary * fd_g_dict = NULL; -/* 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, union avp_value * avp_value) -{ - sSS * ss = (sSS *) data; - uint16_t AddressType = 0; - size_t size = 0; - unsigned char * buf = NULL; - - TRACE_ENTRY("%p %p", data, avp_value); - CHECK_PARAMS( data && avp_value ); - - switch (ss->ss_family) { - case AF_INET: - { - /* We are encoding an IP address */ - sSA4 * sin = (sSA4 *)ss; - - AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */ - size = 6; /* 2 for AddressType + 4 for data */ - - CHECK_MALLOC( buf = malloc(size) ); - - /* 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 an IPv6 address */ - sSA6 * sin6 = (sSA6 *)ss; - - AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */ - size = 18; /* 2 for AddressType + 16 for data */ - - CHECK_MALLOC( buf = malloc(size) ); - - /* The order is already good here */ - memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16); - } - break; - - default: - CHECK_PARAMS( AddressType = 0 ); - } - - *(uint16_t *)buf = htons(AddressType); - - avp_value->os.len = size; - avp_value->os.data = buf; - - return 0; -} - -static int Address_interpret(union avp_value * avp_value, void * interpreted) -{ - uint16_t AddressType = 0; - unsigned char * buf; - - TRACE_ENTRY("%p %p", avp_value, interpreted); - - CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2) ); - - AddressType = ntohs(*(uint16_t *)avp_value->os.data); - buf = &avp_value->os.data[2]; - - switch (AddressType) { - case 1 /* IP */: - { - sSA4 * sin = (sSA4 *)interpreted; - - CHECK_PARAMS( avp_value->os.len == 6 ); - - sin->sin_family = AF_INET; - /* 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 */: - { - sSA6 * sin6 = (sSA6 *)interpreted; - - CHECK_PARAMS( avp_value->os.len == 18 ); - - sin6->sin6_family = AF_INET6; - memcpy(&sin6->sin6_addr.s6_addr, buf, 16); - } - break; - - default: - CHECK_PARAMS( AddressType = 0 ); - } - - return 0; -} - -/* Dump the content of an Address AVP */ -static char * Address_dump(union avp_value * avp_value) -{ - char * ret; - #define STR_LEN 1024 - union { - sSA sa; - sSS ss; - sSA4 sin; - sSA6 sin6; - } s; - uint16_t fam; - - memset(&s, 0, sizeof(s)); - - CHECK_MALLOC_DO( ret = malloc(STR_LEN), return NULL ); - - /* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */ - if (avp_value->os.len < 2) { - snprintf(ret, STR_LEN, "[invalid length: %zd]", avp_value->os.len); - return ret; - } - - /* Following octets are the address in network byte order already */ - fam = avp_value->os.data[0] << 8 | avp_value->os.data[1]; - switch (fam) { - case 1: - /* IP */ - s.sa.sa_family = AF_INET; - if (avp_value->os.len != 6) { - snprintf(ret, STR_LEN, "[invalid IP length: %zd]", avp_value->os.len); - return ret; - } - memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4); - break; - case 2: - /* IP6 */ - s.sa.sa_family = AF_INET6; - if (avp_value->os.len != 18) { - snprintf(ret, STR_LEN, "[invalid IP6 length: %zd]", avp_value->os.len); - return ret; - } - memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16); - break; - default: - snprintf(ret, STR_LEN, "[unsupported family: 0x%hx]", fam); - return ret; - } - - { - int rc = getnameinfo(&s.sa, sSAlen(&s.sa), ret, STR_LEN, NULL, 0, NI_NUMERICHOST); - if (rc) - snprintf(ret, STR_LEN, "%s", (char *)gai_strerror(rc)); - } - - return ret; -} - -static char * UTF8String_dump(union avp_value * avp_value) -{ - return strndup((char *)avp_value->os.data, 42); /* avoid very long strings */ -} - - #define CHECK_dict_new( _type, _data, _parent, _ref ) \ @@ -294,7 +131,7 @@ defined in [IANAADFAM]. The AddressType is used to discriminate the content and format of the remaining octets. */ - struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "Address" , Address_interpret , Address_encode, Address_dump }; + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "Address" , fd_dictfct_Address_interpret , fd_dictfct_Address_encode, fd_dictfct_Address_dump }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -313,7 +150,7 @@ SNTP [RFC4330] describes a procedure to extend the time to 2104. This procedure MUST be supported by all DIAMETER nodes. */ - struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "Time" , NULL , NULL , NULL }; + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "Time" , fd_dictfct_Time_interpret , fd_dictfct_Time_encode, fd_dictfct_Time_dump }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -351,7 +188,7 @@ Note that the AVP Length field of an UTF8String is measured in octets, not characters. */ - struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "UTF8String" , NULL , NULL , UTF8String_dump }; + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "UTF8String" , NULL , NULL , fd_dictfct_UTF8String_dump }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -380,7 +217,7 @@ interactions between the Diameter protocol and Internationalized Domain Name (IDNs). */ - struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity" , NULL , NULL , UTF8String_dump }; + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "DiameterIdentity" , NULL , NULL , fd_dictfct_UTF8String_dump }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -435,7 +272,7 @@ aaa://host.example.com:6666;transport=tcp;protocol=diameter aaa://host.example.com:1813;transport=udp;protocol=radius */ - struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "DiameterURI" , NULL , NULL , UTF8String_dump }; + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "DiameterURI" , NULL , NULL , fd_dictfct_UTF8String_dump }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } @@ -497,7 +334,7 @@ supplied rules, for example to protect the access device owner's infrastructure. */ - struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "IPFilterRule" , NULL , NULL , UTF8String_dump }; + struct dict_type_data data = { AVP_TYPE_OCTETSTRING, "IPFilterRule" , NULL , NULL , fd_dictfct_UTF8String_dump }; CHECK_dict_new( DICT_TYPE, &data , NULL, NULL); } } diff -r a0ab56aa089f -r c7bf1a7a4e90 libfdproto/CMakeLists.txt --- a/libfdproto/CMakeLists.txt Thu Feb 14 14:12:23 2013 +0100 +++ b/libfdproto/CMakeLists.txt Thu Feb 14 15:43:36 2013 +0100 @@ -5,6 +5,7 @@ SET(LFDPROTO_SRC fdproto-internal.h dictionary.c + dictionary_functions.c dispatch.c fifo.c init.c diff -r a0ab56aa089f -r c7bf1a7a4e90 libfdproto/dictionary_functions.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfdproto/dictionary_functions.c Thu Feb 14 15:43:36 2013 +0100 @@ -0,0 +1,319 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2013, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +#include "fdproto-internal.h" + +/* This file contains helpers functions to be reused as callbacks in the struct dict_type_data structure. +There are three callbacks there: + + - type_encode : + - type_interpret : + Those two callbacks allow to manipulate more natural structures of data in the code, and to + map transparently these natural structures with the AVP-encoded format by calling the functions + msg_avp_value_encode or msg_avp_value_interpret. + - type_dump : + This callback if provided gives a more human-readable debug information. + + */ + +/****************************/ +/* Address AVP type */ +/****************************/ + +/* The interpret and encode functions work with a "struct sockaddr_storage" pointer for mapping +the contents of the AVP */ + +int fd_dictfct_Address_encode(void * data, union avp_value * avp_value) +{ + sSS * ss = (sSS *) data; + uint16_t AddressType = 0; + size_t size = 0; + unsigned char * buf = NULL; + + TRACE_ENTRY("%p %p", data, avp_value); + CHECK_PARAMS( data && avp_value ); + + switch (ss->ss_family) { + case AF_INET: + { + /* We are encoding an IP address */ + sSA4 * sin = (sSA4 *)ss; + + AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */ + size = 6; /* 2 for AddressType + 4 for data */ + + CHECK_MALLOC( buf = malloc(size) ); + + /* 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 an IPv6 address */ + sSA6 * sin6 = (sSA6 *)ss; + + AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */ + size = 18; /* 2 for AddressType + 16 for data */ + + CHECK_MALLOC( buf = malloc(size) ); + + /* The order is already good here */ + memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16); + } + break; + + default: + CHECK_PARAMS( AddressType = 0 ); + } + + *(uint16_t *)buf = htons(AddressType); + + avp_value->os.len = size; + avp_value->os.data = buf; + + return 0; +} + +int fd_dictfct_Address_interpret(union avp_value * avp_value, void * interpreted) +{ + uint16_t AddressType = 0; + unsigned char * buf; + + TRACE_ENTRY("%p %p", avp_value, interpreted); + + CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2) ); + + AddressType = ntohs(*(uint16_t *)avp_value->os.data); + buf = &avp_value->os.data[2]; + + switch (AddressType) { + case 1 /* IP */: + { + sSA4 * sin = (sSA4 *)interpreted; + + CHECK_PARAMS( avp_value->os.len == 6 ); + + sin->sin_family = AF_INET; + /* 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 */: + { + sSA6 * sin6 = (sSA6 *)interpreted; + + CHECK_PARAMS( avp_value->os.len == 18 ); + + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr.s6_addr, buf, 16); + } + break; + + default: + CHECK_PARAMS( AddressType = 0 ); + } + + return 0; +} + +/* Dump the content of an Address AVP */ +char * fd_dictfct_Address_dump(union avp_value * avp_value) +{ + char * ret; + #define STR_LEN 1024 + union { + sSA sa; + sSS ss; + sSA4 sin; + sSA6 sin6; + } s; + uint16_t fam; + + memset(&s, 0, sizeof(s)); + + CHECK_MALLOC_DO( ret = malloc(STR_LEN), return NULL ); + + /* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */ + if (avp_value->os.len < 2) { + snprintf(ret, STR_LEN, "[invalid length: %zd]", avp_value->os.len); + return ret; + } + + /* Following octets are the address in network byte order already */ + fam = avp_value->os.data[0] << 8 | avp_value->os.data[1]; + switch (fam) { + case 1: + /* IP */ + s.sa.sa_family = AF_INET; + if (avp_value->os.len != 6) { + snprintf(ret, STR_LEN, "[invalid IP length: %zd]", avp_value->os.len); + return ret; + } + memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4); + break; + case 2: + /* IP6 */ + s.sa.sa_family = AF_INET6; + if (avp_value->os.len != 18) { + snprintf(ret, STR_LEN, "[invalid IP6 length: %zd]", avp_value->os.len); + return ret; + } + memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16); + break; + default: + snprintf(ret, STR_LEN, "[unsupported family: 0x%hx]", fam); + return ret; + } + + { + int rc = getnameinfo(&s.sa, sSAlen(&s.sa), ret, STR_LEN, NULL, 0, NI_NUMERICHOST); + if (rc) + snprintf(ret, STR_LEN, "%s", (char *)gai_strerror(rc)); + } + + return ret; +} + + + +/*******************************/ +/* UTF8String AVP type */ +/*******************************/ + +/* Dump the AVP in a natural human-readable format */ +char * fd_dictfct_UTF8String_dump(union avp_value * avp_value) +{ +#define TRUNC_LEN 42 /* avoid very long strings */ + char * ret = strndup((char *)avp_value->os.data, TRUNC_LEN); + if (ret && (*ret != '\0')) { + /* We sanitize the returned string to avoid UTF8 boundary problem. + We do this whether the string is trucated at TRUNC_LEN or not, to avoid potential problem + with malformed AVP */ + + char * end = strchr(ret, '\0'); + + + } + return ret; +} + + +/*******************************/ +/* Time AVP type */ +/*******************************/ + +/* The interpret and encode functions work with a "time_t" pointer for mapping +the contents of the AVP */ + +/* Unix Epoch starts 1970-01-01, NTP 0 is at 1900-01-01 */ +#define DIFF_EPOCH_TO_NTP ((365*(1970-1900) + 17ul) * 24 * 60 * 60) + +static int diameter_string_to_time_t(const char *str, size_t len, time_t *result) { + time_t time_stamp; + CHECK_PARAMS(len == 4); + + time_stamp = (((unsigned long)(str[0]&0xff))<<24) + ((str[1]&0xff)<<16) + ((str[2]&0xff)<<8) + ((str[3]&0xff)); + time_stamp -= DIFF_EPOCH_TO_NTP; +#ifdef FIX__NEEDED_FOR_YEAR_2036_AND_LATER +/* NTP overflows in 2036; after that, values start at zero again */ +#define NTP_OVERFLOW_CORRECTION (0x100000000ull) + /* XXX: debug and find correct conversion */ + if (str[0] & 0x80 == 0x00) { + time_stamp += NTP_OVERFLOW_CORRECTION; + } +#endif + *result = time_stamp; + return 0; +} + +static int time_t_to_diameter_string(time_t time_stamp, char **result) { + uint64_t out = time_stamp; + char *conv; + /* XXX: 2036 fix */ + out += DIFF_EPOCH_TO_NTP; + CHECK_PARAMS( (out & 0xffffffff00000000) == 0); + + CHECK_MALLOC(conv=(char *)malloc(5)); + + conv[0] = (out>>24) & 0xff; + conv[1] = (out>>16) & 0xff; + conv[2] = (out>> 8) & 0xff; + conv[3] = out & 0xff; + conv[4] = '\0'; + *result = conv; + return 0; +} + +int fd_dictfct_Time_encode(void * data, union avp_value * avp_value) +{ + char * buf; + size_t len; + + TRACE_ENTRY("%p %p", data, avp_value); + CHECK_PARAMS( data && avp_value ); + + CHECK_FCT( time_t_to_diameter_string( *((time_t *)data), &buf) ); + /* FIXME: return len from the function above? */ len = 4; + + avp_value->os.len = len; + avp_value->os.data = buf; + return 0; +} + +int fd_dictfct_Time_interpret(union avp_value * avp_value, void * interpreted) +{ + TRACE_ENTRY("%p %p", avp_value, interpreted); + + CHECK_PARAMS( avp_value && interpreted ); + + return diameter_string_to_time_t(avp_value->os.data, avp_value->os.len, interpreted); +} + +char * fd_dictfct_Time_dump(union avp_value * avp_value) +{ + char * ret; + CHECK_MALLOC_DO( ret = malloc(STR_LEN), return NULL ); + if (avp_value->os.len != 4) { + snprintf(ret, STR_LEN, "[invalid length: %zd]", avp_value->os.len); + return ret; + } + /* TODO: display the time as human-readable */ + snprintf(ret, STR_LEN, "[TODO Time dump: 0x%02hhx%02hhx%02hhx%02hhx]", avp_value->os.data[0], avp_value->os.data[1], avp_value->os.data[2], avp_value->os.data[3]); + return ret; +} +