Mercurial > hg > freeDiameter
diff libfdcore/messages.c @ 658:f198d16fa7f4
Initial commit for 1.1.0:
* Restructuring:
* libfreeDiameter:
- renamed folder & binary into libfdproto
- renamed libfD.h into fdproto-internal.h
- removed signals management (replaced by triggers in libfdcore)
* freeDiameter split into:
- libfdcore (most contents)
- renamed fD.h into fdcore-internal.h
- added core.c for framework init/shutdown.
- new triggers mechanism in events.c.
- freeDiameterd (main, command line parsing, signals management)
* tests:
- now in top-level directory tests.
* other changes:
- fd_dict_new now returns 0 on duplicate identical entries.
- fixes in dict_legacy_xml
- fixes in some dictionaries
- moved FD_DEFAULT_CONF_FILENAME definition to freeDiameter-host.h
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Fri, 14 Jan 2011 15:15:23 +0900 |
parents | freeDiameter/messages.c@ae29bf971f20 |
children | 8c3dc8584dab |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfdcore/messages.c Fri Jan 14 15:15:23 2011 +0900 @@ -0,0 +1,371 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@nict.go.jp> * +* * +* Copyright (c) 2011, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +#include "fdcore-internal.h" + +static struct dict_object * dict_avp_OH = NULL; /* Origin-Host */ +static struct dict_object * dict_avp_OR = NULL; /* Origin-Realm */ +static struct dict_object * dict_avp_EM = NULL; /* Error-Message */ +static struct dict_object * dict_avp_ERH = NULL; /* Error-Reporting-Host */ +static struct dict_object * dict_avp_FAVP= NULL; /* Failed-AVP */ +static struct dict_object * dict_avp_RC = NULL; /* Result-Code */ +struct dict_object * fd_dict_avp_OSI = NULL; /* Origin-State-Id */ +struct dict_object * fd_dict_cmd_CER = NULL; /* Capabilities-Exchange-Request */ +struct dict_object * fd_dict_cmd_DWR = NULL; /* Device-Watchdog-Request */ +struct dict_object * fd_dict_avp_DC = NULL; /* Disconnect-Cause */ +struct dict_object * fd_dict_cmd_DPR = NULL; /* Disconnect-Peer-Request */ + +/* Resolve the dictionary objects */ +int fd_msg_init(void) +{ + TRACE_ENTRY(""); + + /* Initialize the dictionary objects that we may use frequently */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &dict_avp_OH , ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &dict_avp_OR , ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-State-Id", &fd_dict_avp_OSI , ENOENT) ); + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &dict_avp_RC , ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &dict_avp_EM , ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &dict_avp_ERH , ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &dict_avp_FAVP, ENOENT) ); + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Disconnect-Cause", &fd_dict_avp_DC , ENOENT) ); + + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Capabilities-Exchange-Request", &fd_dict_cmd_CER, ENOENT ) ); + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request", &fd_dict_cmd_DWR, ENOENT ) ); + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Disconnect-Peer-Request", &fd_dict_cmd_DPR, ENOENT ) ); + + + return 0; +} + +/* Add Origin-Host, Origin-Realm, Origin-State-Id AVPS at the end of the message */ +int fd_msg_add_origin ( struct msg * msg, int osi ) +{ + union avp_value val; + struct avp * avp_OH = NULL; + struct avp * avp_OR = NULL; + struct avp * avp_OSI = NULL; + + TRACE_ENTRY("%p", msg); + CHECK_PARAMS( msg ); + + /* Create the Origin-Host AVP */ + CHECK_FCT( fd_msg_avp_new( dict_avp_OH, 0, &avp_OH ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + val.os.data = (unsigned char *)fd_g_config->cnf_diamid; + val.os.len = fd_g_config->cnf_diamid_len; + CHECK_FCT( fd_msg_avp_setvalue( avp_OH, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OH ) ); + + + /* Create the Origin-Realm AVP */ + CHECK_FCT( fd_msg_avp_new( dict_avp_OR, 0, &avp_OR ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + val.os.data = (unsigned char *)fd_g_config->cnf_diamrlm; + val.os.len = fd_g_config->cnf_diamrlm_len; + CHECK_FCT( fd_msg_avp_setvalue( avp_OR, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OR ) ); + + if (osi) { + /* Create the Origin-State-Id AVP */ + CHECK_FCT( fd_msg_avp_new( fd_dict_avp_OSI, 0, &avp_OSI ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + val.u32 = fd_g_config->cnf_orstateid; + CHECK_FCT( fd_msg_avp_setvalue( avp_OSI, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_OSI ) ); + } + + return 0; +} + +/* Add Result-Code and eventually Failed-AVP, Error-Message and Error-Reporting-Host AVPs */ +int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id ) +{ + union avp_value val; + struct avp * avp_RC = NULL; + struct avp * avp_EM = NULL; + struct avp * avp_ERH = NULL; + struct avp * avp_FAVP= NULL; + uint32_t rc_val = 0; + int set_e_bit=0; + int std_err_msg=0; + + TRACE_ENTRY("%p %s %p %p %d", msg, rescode, errormsg, optavp, type_id); + + CHECK_PARAMS( msg && rescode ); + + /* Find the enum value corresponding to the rescode string, this will give the class of error */ + { + struct dict_object * enum_obj = NULL; + struct dict_enumval_request req; + memset(&req, 0, sizeof(struct dict_enumval_request)); + + /* First, get the enumerated type of the Result-Code AVP */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_TYPE, TYPE_OF_AVP, dict_avp_RC, &(req.type_obj), ENOENT ) ); + + /* Now search for the value given as parameter */ + req.search.enum_name = rescode; + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enum_obj, ENOTSUP) ); + + /* finally retrieve its data */ + CHECK_FCT_DO( fd_dict_getval( enum_obj, &(req.search) ), return EINVAL ); + + /* copy the found value, we're done */ + rc_val = req.search.enum_value.u32; + } + + if (type_id == 1) { + /* Add the Origin-Host and Origin-Realm AVP */ + CHECK_FCT( fd_msg_add_origin ( msg, 0 ) ); + } + + /* Create the Result-Code AVP */ + CHECK_FCT( fd_msg_avp_new( dict_avp_RC, 0, &avp_RC ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + val.u32 = rc_val; + CHECK_FCT( fd_msg_avp_setvalue( avp_RC, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_RC ) ); + + if (type_id == 2) { + /* Add the Error-Reporting-Host AVP */ + + CHECK_FCT( fd_msg_avp_new( dict_avp_ERH, 0, &avp_ERH ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + val.os.data = (unsigned char *)fd_g_config->cnf_diamid; + val.os.len = fd_g_config->cnf_diamid_len; + CHECK_FCT( fd_msg_avp_setvalue( avp_ERH, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_ERH ) ); + + } + + /* Now add the optavp in a FailedAVP if provided */ + if (optavp) { + /* Create the Failed-AVP AVP */ + CHECK_FCT( fd_msg_avp_new( dict_avp_FAVP, 0, &avp_FAVP ) ); + + /* Add the passed AVP inside it */ + CHECK_FCT( fd_msg_avp_add( avp_FAVP, MSG_BRW_LAST_CHILD, optavp ) ); + + /* And add to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_FAVP ) ); + } + + + /* Deal with the 'E' bit and the error message */ + switch (rc_val / 1000) { + case 1: /* Informational */ + case 2: /* Success */ + /* Nothing special here: no E bit, no error message unless one is specified */ + break; + + case 3: /* Protocol Errors */ + set_e_bit = 1; + std_err_msg = 1; + break; + + case 4: /* Transcient Failure */ + case 5: /* Permanent Failure */ + default: + std_err_msg = 1; + break; + + } + + { + struct msg_hdr * hdr = NULL; + + CHECK_FCT( fd_msg_hdr( msg, &hdr ) ); + + if (set_e_bit) + hdr->msg_flags |= CMD_FLAG_ERROR; + else + hdr->msg_flags &= ! CMD_FLAG_ERROR; + } + + if (std_err_msg || errormsg) { + /* Add the Error-Message AVP */ + + CHECK_FCT( fd_msg_avp_new( dict_avp_EM, 0, &avp_EM ) ); + + /* Set its value */ + memset(&val, 0, sizeof(val)); + + if (errormsg) { + val.os.data = (unsigned char *)errormsg; + val.os.len = strlen(errormsg); + } else { + val.os.data = (unsigned char *)rescode; + val.os.len = strlen(rescode); + } + CHECK_FCT( fd_msg_avp_setvalue( avp_EM, &val ) ); + + /* Add it to the message */ + CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp_EM ) ); + } + + return 0; +} + +/* Send a message and optionaly register a callback for an answer */ +int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data ) +{ + TRACE_ENTRY("%p %p %p", pmsg, anscb, data); + CHECK_PARAMS( pmsg ); + + /* Save the callback in the message */ + if (anscb) { + CHECK_FCT( fd_msg_anscb_associate( *pmsg, anscb, data, NULL /* we should maybe use a safeguard here like 1 hour or so? */ ) ); + } + + /* Post the message in the outgoing queue */ + CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) ); + + return 0; +} + +/* The variation of the same function with a timeout callback */ +int fd_msg_send_timeout ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data, const struct timespec *timeout ) +{ + TRACE_ENTRY("%p %p %p", pmsg, anscb, data, timeout); + CHECK_PARAMS( pmsg && anscb && timeout ); + + /* Save the callback in the message, with the timeout */ + CHECK_FCT( fd_msg_anscb_associate( *pmsg, anscb, data, timeout ) ); + + /* Post the message in the outgoing queue */ + CHECK_FCT( fd_fifo_post(fd_g_outgoing, pmsg) ); + + return 0; +} + + +/* Parse a message against our dictionary, and in case of error log and eventually build the error reply -- returns the parsing status */ +int fd_msg_parse_or_error( struct msg ** msg ) +{ + int ret = 0; + struct msg * m; + struct msg_hdr * hdr = NULL; + struct fd_pei pei; + + TRACE_ENTRY("%p", msg); + + CHECK_PARAMS(msg && *msg); + m = *msg; + + /* Parse the message against our dictionary */ + ret = fd_msg_parse_rules ( m, fd_g_config->cnf_dict, &pei); + if ((ret != EBADMSG) /* Parsing grouped AVP failed / Conflicting rule found */ + && (ret != ENOTSUP)) /* Command is not supported / Mandatory AVP is not supported */ + return ret; + + fd_log_debug("The following message does not comply to the dictionary and/or rules (%s):\n", pei.pei_errcode); + fd_msg_dump_walk(NONE, m); + + CHECK_FCT( fd_msg_hdr(m, &hdr) ); + + /* Now create an answer error if the message is a query */ + if (hdr->msg_flags & CMD_FLAG_REQUEST) { + + /* Create the error message */ + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, pei.pei_protoerr ? MSGFL_ANSW_ERROR : 0 ) ); + + /* Set the error code */ + CHECK_FCT( fd_msg_rescode_set(*msg, pei.pei_errcode, pei.pei_message, pei.pei_avp, 1 ) ); + + } else { + do { /* Rescue error messages */ + struct avp * avp; + union avp_value * rc = NULL; + + /* Search the Result-Code AVP */ + CHECK_FCT_DO( fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL), break ); + while (avp) { + struct avp_hdr * ahdr; + CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), break ); + + if ((ahdr->avp_code == AC_RESULT_CODE) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) { + /* Parse this AVP */ + ASSERT( ahdr->avp_value ); + rc = ahdr->avp_value; + break; + } + + /* Go to next AVP */ + CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), break ); + } + + if (rc) { + switch (rc->u32 / 1000) { + case 1: /* 1xxx : Informational */ + case 2: /* 2xxx : Sucess */ + /* In these cases, we want the message to validate the ABNF, so we will discard the bad message */ + break; + + default: /* Other errors */ + /* We let the application decide what to do with the message, we rescue it */ + return 0; + } + } + } while (0); + + /* Just discard */ + CHECK_FCT( fd_msg_free( m ) ); + *msg = NULL; + } + + return EBADMSG; /* We convert ENOTSUP to EBADMSG as well */ +}