Navigation


source: freeDiameter/libfdcore/messages.c @ 706:4ffbc9f1e922

Last change on this file since 706:4ffbc9f1e922 was 706:4ffbc9f1e922, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Large UNTESTED commit with the following changes:

  • Improved DiameterIdentity? handling (esp. interationalization issues), and improve efficiency of some string operations in peers, sessions, and dictionary modules (closes #7)
  • Cleanup in the session module to free only unreferenced sessions (#16)
  • Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
  • Improved peer state machine algorithm to counter SCTP multistream race condition.
File size: 14.3 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2011, 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        /* Done! */
162        return 0;
163}
164
165
166/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */
167int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id )
168{
169        union avp_value val;
170        struct avp * avp_RC  = NULL;
171        struct avp * avp_EM  = NULL;
172        struct avp * avp_ERH = NULL;
173        struct avp * avp_FAVP= NULL;
174        uint32_t rc_val = 0;
175        int set_e_bit=0;
176        int std_err_msg=0;
177       
178        TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id);
179               
180        CHECK_PARAMS(  msg && rescode  );
181       
182        /* Find the enum value corresponding to the rescode string, this will give the class of error */
183        {
184                struct dict_object * enum_obj = NULL;
185                struct dict_enumval_request req;
186                memset(&req, 0, sizeof(struct dict_enumval_request));
187               
188                /* First, get the enumerated type of the Result-Code AVP (this is fast, no need to cache the object) */
189                CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT  )  );
190               
191                /* Now search for the value given as parameter */
192                req.search.enum_name = rescode;
193                CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP)  );
194               
195                /* finally retrieve its data */
196                CHECK_FCT_DO(  fd_dict_getval( enum_obj, &(req.search) ), return EINVAL );
197               
198                /* copy the found value, we're done */
199                rc_val = req.search.enum_value.u32;
200        }
201       
202        if (type_id == 1) {
203                /* Add the Origin-Host and Origin-Realm AVP */
204                CHECK_FCT( fd_msg_add_origin ( msg, 0 ) );
205        }
206       
207        /* Create the Result-Code AVP */
208        CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) );
209       
210        /* Set its value */
211        memset(&val, 0, sizeof(val));
212        val.u32  = rc_val;
213        CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) );
214       
215        /* Add it to the message */
216        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) );
217       
218        if (type_id == 2) {
219                /* Add the Error-Reporting-Host AVP */
220               
221                CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) );
222
223                /* Set its value */
224                memset(&val, 0, sizeof(val));
225                val.os.data = (uint8_t *)fd_g_config->cnf_diamid;
226                val.os.len  = fd_g_config->cnf_diamid_len;
227                CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) );
228
229                /* Add it to the message */
230                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) );
231       
232        }
233       
234        /* Now add the optavp in a FailedAVP if provided */
235        if (optavp) {
236                /* Create the Failed-AVP AVP */
237                CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) );
238
239                /* Add the passed AVP inside it */
240                CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp ) );
241               
242                /* And add to the message */
243                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) );
244        }
245       
246       
247        /* Deal with the 'E' bit and the error message */
248        switch (rc_val / 1000) {
249                case 1: /* Informational */
250                case 2: /* Success */
251                        /* Nothing special here: no E bit, no error message unless one is specified */
252                        break;
253                       
254                case 3: /* Protocol Errors */
255                        set_e_bit = 1;
256                        std_err_msg = 1;
257                        break;
258                       
259                case 4: /* Transcient Failure */
260                case 5: /* Permanent Failure */
261                default:
262                        std_err_msg = 1;
263                        break;
264                       
265        }
266       
267        {
268                struct msg_hdr * hdr = NULL;
269               
270                CHECK_FCT(  fd_msg_hdr( msg, &hdr )  );
271               
272                if (set_e_bit)
273                        hdr->msg_flags |= CMD_FLAG_ERROR;
274                else
275                        hdr->msg_flags &= ! CMD_FLAG_ERROR;
276        }
277       
278        if (std_err_msg || errormsg) {
279                /* Add the Error-Message AVP */
280               
281                CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) );
282
283                /* Set its value */
284                memset(&val, 0, sizeof(val));
285               
286                if (errormsg) {
287                        val.os.data = (uint8_t *)errormsg;
288                        val.os.len  = strlen(errormsg);
289                } else {
290                        val.os.data = (uint8_t *)rescode;
291                        val.os.len  = strlen(rescode);
292                }
293                CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) );
294
295                /* Add it to the message */
296                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) );
297        }
298       
299        return 0;
300}
301
302/* Send a message and optionaly register a callback for an answer */
303int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data )
304{
305        TRACE_ENTRY("%p %p %p", pmsg, anscb, data);
306        CHECK_PARAMS( pmsg );
307       
308        /* Save the callback in the message */
309        if (anscb) {
310                CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, NULL /* we should maybe use a safeguard here like 1 hour or so? */ )  );
311        }
312       
313        /* Post the message in the outgoing queue */
314        CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
315       
316        return 0;
317}
318
319/* The variation of the same function with a timeout callback */
320int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, const struct timespec *timeout )
321{
322        TRACE_ENTRY("%p %p %p", pmsg, anscb, data, timeout);
323        CHECK_PARAMS( pmsg && anscb && timeout );
324       
325        /* Save the callback in the message, with the timeout */
326        CHECK_FCT(  fd_msg_anscb_associate( *pmsg, anscb, data, timeout )  );
327       
328        /* Post the message in the outgoing queue */
329        CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) );
330       
331        return 0;
332}
333
334
335/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */
336int fd_msg_parse_or_error( struct msg ** msg )
337{
338        int ret = 0;
339        struct msg * m;
340        struct msg_hdr * hdr = NULL;
341        struct fd_pei   pei;
342       
343        TRACE_ENTRY("%p", msg);
344       
345        CHECK_PARAMS(msg && *msg);
346        m = *msg;
347       
348        /* Parse the message against our dictionary */
349        ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei);
350        if      ((ret != EBADMSG)       /* Parsing grouped AVP failed / Conflicting rule found */
351                && (ret != ENOTSUP))    /* Command is not supported / Mandatory AVP is not supported */
352                return ret; /* 0 or another error */
353       
354        TRACE_DEBUG(INFO, "A message does not comply to the dictionary and/or rules (%s)", pei.pei_errcode);
355        fd_msg_dump_walk(FULL, m);
356       
357        CHECK_FCT( fd_msg_hdr(m, &hdr) );
358       
359        /* Now create an answer error if the message is a query */
360        if (hdr->msg_flags & CMD_FLAG_REQUEST) {
361               
362                /* Create the error message */
363                CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) );
364               
365                /* Set the error code */
366                CHECK_FCT( fd_msg_rescode_set(*msg, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) );
367               
368        } else {
369                do { /* Rescue error messages */
370                        struct avp * avp;
371                        union avp_value * rc = NULL;
372                       
373                        /* Search the Result-Code AVP */
374                        CHECK_FCT_DO(  fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break  );
375                        while (avp) {
376                                struct avp_hdr * ahdr;
377                                CHECK_FCT_DO(  fd_msg_avp_hdr( avp, &ahdr ), break  );
378                               
379                                if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) {
380                                        /* Parse this AVP */
381                                        ASSERT( ahdr->avp_value );
382                                        rc = ahdr->avp_value;
383                                        break;
384                                }
385                               
386                                /* Go to next AVP */
387                                CHECK_FCT_DO(  fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break  );
388                        }
389                       
390                        if (rc) {
391                                switch (rc->u32 / 1000) {
392                                        case 1: /* 1xxx : Informational */
393                                        case 2: /* 2xxx : Sucess */
394                                                /* In these cases, we want the message to validate the ABNF, so we will discard the bad message */
395                                                break;
396                                               
397                                        default: /* Other errors */
398                                                /* We let the application decide what to do with the message, we rescue it */
399                                                return 0;
400                                }
401                        }
402                } while (0);
403               
404                /* Just discard */
405                fd_msg_log( FD_MSG_LOG_DROPPED, m, "Answer not compliant to dictionary's ABNF (%s)", pei.pei_errcode  );
406                CHECK_FCT( fd_msg_free( m ) );
407                *msg = NULL;
408        }
409       
410        return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */
411}
Note: See TracBrowser for help on using the repository browser.