blob: 315b417ea8a7bfba8c27ad8461f92754a6245e36 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
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.
40There 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
57the contents of the AVP */
58
59int 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
113int 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 */
158DECLARE_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 */
222DECLARE_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
247the 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
252static 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
270static 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
288int 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
304int 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
313static 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
337DECLARE_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 */
362static char error_message[80];
363int 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}