Brian Waters | 13d9601 | 2017-12-08 16:53:31 -0600 | [diff] [blame] | 1 | /********************************************************************************************************* |
| 2 | * Software License Agreement (BSD License) * |
| 3 | * Author: Sebastien Decugis <sdecugis@freediameter.net> * |
| 4 | * * |
| 5 | * Copyright (c) 2015, WIDE Project and NICT * |
| 6 | * All rights reserved. * |
| 7 | * * |
| 8 | * Redistribution and use of this software in source and binary forms, with or without modification, are * |
| 9 | * permitted provided that the following conditions are met: * |
| 10 | * * |
| 11 | * * Redistributions of source code must retain the above * |
| 12 | * copyright notice, this list of conditions and the * |
| 13 | * following disclaimer. * |
| 14 | * * |
| 15 | * * Redistributions in binary form must reproduce the above * |
| 16 | * copyright notice, this list of conditions and the * |
| 17 | * following disclaimer in the documentation and/or other * |
| 18 | * materials provided with the distribution. * |
| 19 | * * |
| 20 | * * Neither the name of the WIDE Project or NICT nor the * |
| 21 | * names of its contributors may be used to endorse or * |
| 22 | * promote products derived from this software without * |
| 23 | * specific prior written permission of WIDE Project and * |
| 24 | * NICT. * |
| 25 | * * |
| 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
| 27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
| 28 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
| 29 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
| 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
| 32 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
| 33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
| 34 | *********************************************************************************************************/ |
| 35 | |
| 36 | #include "fdproto-internal.h" |
| 37 | #include <time.h> |
| 38 | |
| 39 | /* This file contains helpers functions to be reused as callbacks in the struct dict_type_data structure. |
| 40 | There are three callbacks there: |
| 41 | |
| 42 | - type_encode : |
| 43 | - type_interpret : |
| 44 | Those two callbacks allow to manipulate more natural structures of data in the code, and to |
| 45 | map transparently these natural structures with the AVP-encoded format by calling the functions |
| 46 | msg_avp_value_encode or msg_avp_value_interpret. |
| 47 | - type_dump : |
| 48 | This callback if provided gives a more human-readable debug information. |
| 49 | |
| 50 | */ |
| 51 | |
| 52 | /****************************/ |
| 53 | /* Address AVP type */ |
| 54 | /****************************/ |
| 55 | |
| 56 | /* The interpret and encode functions work with a "struct sockaddr_storage" pointer for mapping |
| 57 | the contents of the AVP */ |
| 58 | |
| 59 | int fd_dictfct_Address_encode(void * data, union avp_value * avp_value) |
| 60 | { |
| 61 | sSS * ss = (sSS *) data; |
| 62 | uint16_t AddressType = 0; |
| 63 | size_t size = 0; |
| 64 | unsigned char * buf = NULL; |
| 65 | |
| 66 | TRACE_ENTRY("%p %p", data, avp_value); |
| 67 | CHECK_PARAMS( data && avp_value ); |
| 68 | |
| 69 | switch (ss->ss_family) { |
| 70 | case AF_INET: |
| 71 | { |
| 72 | /* We are encoding an IP address */ |
| 73 | sSA4 * sin = (sSA4 *)ss; |
| 74 | |
| 75 | AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */ |
| 76 | size = 6; /* 2 for AddressType + 4 for data */ |
| 77 | |
| 78 | CHECK_MALLOC( buf = malloc(size) ); |
| 79 | |
| 80 | /* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */ |
| 81 | memcpy(buf + 2, &sin->sin_addr.s_addr, 4); |
| 82 | } |
| 83 | break; |
| 84 | |
| 85 | case AF_INET6: |
| 86 | { |
| 87 | /* We are encoding an IPv6 address */ |
| 88 | sSA6 * sin6 = (sSA6 *)ss; |
| 89 | |
| 90 | AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */ |
| 91 | size = 18; /* 2 for AddressType + 16 for data */ |
| 92 | |
| 93 | CHECK_MALLOC( buf = malloc(size) ); |
| 94 | |
| 95 | /* The order is already good here */ |
| 96 | memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16); |
| 97 | |
| 98 | } |
| 99 | break; |
| 100 | |
| 101 | default: |
| 102 | CHECK_PARAMS( AddressType = 0 ); |
| 103 | } |
| 104 | |
| 105 | *(uint16_t *)buf = htons(AddressType); |
| 106 | |
| 107 | avp_value->os.len = size; |
| 108 | avp_value->os.data = buf; |
| 109 | |
| 110 | return 0; |
| 111 | } |
| 112 | |
| 113 | int fd_dictfct_Address_interpret(union avp_value * avp_value, void * interpreted) |
| 114 | { |
| 115 | uint16_t AddressType = 0; |
| 116 | unsigned char * buf; |
| 117 | |
| 118 | TRACE_ENTRY("%p %p", avp_value, interpreted); |
| 119 | |
| 120 | CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2) ); |
| 121 | |
| 122 | AddressType = ntohs(*(uint16_t *)avp_value->os.data); |
| 123 | buf = &avp_value->os.data[2]; |
| 124 | |
| 125 | switch (AddressType) { |
| 126 | case 1 /* IP */: |
| 127 | { |
| 128 | sSA4 * sin = (sSA4 *)interpreted; |
| 129 | |
| 130 | CHECK_PARAMS( avp_value->os.len == 6 ); |
| 131 | |
| 132 | sin->sin_family = AF_INET; |
| 133 | /* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */ |
| 134 | memcpy(&sin->sin_addr.s_addr, buf, 4); |
| 135 | } |
| 136 | break; |
| 137 | |
| 138 | case 2 /* IP6 */: |
| 139 | { |
| 140 | sSA6 * sin6 = (sSA6 *)interpreted; |
| 141 | |
| 142 | CHECK_PARAMS( avp_value->os.len == 18 ); |
| 143 | |
| 144 | sin6->sin6_family = AF_INET6; |
| 145 | memcpy(&sin6->sin6_addr.s6_addr, buf, 16); |
| 146 | |
| 147 | } |
| 148 | break; |
| 149 | |
| 150 | default: |
| 151 | CHECK_PARAMS( AddressType = 0 ); |
| 152 | } |
| 153 | |
| 154 | return 0; |
| 155 | } |
| 156 | |
| 157 | /* Dump the content of an Address AVP */ |
| 158 | DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Address_dump, union avp_value * avp_value) |
| 159 | { |
| 160 | union { |
| 161 | sSA sa; |
| 162 | sSS ss; |
| 163 | sSA4 sin; |
| 164 | sSA6 sin6; |
| 165 | } s; |
| 166 | uint16_t fam; |
| 167 | |
| 168 | FD_DUMP_HANDLE_OFFSET(); |
| 169 | |
| 170 | memset(&s, 0, sizeof(s)); |
| 171 | |
| 172 | /* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */ |
| 173 | if (avp_value->os.len < 2) { |
| 174 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid length: %zd]", avp_value->os.len), return NULL); |
| 175 | return *buf; |
| 176 | } |
| 177 | |
| 178 | /* Following octets are the address in network byte order already */ |
| 179 | fam = avp_value->os.data[0] << 8 | avp_value->os.data[1]; |
| 180 | switch (fam) { |
| 181 | case 1: |
| 182 | /* IP */ |
| 183 | s.sa.sa_family = AF_INET; |
| 184 | if ((avp_value->os.len != 6) && (avp_value->os.len != 8)) { |
| 185 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid IP length: %zd]", avp_value->os.len), return NULL); |
| 186 | return *buf; |
| 187 | } |
| 188 | memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4); |
| 189 | if (avp_value->os.len == 8) |
| 190 | memcpy(&s.sin.sin_port, avp_value->os.data + 6, 2); |
| 191 | break; |
| 192 | case 2: |
| 193 | /* IP6 */ |
| 194 | s.sa.sa_family = AF_INET6; |
| 195 | if ((avp_value->os.len != 18) && (avp_value->os.len != 20)) { |
| 196 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid IP6 length: %zd]", avp_value->os.len), return NULL); |
| 197 | return *buf; |
| 198 | } |
| 199 | memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16); |
| 200 | if (avp_value->os.len == 20) |
| 201 | memcpy(&s.sin6.sin6_port, avp_value->os.data + 18, 2); |
| 202 | break; |
| 203 | case 8: |
| 204 | /* E.164 */ |
| 205 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "%.*s", (int)(avp_value->os.len-2), avp_value->os.data+2), return NULL); |
| 206 | return *buf; |
| 207 | default: |
| 208 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[unsupported family: 0x%hx]", fam), return NULL); |
| 209 | return *buf; |
| 210 | } |
| 211 | |
| 212 | return fd_sa_dump(FD_DUMP_STD_PARAMS, &s.sa, NI_NUMERICHOST); |
| 213 | } |
| 214 | |
| 215 | |
| 216 | |
| 217 | /*******************************/ |
| 218 | /* UTF8String AVP type */ |
| 219 | /*******************************/ |
| 220 | |
| 221 | /* Dump the AVP in a natural human-readable format. This dumps the complete length of the AVP, it is up to the caller to truncate if needed */ |
| 222 | DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_UTF8String_dump, union avp_value * avp_value) |
| 223 | { |
| 224 | size_t l; |
| 225 | FD_DUMP_HANDLE_OFFSET(); |
| 226 | |
| 227 | l = avp_value->os.len; |
| 228 | /* Just in case the string ends in invalid UTF-8 chars, we shorten it */ |
| 229 | while ((l > 0) && (avp_value->os.data[l - 1] & 0x80)) { |
| 230 | /* this byte is start or cont. of multibyte sequence, as we do not know the next byte we need to delete it. */ |
| 231 | l--; |
| 232 | if (avp_value->os.data[l] & 0x40) |
| 233 | break; /* This was a start byte, we can stop the loop */ |
| 234 | } |
| 235 | |
| 236 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "\"%.*s\"", (int)l, (char *)avp_value->os.data), return NULL); |
| 237 | |
| 238 | return *buf; |
| 239 | } |
| 240 | |
| 241 | |
| 242 | /*******************************/ |
| 243 | /* Time AVP type */ |
| 244 | /*******************************/ |
| 245 | |
| 246 | /* The interpret and encode functions work with a "time_t" pointer for mapping |
| 247 | the contents of the AVP */ |
| 248 | |
| 249 | /* Unix Epoch starts 1970-01-01, NTP 0 is at 1900-01-01 */ |
| 250 | #define DIFF_EPOCH_TO_NTP ((365*(1970-1900) + 17ul) * 24 * 60 * 60) |
| 251 | |
| 252 | static int diameter_string_to_time_t(const char *str, size_t len, time_t *result) { |
| 253 | time_t time_stamp; |
| 254 | CHECK_PARAMS(len == 4); |
| 255 | |
| 256 | time_stamp = (((unsigned long)(str[0]&0xff))<<24) + ((str[1]&0xff)<<16) + ((str[2]&0xff)<<8) + ((str[3]&0xff)); |
| 257 | time_stamp -= DIFF_EPOCH_TO_NTP; |
| 258 | #ifdef FIX__NEEDED_FOR_YEAR_2036_AND_LATER |
| 259 | /* NTP overflows in 2036; after that, values start at zero again */ |
| 260 | #define NTP_OVERFLOW_CORRECTION (0x100000000ull) |
| 261 | /* XXX: debug and find correct conversion */ |
| 262 | if (str[0] & 0x80 == 0x00) { |
| 263 | time_stamp += NTP_OVERFLOW_CORRECTION; |
| 264 | } |
| 265 | #endif |
| 266 | *result = time_stamp; |
| 267 | return 0; |
| 268 | } |
| 269 | |
| 270 | static int time_t_to_diameter_string(time_t time_stamp, char **result) { |
| 271 | uint64_t out = time_stamp; |
| 272 | char *conv; |
| 273 | /* XXX: 2036 fix */ |
| 274 | out += DIFF_EPOCH_TO_NTP; |
| 275 | CHECK_PARAMS( (out >> 32) == 0); |
| 276 | |
| 277 | CHECK_MALLOC(conv=(char *)malloc(5)); |
| 278 | |
| 279 | conv[0] = (out>>24) & 0xff; |
| 280 | conv[1] = (out>>16) & 0xff; |
| 281 | conv[2] = (out>> 8) & 0xff; |
| 282 | conv[3] = out & 0xff; |
| 283 | conv[4] = '\0'; |
| 284 | *result = conv; |
| 285 | return 0; |
| 286 | } |
| 287 | |
| 288 | int fd_dictfct_Time_encode(void * data, union avp_value * avp_value) |
| 289 | { |
| 290 | char * buf; |
| 291 | size_t len; |
| 292 | |
| 293 | TRACE_ENTRY("%p %p", data, avp_value); |
| 294 | CHECK_PARAMS( data && avp_value ); |
| 295 | |
| 296 | CHECK_FCT( time_t_to_diameter_string( *((time_t *)data), &buf) ); |
| 297 | /* FIXME: return len from the function above? */ len = 4; |
| 298 | |
| 299 | avp_value->os.len = len; |
| 300 | avp_value->os.data = (uint8_t *)buf; |
| 301 | return 0; |
| 302 | } |
| 303 | |
| 304 | int fd_dictfct_Time_interpret(union avp_value * avp_value, void * interpreted) |
| 305 | { |
| 306 | TRACE_ENTRY("%p %p", avp_value, interpreted); |
| 307 | |
| 308 | CHECK_PARAMS( avp_value && interpreted ); |
| 309 | |
| 310 | return diameter_string_to_time_t((const char *)avp_value->os.data, avp_value->os.len, interpreted); |
| 311 | } |
| 312 | |
| 313 | static void _format_offs (long offset, char *buf) { |
| 314 | int offs_hours, offs_minutes, sgn = 1; |
| 315 | if (offset < 0) { |
| 316 | offset = -offset; |
| 317 | sgn = 1; |
| 318 | } |
| 319 | offs_hours = (int)(offset/3600); |
| 320 | offs_minutes = (offset%3600)/60; |
| 321 | |
| 322 | char* s = buf; |
| 323 | |
| 324 | *(s++) = sgn == 1 ? '+' : '-'; |
| 325 | *(s++) = (char)(offs_hours/10) + '0'; |
| 326 | *(s++) = offs_hours%10 + '0'; |
| 327 | |
| 328 | if (offs_minutes == 0) { |
| 329 | *(s++) = '\0'; |
| 330 | } else { |
| 331 | *(s++) = (char)(offs_minutes/10) + '0'; |
| 332 | *(s++) = offs_minutes%10 + '0'; |
| 333 | *(s++) = '\0'; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | DECLARE_FD_DUMP_PROTOTYPE(fd_dictfct_Time_dump, union avp_value * avp_value) |
| 338 | { |
| 339 | time_t val; |
| 340 | struct tm conv; |
| 341 | char tz_buf[7]; |
| 342 | |
| 343 | FD_DUMP_HANDLE_OFFSET(); |
| 344 | |
| 345 | if (avp_value->os.len != 4) { |
| 346 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[invalid length: %zd]", avp_value->os.len), return NULL); |
| 347 | return *buf; |
| 348 | } |
| 349 | |
| 350 | if (diameter_string_to_time_t((char *)avp_value->os.data, avp_value->os.len, &val) != 0) { |
| 351 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "[time conversion error]"), return NULL); |
| 352 | return *buf; |
| 353 | } |
| 354 | |
| 355 | CHECK_MALLOC_DO( localtime_r(&val, &conv), return NULL); |
| 356 | _format_offs(conv.tm_gmtoff, tz_buf); |
| 357 | CHECK_MALLOC_DO( fd_dump_extend(FD_DUMP_STD_PARAMS, "%d%02d%02dT%02d%02d%02d%s", conv.tm_year+1900, conv.tm_mon+1, conv.tm_mday, conv.tm_hour, conv.tm_min, conv.tm_sec, tz_buf), return NULL); |
| 358 | return *buf; |
| 359 | } |
| 360 | |
| 361 | /* Check that a given AVP value contains all the characters from data in the same order */ |
| 362 | static char error_message[80]; |
| 363 | int fd_dictfct_CharInOS_check(void * data, union avp_value * val, char ** error_msg) |
| 364 | { |
| 365 | char * inChar = data; |
| 366 | char * inData = (char *)val->os.data; |
| 367 | int i = 0; |
| 368 | CHECK_PARAMS(data); |
| 369 | while (*inChar != '\0') { |
| 370 | while (i < val->os.len) { |
| 371 | if (*inChar == inData[i++]) { |
| 372 | inChar++; |
| 373 | break; |
| 374 | } |
| 375 | } |
| 376 | if (i >= val->os.len) |
| 377 | break; |
| 378 | } |
| 379 | if (*inChar == '\0') |
| 380 | return 0; |
| 381 | |
| 382 | if (error_msg) { |
| 383 | snprintf(error_message, sizeof(error_message), "Could not find '%c' in AVP", *inChar); |
| 384 | *error_msg = error_message; |
| 385 | } |
| 386 | return EBADMSG; |
| 387 | } |