blob: 22428276c2ed31d7fe6845d498dbc6a017435f60 [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 "fdcore-internal.h"
37
38static struct dict_object * dict_avp_SI = NULL; /* Session-Id */
39static struct dict_object * dict_avp_OH = NULL; /* Origin-Host */
40static struct dict_object * dict_avp_OR = NULL; /* Origin-Realm */
41static struct dict_object * dict_avp_EM = NULL; /* Error-Message */
42static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */
43static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */
44static struct dict_object * dict_avp_RC = NULL; /* Result-Code */
45struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */
46struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */
47struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */
48struct dict_object * fd_dict_avp_DC = NULL; /* Disconnect-Cause */
49struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */
50
51/* Resolve the dictionary objects */
52int fd_msg_init(void)
53{
54 TRACE_ENTRY("");
55
56 /* Initialize the dictionary objects that we may use frequently */
57 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &dict_avp_SI , ENOENT) );
58 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &dict_avp_OH , ENOENT) );
59 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &dict_avp_OR , ENOENT) );
60 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", &fd_dict_avp_OSI , ENOENT) );
61
62 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &dict_avp_RC , ENOENT) );
63 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &dict_avp_EM , ENOENT) );
64 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT) );
65 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &dict_avp_FAVP, ENOENT) );
66
67 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", &fd_dict_avp_DC , ENOENT) );
68
69 CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) );
70 CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) );
71 CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) );
72
73
74 return 0;
75}
76
77/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */
78int fd_msg_add_origin ( struct msg * msg, int osi )
79{
80 union avp_value val;
81 struct avp * avp_OH = NULL;
82 struct avp * avp_OR = NULL;
83 struct avp * avp_OSI = NULL;
84
85 TRACE_ENTRY("%p", msg);
86 CHECK_PARAMS( msg );
87
88 /* Create the Origin-Host AVP */
89 CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) );
90
91 /* Set its value */
92 memset(&val, 0, sizeof(val));
93 val.os.data = (os0_t)fd_g_config->cnf_diamid;
94 val.os.len = fd_g_config->cnf_diamid_len;
95 CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) );
96
97 /* Add it to the message */
98 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) );
99
100
101 /* Create the Origin-Realm AVP */
102 CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) );
103
104 /* Set its value */
105 memset(&val, 0, sizeof(val));
106 val.os.data = (os0_t)fd_g_config->cnf_diamrlm;
107 val.os.len = fd_g_config->cnf_diamrlm_len;
108 CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) );
109
110 /* Add it to the message */
111 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) );
112
113 if (osi) {
114 /* Create the Origin-State-Id AVP */
115 CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) );
116
117 /* Set its value */
118 memset(&val, 0, sizeof(val));
119 val.u32 = fd_g_config->cnf_orstateid;
120 CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) );
121
122 /* Add it to the message */
123 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) );
124 }
125
126 return 0;
127}
128
129/* Create a new Session-Id and add at the beginning of the message. */
130int fd_msg_new_session( struct msg * msg, os0_t opt, size_t optlen )
131{
132 union avp_value val;
133 struct avp * avp = NULL;
134 struct session * sess = NULL;
135 os0_t sid;
136 size_t sidlen;
137
138 TRACE_ENTRY("%p %p %zd", msg, opt, optlen);
139 CHECK_PARAMS( msg );
140
141 /* Check there is not already a session in the message */
142 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL) );
143 CHECK_PARAMS( sess == NULL );
144
145 /* Ok, now create the session */
146 CHECK_FCT( fd_sess_new ( &sess, fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len, opt, optlen ) );
147 CHECK_FCT( fd_sess_getsid( sess, &sid, &sidlen) );
148
149 /* Create an AVP to hold it */
150 CHECK_FCT( fd_msg_avp_new( dict_avp_SI, 0, &avp ) );
151
152 /* Set its value */
153 memset(&val, 0, sizeof(val));
154 val.os.data = sid;
155 val.os.len = sidlen;
156 CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
157
158 /* Add it to the message */
159 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_FIRST_CHILD, avp ) );
160
161 /* Save the session associated with the message */
162 CHECK_FCT( fd_msg_sess_set( msg, sess) );
163
164 /* Done! */
165 return 0;
166}
167
168
169/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
170int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
171{
172 union avp_value val;
173 struct avp * avp_RC = NULL;
174 struct avp * avp_EM = NULL;
175 struct avp * avp_ERH = NULL;
176 struct avp * avp_FAVP= NULL;
177 uint32_t rc_val = 0;
178 int set_e_bit=0;
179 int std_err_msg=0;
180
181 TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
182
183 CHECK_PARAMS( msg && rescode );
184
185 /* Find the enum value corresponding to the rescode string, this will give the class of error */
186 {
187 struct dict_object * enum_obj = NULL;
188 struct dict_enumval_request req;
189 memset(&req, 0, sizeof(struct dict_enumval_request));
190
191 /* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
192 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT ) );
193
194 /* Now search for the value given as parameter */
195 req.search.enum_name = rescode;
196 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP) );
197
198 /* finally retrieve its data */
199 CHECK_FCT_DO( fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
200
201 /* copy the found value, we're done */
202 rc_val = req.search.enum_value.u32;
203 }
204
205 if (type_id == 1) {
206 /* Add the Origin-Host and Origin-Realm AVP */
207 CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
208 }
209
210 /* Create the Result-Code AVP */
211 CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
212
213 /* Set its value */
214 memset(&val, 0, sizeof(val));
215 val.u32 = rc_val;
216 CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
217
218 /* Add it to the message */
219 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
220
221 if (type_id == 2) {
222 /* Add the Error-Reporting-Host AVP */
223
224 CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
225
226 /* Set its value */
227 memset(&val, 0, sizeof(val));
228 val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
229 val.os.len = fd_g_config->cnf_diamid_len;
230 CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
231
232 /* Add it to the message */
233 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
234
235 }
236
237 /* Now add the optavp in a FailedAVP if provided */
238 if (optavp) {
239 struct avp * optavp_cpy = NULL;
240 struct avp_hdr *opt_hdr, *optcpy_hdr;
241 struct dict_object * opt_model = NULL;
242 int is_grouped = 0;
243
244 /* Create the Failed-AVP AVP */
245 CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
246
247 /* Was this AVP a grouped one? Best effort only here */
248 if (!fd_msg_model ( optavp, &opt_model ) && (opt_model != NULL)) {
249 struct dict_avp_data dictdata;
250 CHECK_FCT( fd_dict_getval(opt_model, &dictdata) );
251 if (dictdata.avp_basetype == AVP_TYPE_GROUPED)
252 is_grouped = 1;
253 }
254
255 /* Create a new AVP with a copy of the data of the invalid or missing AVP */
256 optavp_cpy = optavp;
257
258 if (is_grouped) {
259 CHECK_FCT( fd_msg_avp_new( opt_model, 0, &optavp_cpy) );
260 } else {
261 CHECK_FCT( fd_msg_avp_new( NULL, AVPFL_SET_BLANK_VALUE | AVPFL_SET_RAWDATA_FROM_AVP, &optavp_cpy) );
262
263 CHECK_FCT( fd_msg_avp_hdr(optavp, &opt_hdr) );
264 CHECK_FCT( fd_msg_avp_hdr(optavp_cpy, &optcpy_hdr) );
265 memcpy(optcpy_hdr, opt_hdr, sizeof(struct avp_hdr));
266 }
267
268 /* Add the passed AVP inside it */
269 CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp_cpy ) );
270
271 /* And add to the message */
272 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
273 }
274
275
276 /* Deal with the 'E' bit and the error message */
277 switch (rc_val / 1000) {
278 case 1: /* Informational */
279 case 2: /* Success */
280 /* Nothing special here: no E bit, no error message unless one is specified */
281 break;
282
283 case 3: /* Protocol Errors */
284 set_e_bit = 1;
285 std_err_msg = 1;
286 break;
287
288 case 4: /* Transcient Failure */
289 case 5: /* Permanent Failure */
290 default:
291 std_err_msg = 1;
292 break;
293
294 }
295
296 {
297 struct msg_hdr * hdr = NULL;
298
299 CHECK_FCT( fd_msg_hdr( msg, &hdr ) );
300
301 if (set_e_bit)
302 hdr->msg_flags |= CMD_FLAG_ERROR;
303 else
304 hdr->msg_flags &= ~ CMD_FLAG_ERROR;
305 }
306
307 if (std_err_msg || errormsg) {
308 /* Add the Error-Message AVP */
309
310 CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
311
312 /* Set its value */
313 memset(&val, 0, sizeof(val));
314
315 if (errormsg) {
316 val.os.data = (uint8_t *)errormsg;
317 val.os.len = strlen(errormsg);
318 } else {
319 val.os.data = (uint8_t *)rescode;
320 val.os.len = strlen(rescode);
321 }
322 CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
323
324 /* Add it to the message */
325 CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
326 }
327
328 return 0;
329}
330
331static int fd_msg_send_int( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
332{
333 struct msg_hdr *hdr;
334 DiamId_t diamid;
335
336 /* Save the callback in the message, with the timeout */
337 CHECK_FCT( fd_msg_anscb_associate( *pmsg, anscb, data, expirecb, timeout ) );
338
339 /* If this is a new request, call the HOOK_MESSAGE_LOCAL hook */
340 if ( (fd_msg_hdr(*pmsg, &hdr) == 0)
341 && (hdr->msg_flags & CMD_FLAG_REQUEST)
342 && (fd_msg_source_get(*pmsg, &diamid, NULL) == 0)
343 && (diamid == NULL)) {
344 fd_hook_call(HOOK_MESSAGE_LOCAL, *pmsg, NULL, NULL, fd_msg_pmdl_get(*pmsg));
345 }
346
347 /* Post the message in the outgoing queue */
348 CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
349
350 return 0;
351}
352
353/* Send a message and optionally register a callback for an answer */
354int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
355{
356 TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
357 CHECK_PARAMS( pmsg );
358
359 return fd_msg_send_int(pmsg, anscb, data, NULL, NULL);
360}
361
362/* The variation of the same function with a timeout callback */
363int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, void (*expirecb)(void *, DiamId_t, size_t, struct msg **), const struct timespec *timeout )
364{
365 TRACE_ENTRY("%p %p %p %p %p", pmsg, anscb, data, expirecb, timeout);
366 CHECK_PARAMS( pmsg && expirecb && timeout );
367
368 return fd_msg_send_int(pmsg, anscb, data, expirecb, timeout);
369}
370
371
372/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
373int fd_msg_parse_or_error( struct msg ** msg, struct msg **error)
374{
375 int ret = 0;
376 struct msg * m;
377 struct msg_hdr * hdr = NULL;
378 struct fd_pei pei;
379
380 TRACE_ENTRY("%p", msg);
381
382 CHECK_PARAMS(msg && *msg && error);
383 m = *msg;
384 *error = NULL;
385
386 /* Parse the message against our dictionary */
387 ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
388 if ((ret != EBADMSG) /* Parsing grouped AVP failed / Conflicting rule found */
389 && (ret != ENOTSUP)) /* Command is not supported / Mandatory AVP is not supported */
390 return ret; /* 0 or another error */
391
392 /* Log */
393 fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, m, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(m));
394
395 CHECK_FCT( fd_msg_hdr(m, &hdr) );
396
397 /* Now create an answer error if the message is a query */
398 if (hdr->msg_flags & CMD_FLAG_REQUEST) {
399
400 /* Create the error message */
401 CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &m, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
402
403 /* Set the error code */
404 CHECK_FCT( fd_msg_rescode_set(m, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
405
406 /* free the pei AVP to avoid memory leak */
407 if (pei.pei_avp_free) {
408 fd_msg_free(pei.pei_avp);
409 }
410
411 *msg = NULL;
412 *error = m;
413
414 } else {
415 do { /* Rescue error messages */
416 struct avp * avp;
417 union avp_value * rc = NULL;
418
419 /* Search the Result-Code AVP */
420 CHECK_FCT_DO( fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break );
421 while (avp) {
422 struct avp_hdr * ahdr;
423 CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), break );
424
425 if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
426 /* Parse this AVP */
427 ASSERT( ahdr->avp_value );
428 rc = ahdr->avp_value;
429 break;
430 }
431
432 /* Go to next AVP */
433 CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break );
434 }
435
436 if (rc) {
437 switch (rc->u32 / 1000) {
438 case 1: /* 1xxx : Informational */
439 case 2: /* 2xxx : Sucess */
440 /* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
441 break;
442
443 default: /* Other errors */
444 /* We let the application decide what to do with the message, we rescue it */
445 *error = m;
446 }
447 }
448 } while (0);
449 }
450
451 return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
452}