Mercurial > hg > freeDiameter
diff libfdproto/dictionary_functions.c @ 922:c7bf1a7a4e90
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
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Thu, 14 Feb 2013 15:43:36 +0100 |
parents | |
children | 6a4d08e239bd |
line wrap: on
line diff
--- /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 <sdecugis@freediameter.net> * +* * +* 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; +} +