Mercurial > hg > freeDiameter
comparison 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 |
comparison
equal
deleted
inserted
replaced
921:a0ab56aa089f | 922:c7bf1a7a4e90 |
---|---|
1 /********************************************************************************************************* | |
2 * Software License Agreement (BSD License) * | |
3 * Author: Sebastien Decugis <sdecugis@freediameter.net> * | |
4 * * | |
5 * Copyright (c) 2013, 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 | |
38 /* This file contains helpers functions to be reused as callbacks in the struct dict_type_data structure. | |
39 There are three callbacks there: | |
40 | |
41 - type_encode : | |
42 - type_interpret : | |
43 Those two callbacks allow to manipulate more natural structures of data in the code, and to | |
44 map transparently these natural structures with the AVP-encoded format by calling the functions | |
45 msg_avp_value_encode or msg_avp_value_interpret. | |
46 - type_dump : | |
47 This callback if provided gives a more human-readable debug information. | |
48 | |
49 */ | |
50 | |
51 /****************************/ | |
52 /* Address AVP type */ | |
53 /****************************/ | |
54 | |
55 /* The interpret and encode functions work with a "struct sockaddr_storage" pointer for mapping | |
56 the contents of the AVP */ | |
57 | |
58 int fd_dictfct_Address_encode(void * data, union avp_value * avp_value) | |
59 { | |
60 sSS * ss = (sSS *) data; | |
61 uint16_t AddressType = 0; | |
62 size_t size = 0; | |
63 unsigned char * buf = NULL; | |
64 | |
65 TRACE_ENTRY("%p %p", data, avp_value); | |
66 CHECK_PARAMS( data && avp_value ); | |
67 | |
68 switch (ss->ss_family) { | |
69 case AF_INET: | |
70 { | |
71 /* We are encoding an IP address */ | |
72 sSA4 * sin = (sSA4 *)ss; | |
73 | |
74 AddressType = 1;/* see http://www.iana.org/assignments/address-family-numbers/ */ | |
75 size = 6; /* 2 for AddressType + 4 for data */ | |
76 | |
77 CHECK_MALLOC( buf = malloc(size) ); | |
78 | |
79 /* may not work because of alignment: *(uint32_t *)(buf+2) = htonl(sin->sin_addr.s_addr); */ | |
80 memcpy(buf + 2, &sin->sin_addr.s_addr, 4); | |
81 } | |
82 break; | |
83 | |
84 case AF_INET6: | |
85 { | |
86 /* We are encoding an IPv6 address */ | |
87 sSA6 * sin6 = (sSA6 *)ss; | |
88 | |
89 AddressType = 2;/* see http://www.iana.org/assignments/address-family-numbers/ */ | |
90 size = 18; /* 2 for AddressType + 16 for data */ | |
91 | |
92 CHECK_MALLOC( buf = malloc(size) ); | |
93 | |
94 /* The order is already good here */ | |
95 memcpy(buf + 2, &sin6->sin6_addr.s6_addr, 16); | |
96 } | |
97 break; | |
98 | |
99 default: | |
100 CHECK_PARAMS( AddressType = 0 ); | |
101 } | |
102 | |
103 *(uint16_t *)buf = htons(AddressType); | |
104 | |
105 avp_value->os.len = size; | |
106 avp_value->os.data = buf; | |
107 | |
108 return 0; | |
109 } | |
110 | |
111 int fd_dictfct_Address_interpret(union avp_value * avp_value, void * interpreted) | |
112 { | |
113 uint16_t AddressType = 0; | |
114 unsigned char * buf; | |
115 | |
116 TRACE_ENTRY("%p %p", avp_value, interpreted); | |
117 | |
118 CHECK_PARAMS( avp_value && interpreted && (avp_value->os.len >= 2) ); | |
119 | |
120 AddressType = ntohs(*(uint16_t *)avp_value->os.data); | |
121 buf = &avp_value->os.data[2]; | |
122 | |
123 switch (AddressType) { | |
124 case 1 /* IP */: | |
125 { | |
126 sSA4 * sin = (sSA4 *)interpreted; | |
127 | |
128 CHECK_PARAMS( avp_value->os.len == 6 ); | |
129 | |
130 sin->sin_family = AF_INET; | |
131 /* sin->sin_addr.s_addr = ntohl( * (uint32_t *) buf); -- may not work because of bad alignment */ | |
132 memcpy(&sin->sin_addr.s_addr, buf, 4); | |
133 } | |
134 break; | |
135 | |
136 case 2 /* IP6 */: | |
137 { | |
138 sSA6 * sin6 = (sSA6 *)interpreted; | |
139 | |
140 CHECK_PARAMS( avp_value->os.len == 18 ); | |
141 | |
142 sin6->sin6_family = AF_INET6; | |
143 memcpy(&sin6->sin6_addr.s6_addr, buf, 16); | |
144 } | |
145 break; | |
146 | |
147 default: | |
148 CHECK_PARAMS( AddressType = 0 ); | |
149 } | |
150 | |
151 return 0; | |
152 } | |
153 | |
154 /* Dump the content of an Address AVP */ | |
155 char * fd_dictfct_Address_dump(union avp_value * avp_value) | |
156 { | |
157 char * ret; | |
158 #define STR_LEN 1024 | |
159 union { | |
160 sSA sa; | |
161 sSS ss; | |
162 sSA4 sin; | |
163 sSA6 sin6; | |
164 } s; | |
165 uint16_t fam; | |
166 | |
167 memset(&s, 0, sizeof(s)); | |
168 | |
169 CHECK_MALLOC_DO( ret = malloc(STR_LEN), return NULL ); | |
170 | |
171 /* The first two octets represent the address family, http://www.iana.org/assignments/address-family-numbers/ */ | |
172 if (avp_value->os.len < 2) { | |
173 snprintf(ret, STR_LEN, "[invalid length: %zd]", avp_value->os.len); | |
174 return ret; | |
175 } | |
176 | |
177 /* Following octets are the address in network byte order already */ | |
178 fam = avp_value->os.data[0] << 8 | avp_value->os.data[1]; | |
179 switch (fam) { | |
180 case 1: | |
181 /* IP */ | |
182 s.sa.sa_family = AF_INET; | |
183 if (avp_value->os.len != 6) { | |
184 snprintf(ret, STR_LEN, "[invalid IP length: %zd]", avp_value->os.len); | |
185 return ret; | |
186 } | |
187 memcpy(&s.sin.sin_addr.s_addr, avp_value->os.data + 2, 4); | |
188 break; | |
189 case 2: | |
190 /* IP6 */ | |
191 s.sa.sa_family = AF_INET6; | |
192 if (avp_value->os.len != 18) { | |
193 snprintf(ret, STR_LEN, "[invalid IP6 length: %zd]", avp_value->os.len); | |
194 return ret; | |
195 } | |
196 memcpy(&s.sin6.sin6_addr.s6_addr, avp_value->os.data + 2, 16); | |
197 break; | |
198 default: | |
199 snprintf(ret, STR_LEN, "[unsupported family: 0x%hx]", fam); | |
200 return ret; | |
201 } | |
202 | |
203 { | |
204 int rc = getnameinfo(&s.sa, sSAlen(&s.sa), ret, STR_LEN, NULL, 0, NI_NUMERICHOST); | |
205 if (rc) | |
206 snprintf(ret, STR_LEN, "%s", (char *)gai_strerror(rc)); | |
207 } | |
208 | |
209 return ret; | |
210 } | |
211 | |
212 | |
213 | |
214 /*******************************/ | |
215 /* UTF8String AVP type */ | |
216 /*******************************/ | |
217 | |
218 /* Dump the AVP in a natural human-readable format */ | |
219 char * fd_dictfct_UTF8String_dump(union avp_value * avp_value) | |
220 { | |
221 #define TRUNC_LEN 42 /* avoid very long strings */ | |
222 char * ret = strndup((char *)avp_value->os.data, TRUNC_LEN); | |
223 if (ret && (*ret != '\0')) { | |
224 /* We sanitize the returned string to avoid UTF8 boundary problem. | |
225 We do this whether the string is trucated at TRUNC_LEN or not, to avoid potential problem | |
226 with malformed AVP */ | |
227 | |
228 char * end = strchr(ret, '\0'); | |
229 | |
230 | |
231 } | |
232 return ret; | |
233 } | |
234 | |
235 | |
236 /*******************************/ | |
237 /* Time AVP type */ | |
238 /*******************************/ | |
239 | |
240 /* The interpret and encode functions work with a "time_t" pointer for mapping | |
241 the contents of the AVP */ | |
242 | |
243 /* Unix Epoch starts 1970-01-01, NTP 0 is at 1900-01-01 */ | |
244 #define DIFF_EPOCH_TO_NTP ((365*(1970-1900) + 17ul) * 24 * 60 * 60) | |
245 | |
246 static int diameter_string_to_time_t(const char *str, size_t len, time_t *result) { | |
247 time_t time_stamp; | |
248 CHECK_PARAMS(len == 4); | |
249 | |
250 time_stamp = (((unsigned long)(str[0]&0xff))<<24) + ((str[1]&0xff)<<16) + ((str[2]&0xff)<<8) + ((str[3]&0xff)); | |
251 time_stamp -= DIFF_EPOCH_TO_NTP; | |
252 #ifdef FIX__NEEDED_FOR_YEAR_2036_AND_LATER | |
253 /* NTP overflows in 2036; after that, values start at zero again */ | |
254 #define NTP_OVERFLOW_CORRECTION (0x100000000ull) | |
255 /* XXX: debug and find correct conversion */ | |
256 if (str[0] & 0x80 == 0x00) { | |
257 time_stamp += NTP_OVERFLOW_CORRECTION; | |
258 } | |
259 #endif | |
260 *result = time_stamp; | |
261 return 0; | |
262 } | |
263 | |
264 static int time_t_to_diameter_string(time_t time_stamp, char **result) { | |
265 uint64_t out = time_stamp; | |
266 char *conv; | |
267 /* XXX: 2036 fix */ | |
268 out += DIFF_EPOCH_TO_NTP; | |
269 CHECK_PARAMS( (out & 0xffffffff00000000) == 0); | |
270 | |
271 CHECK_MALLOC(conv=(char *)malloc(5)); | |
272 | |
273 conv[0] = (out>>24) & 0xff; | |
274 conv[1] = (out>>16) & 0xff; | |
275 conv[2] = (out>> 8) & 0xff; | |
276 conv[3] = out & 0xff; | |
277 conv[4] = '\0'; | |
278 *result = conv; | |
279 return 0; | |
280 } | |
281 | |
282 int fd_dictfct_Time_encode(void * data, union avp_value * avp_value) | |
283 { | |
284 char * buf; | |
285 size_t len; | |
286 | |
287 TRACE_ENTRY("%p %p", data, avp_value); | |
288 CHECK_PARAMS( data && avp_value ); | |
289 | |
290 CHECK_FCT( time_t_to_diameter_string( *((time_t *)data), &buf) ); | |
291 /* FIXME: return len from the function above? */ len = 4; | |
292 | |
293 avp_value->os.len = len; | |
294 avp_value->os.data = buf; | |
295 return 0; | |
296 } | |
297 | |
298 int fd_dictfct_Time_interpret(union avp_value * avp_value, void * interpreted) | |
299 { | |
300 TRACE_ENTRY("%p %p", avp_value, interpreted); | |
301 | |
302 CHECK_PARAMS( avp_value && interpreted ); | |
303 | |
304 return diameter_string_to_time_t(avp_value->os.data, avp_value->os.len, interpreted); | |
305 } | |
306 | |
307 char * fd_dictfct_Time_dump(union avp_value * avp_value) | |
308 { | |
309 char * ret; | |
310 CHECK_MALLOC_DO( ret = malloc(STR_LEN), return NULL ); | |
311 if (avp_value->os.len != 4) { | |
312 snprintf(ret, STR_LEN, "[invalid length: %zd]", avp_value->os.len); | |
313 return ret; | |
314 } | |
315 /* TODO: display the time as human-readable */ | |
316 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]); | |
317 return ret; | |
318 } | |
319 |