# HG changeset patch # User Sebastien Decugis # Date 1252055125 -32400 # Node ID e5af94b0494675763538df3dbbecd9931785f288 # Parent b0d377c79d80a99a54b281ecdab63b32bdde08b2 Added dispatch module and tests diff -r b0d377c79d80 -r e5af94b04946 freeDiameter/tests/CMakeLists.txt --- a/freeDiameter/tests/CMakeLists.txt Thu Sep 03 16:03:25 2009 +0900 +++ b/freeDiameter/tests/CMakeLists.txt Fri Sep 04 18:05:25 2009 +0900 @@ -16,6 +16,7 @@ testmesg testmq testsess + testdisp ) ############################# diff -r b0d377c79d80 -r e5af94b04946 freeDiameter/tests/testdisp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/freeDiameter/tests/testdisp.c Fri Sep 04 18:05:25 2009 +0900 @@ -0,0 +1,674 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, 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 "tests.h" + +#define Define_cb( __int, __extra ) \ +int cb_##__int( struct msg ** msg, struct avp * avp, struct session * session, enum disp_action * action ) \ +{ \ + CHECK( 1, msg ? 1 : 0 ); \ + CHECK( 1, action ? 1 : 0 ); \ + CHECK( sess, session ); \ + *action = DISP_ACT_CONT; \ + cbcalled[__int] += 1; \ + do { \ + __extra ; \ + } while (0); \ + return 0; \ +} + +#define NB_CB 10 +char cbcalled[NB_CB]; +struct session * sess; + +/* cb_0 */ Define_cb( 0, ); +/* cb_1 */ Define_cb( 1, ); +/* cb_2 */ Define_cb( 2, ); +/* cb_3 */ Define_cb( 3, ); +/* cb_4 */ Define_cb( 4, ); +/* cb_5 */ Define_cb( 5, ); +/* cb_6 */ Define_cb( 6, return 12345 ); +/* cb_7 */ Define_cb( 7, { CHECK( 1, avp ? 1 : 0 ); } ); +/* cb_8 */ Define_cb( 8, { CHECK( 0, fd_msg_free( *msg ) ); *msg = NULL; } ); +/* cb_9 */ Define_cb( 9, *action = DISP_ACT_SEND ); +/* max: cb_ */ + +/* Create a new message containing what we want */ +struct msg * new_msg(int appid, struct dict_object * cmd, struct dict_object * avp1, struct dict_object * avp2, int val) +{ + struct msg *new; + struct avp *avp; + union avp_value value; + struct msg_hdr * msg_hdr = NULL; + + CHECK( 0, fd_msg_new ( cmd, 0, &new ) ); + CHECK( 0, fd_msg_hdr ( new, &msg_hdr ) ); + msg_hdr->msg_appl = appid; + + if (avp1) { + CHECK( 0, fd_msg_avp_new ( avp1, 0, &avp ) ); + value.u32 = 0; + CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) ); + CHECK( 0, fd_msg_avp_add ( new, MSG_BRW_LAST_CHILD, avp ) ); + } + + if (avp2) { + CHECK( 0, fd_msg_avp_new ( avp2, 0, &avp ) ); + value.u32 = val; + CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) ); + CHECK( 0, fd_msg_avp_add ( new, MSG_BRW_LAST_CHILD, avp ) ); + } + + return new; +} + +/* Main test routine */ +int main(int argc, char *argv[]) +{ + struct dict_object * app1, * app2; + struct dict_object * cmd1, * cmd2; + struct dict_object * avp1, * avp2; /* avp2 is enumerated; they are both unsigned32 types */ + struct dict_object * enu1, * enu2; + struct msg * msg = NULL; + enum disp_action action; + struct disp_hdl * hdl[NB_CB]; + struct disp_when when; + + /* First, initialize the daemon modules */ + INIT_FD(); + + /* Create a dummy session, we don't use it anyway */ + CHECK( 0, fd_sess_new( &sess, "test.disp", NULL, 0 ) ); + + memset(&when, 0xff, sizeof(when)); /* check that we don't use un-initialized parts */ + + /* Initialize dictionary objects */ + { + struct dict_object * enutype; + struct dict_application_data app1_data = { 1, "Application test 1" }; + struct dict_application_data app2_data = { 2, "Application test 2" }; + struct dict_cmd_data cmd1_data = { 1, "Command test 1 (req)", CMD_FLAG_REQUEST, CMD_FLAG_REQUEST }; + struct dict_cmd_data cmd2_data = { 1, "Command test 2 (ans)", CMD_FLAG_REQUEST, 0 }; + struct dict_type_data type_data = { AVP_TYPE_UNSIGNED32, "Type test", NULL, NULL }; + struct dict_avp_data avp1_data = { 10001, 0, "AVP test 1", 0, 0, AVP_TYPE_UNSIGNED32 }; + struct dict_avp_data avp2_data = { 10002, 0, "AVP test 2", 0, 0, AVP_TYPE_UNSIGNED32 }; + struct dict_enumval_data enu1_data = { "ENU test 1", { .u32 = 1 }}; + struct dict_enumval_data enu2_data = { "ENU test 2", { .u32 = 2 }}; + + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_APPLICATION, &app1_data, NULL, &app1 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_APPLICATION, &app2_data, NULL, &app2 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_COMMAND, &cmd1_data, NULL, &cmd1 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_COMMAND, &cmd2_data, NULL, &cmd2 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_TYPE, &type_data, NULL, &enutype ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp1_data, NULL, &avp1 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_AVP, &avp2_data, enutype, &avp2 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &enu1_data, enutype, &enu1 ) ); + CHECK( 0, fd_dict_new ( fd_g_dict, DICT_ENUMVAL, &enu2_data, enutype, &enu2 ) ); + } + + /* Register first handler, very simple test */ + { + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, &hdl[0] ) ); + CHECK( 1, hdl[0] ? 1 : 0 ); + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( NULL, hdl[0] ); + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, NULL, &hdl[0] ) ); + + /* Check this handler is called for a message */ + msg = new_msg( 0, cmd1, avp1, NULL, 0 ); + memset(cbcalled, 0, sizeof(cbcalled)); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( DISP_ACT_CONT, action ); + + /* Delete the message */ + CHECK( 0, fd_msg_free( msg ) ); + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + } + + /* Handlers for applications */ + { + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); + when.app = app1; + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_APPID, &when, &hdl[1] ) ); + when.app = app2; + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_APPID, &when, &hdl[2] ) ); + when.avp = avp2; + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_APPID, &when, &hdl[3] ) ); + when.avp = avp1; + CHECK( 0, fd_disp_register( cb_4, DISP_HOW_APPID, &when, &hdl[4] ) ); + + /* Check the callbacks are called as appropriate */ + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 0, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 1, cbcalled[4] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + CHECK( 0, fd_disp_unregister( &hdl[4] ) ); + } + + /* Handlers for commands */ + { + when.app = NULL; + when.command = NULL; + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); + CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_CC, &when, &hdl[1] ) ); + when.command = cmd1; + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_CC, &when, &hdl[1] ) ); /* cmd1 */ + when.app = app2; + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_CC, &when, &hdl[2] ) ); /* app2 + cmd1 */ + when.command = cmd2; + when.app = NULL; + when.avp = avp1; + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_CC, &when, &hdl[3] ) ); /* cmd2 (avp1 ignored) */ + + /* Check the callbacks are called as appropriate */ + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 0, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd2, NULL, avp2, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + } + + /* Handlers for AVPs */ + { + when.app = NULL; + when.command = NULL; + when.avp = NULL; + + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); /* all */ + CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP, &when, &hdl[1] ) ); + + when.avp = avp1; + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP, &when, &hdl[1] ) ); /* avp1 */ + + when.command = cmd1; + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP, &when, &hdl[2] ) ); /* avp1 + cmd1 */ + + when.command = NULL; + when.app = app1; + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_AVP, &when, &hdl[3] ) ); /* avp1 + app1 */ + + when.command = cmd1; + CHECK( 0, fd_disp_register( cb_4, DISP_HOW_AVP, &when, &hdl[4] ) ); /* avp1 + cmd1 + app1 */ + + when.app = NULL; + when.command = NULL; + when.avp = avp2; + when.value = enu1; + CHECK( 0, fd_disp_register( cb_5, DISP_HOW_AVP, &when, &hdl[5] ) ); /* avp2 */ + + when.value = enu2; + CHECK( 0, fd_disp_register( cb_7, DISP_HOW_AVP, &when, &hdl[6] ) ); /* avp2 */ + + + + /* Check the callbacks are called as appropriate */ + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 0, cmd1, NULL, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 0, cbcalled[5] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 0, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 0, cbcalled[5] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd2, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 0, cbcalled[5] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 1, cbcalled[4] ); + CHECK( 0, cbcalled[5] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, avp2, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 1, cbcalled[4] ); + CHECK( 1, cbcalled[5] ); + CHECK( 1, cbcalled[7] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, NULL, avp2, 1 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 1, cbcalled[5] ); + CHECK( 1, cbcalled[7] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, NULL, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 1, cbcalled[5] ); + CHECK( 1, cbcalled[7] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + CHECK( 0, fd_disp_unregister( &hdl[4] ) ); + CHECK( 0, fd_disp_unregister( &hdl[5] ) ); + CHECK( 0, fd_disp_unregister( &hdl[6] ) ); + } + + /* Handlers for enum values */ + { + when.app = NULL; + when.command = NULL; + when.avp = NULL; + when.value = NULL; + + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); /* all */ + CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, &hdl[1] ) ); + when.value = enu1; + CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, &hdl[1] ) ); + when.avp = avp1; + CHECK( EINVAL, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, &hdl[1] ) ); + when.avp = avp2; + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, &hdl[1] ) ); /* avp2, enu1 */ + + when.command = cmd1; + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP_ENUMVAL, &when, &hdl[2] ) ); /* avp2, enu1 + cmd1 */ + + when.command = NULL; + when.app = app1; + when.value = enu2; + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_AVP_ENUMVAL, &when, &hdl[3] ) ); /* avp2, enu2 + app1 */ + + /* Check the callbacks are called as appropriate */ + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 0, cmd1, avp1, NULL, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd2, avp1, avp2, 0 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd2, avp1, avp2, 1 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, avp2, 1 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd2, avp1, avp2, 1 ); + { + struct avp *avp; + union avp_value value; + CHECK( 0, fd_msg_avp_new ( avp2, 0, &avp ) ); + value.u32 = 2; + CHECK( 0, fd_msg_avp_setvalue ( avp, &value ) ); + CHECK( 0, fd_msg_avp_add ( msg, MSG_BRW_LAST_CHILD, avp ) ); + } + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( DISP_ACT_CONT, action ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + } + + /* Test behavior of handlers */ + { + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, &hdl[1] ) ); + CHECK( 0, fd_disp_register( cb_6, DISP_HOW_ANY, &when, &hdl[2] ) ); + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, &hdl[3] ) ); + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, &hdl[4] ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, avp2, 1 ); + CHECK( 12345, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[6] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + CHECK( 0, fd_disp_unregister( &hdl[4] ) ); + + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, &hdl[1] ) ); + CHECK( 0, fd_disp_register( cb_8, DISP_HOW_ANY, &when, &hdl[2] ) ); + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, &hdl[3] ) ); + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, &hdl[4] ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, avp2, 1 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[8] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( NULL, msg ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + CHECK( 0, fd_disp_unregister( &hdl[4] ) ); + + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_ANY, &when, &hdl[1] ) ); + CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, &hdl[2] ) ); + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_ANY, &when, &hdl[3] ) ); + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_ANY, &when, &hdl[4] ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 1, cmd1, avp1, avp2, 1 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[9] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( DISP_ACT_SEND, action ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + CHECK( 0, fd_disp_unregister( &hdl[4] ) ); + } + + /* Test order of handlers */ + { + when.app = app2; + when.command = cmd2; + when.avp = avp2; + when.value = enu2; + + CHECK( 0, fd_disp_register( cb_0, DISP_HOW_ANY, &when, &hdl[0] ) ); + CHECK( 0, fd_disp_register( cb_1, DISP_HOW_AVP_ENUMVAL, &when, &hdl[1] ) ); + CHECK( 0, fd_disp_register( cb_2, DISP_HOW_AVP, &when, &hdl[2] ) ); + CHECK( 0, fd_disp_register( cb_3, DISP_HOW_CC, &when, &hdl[3] ) ); + CHECK( 0, fd_disp_register( cb_4, DISP_HOW_APPID, &when, &hdl[4] ) ); + + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 1, cbcalled[4] ); + CHECK( 0, cbcalled[9] ); + CHECK( 0, fd_msg_free( msg ) ); + + CHECK( 0, fd_disp_register( cb_9, DISP_HOW_ANY, &when, &hdl[5] ) ); + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 0, cbcalled[1] ); + CHECK( 0, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 1, cbcalled[9] ); + CHECK( 0, fd_msg_free( msg ) ); + CHECK( 0, fd_disp_unregister( &hdl[5] ) ); + + CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP_ENUMVAL, &when, &hdl[5] ) ); + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 1, cbcalled[9] ); + CHECK( 0, fd_msg_free( msg ) ); + CHECK( 0, fd_disp_unregister( &hdl[5] ) ); + + CHECK( 0, fd_disp_register( cb_9, DISP_HOW_AVP, &when, &hdl[5] ) ); + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 0, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 1, cbcalled[9] ); + CHECK( 0, fd_msg_free( msg ) ); + CHECK( 0, fd_disp_unregister( &hdl[5] ) ); + + CHECK( 0, fd_disp_register( cb_9, DISP_HOW_CC, &when, &hdl[5] ) ); + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 0, cbcalled[4] ); + CHECK( 1, cbcalled[9] ); + CHECK( 0, fd_msg_free( msg ) ); + CHECK( 0, fd_disp_unregister( &hdl[5] ) ); + + CHECK( 0, fd_disp_register( cb_9, DISP_HOW_APPID, &when, &hdl[5] ) ); + memset(cbcalled, 0, sizeof(cbcalled)); + msg = new_msg( 2, cmd2, avp1, avp2, 2 ); + CHECK( 0, fd_msg_dispatch ( &msg, sess, &action ) ); + CHECK( 1, cbcalled[0] ); + CHECK( 1, cbcalled[1] ); + CHECK( 1, cbcalled[2] ); + CHECK( 1, cbcalled[3] ); + CHECK( 1, cbcalled[4] ); + CHECK( 1, cbcalled[9] ); + CHECK( 0, fd_msg_free( msg ) ); + CHECK( 0, fd_disp_unregister( &hdl[5] ) ); + + CHECK( 0, fd_disp_unregister( &hdl[0] ) ); + CHECK( 0, fd_disp_unregister( &hdl[1] ) ); + CHECK( 0, fd_disp_unregister( &hdl[2] ) ); + CHECK( 0, fd_disp_unregister( &hdl[3] ) ); + CHECK( 0, fd_disp_unregister( &hdl[4] ) ); + } + + /* That's all for the tests yet */ + PASSTEST(); +} + diff -r b0d377c79d80 -r e5af94b04946 include/freeDiameter/freeDiameter.h --- a/include/freeDiameter/freeDiameter.h Thu Sep 03 16:03:25 2009 +0900 +++ b/include/freeDiameter/freeDiameter.h Fri Sep 04 18:05:25 2009 +0900 @@ -110,4 +110,35 @@ int fd_msg_add_origin ( struct msg * msg, struct dictionary * dict, int osi ); /* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */ + +/***************************************/ +/* Dispatch module, daemon's part */ +/***************************************/ + +enum { + DISP_APP_AUTH = 1, + DISP_APP_ACCT = 2 +}; +/* + * FUNCTION: fd_disp_app_support + * + * PARAMETERS: + * app : The dictionary object corresponding to the Application. + * vendor : (Optional) the dictionary object of a Vendor to claim support in Vendor-Specific-Application-Id + * flags : Combination of DISP_APP_* flags. + * + * DESCRIPTION: + * Registers an application to be advertized in CER/CEA exchanges. + * Messages with an application-id matching a registered value are passed to the dispatch module, + * while other messages are simply relayed or an error is returned (if local node does not relay) + * + * RETURN VALUE: + * 0 : The application support is registered. + * EINVAL : A parameter is invalid. + */ +int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int flags ); + +/* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */ + + #endif /* _FREEDIAMETER_H */ diff -r b0d377c79d80 -r e5af94b04946 include/freeDiameter/libfreeDiameter.h --- a/include/freeDiameter/libfreeDiameter.h Thu Sep 03 16:03:25 2009 +0900 +++ b/include/freeDiameter/libfreeDiameter.h Fri Sep 04 18:05:25 2009 +0900 @@ -1507,124 +1507,6 @@ /*============================================================*/ -/* DISPATCH */ -/*============================================================*/ - -/* Dispatch module (passing incoming messages to extensions registered callbacks) - * is split between the library and the daemon. - * - * The library provides the support for associating dispatch callbacks with - * dictionary objects. - * - * The daemon is responsible for calling the callbacks for a message when appropriate. - * - * - * The dispatch module has two main roles: - * - help determine if a message can be handled locally (during the routing step) - * This decision involves only the application-id of the message. - * - pass the message to the callback(s) that will handle it (during the dispatch step) - * - * These are the possibilities for registering a so-called dispatch callback: - * - * -> For All messages. - * This callback is called for all messages that are handled locally. This should be used only - * for debug purpose. - * - * -> by AVP value (constants only). - * This callback will be called when a message is received and contains an AVP with a specified enumerated value. - * - * -> by AVP. - * This callback will be called when the received message contains a certain AVP. - * - * -> by command-code. - * This callback will be called when the message is a specific command (and 'R' flag). - * - * -> by application. - * This callback will be called when the message has a specific application-id. - * - * ( by vendor: would this be useful? it may be added later) - */ -enum disp_how { - DISP_HOW_ANY = 1, /* Any message. This should be only used for debug. */ - DISP_HOW_APPID, /* Any message with the specified application-id */ - DISP_HOW_CC, /* Messages of the specified command-code (request or answer). App id may be specified. */ - DISP_HOW_AVP, /* Messages containing a specific AVP. Command-code and App id may be specified. */ - DISP_HOW_AVP_ENUMVAL /* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */ -}; -/* - * Several criteria may be selected at the same time, for example command-code AND application id. - * - * If several callbacks are registered for the same object, their order is unspecified. - * The order in which the callbacks are called is: - * DISP_HOW_ANY - * DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP - * DISP_HOW_CC - * DISP_HOW_APPID - */ - -/* When a callback is registered, a "when" argument is passed in addition to the disp_how value, - * to specify which values the criteria must match. */ -struct disp_when { - struct dict_object * app_id; - struct dict_object * command; - struct dict_object * avp; - struct dict_object * value; -}; - -/* Here is the details on this "when" argument, depending on the disp_how value. - * - * DISP_HOW_ANY. - * In this case, "when" must be NULL. - * - * DISP_HOW_APPID. - * Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION. - * - * DISP_HOW_CC. - * The "command" field must be defined and point to a dictionary object of type DICT_COMMAND. - * The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id. - * The other fields are ignored. - * - * DISP_HOW_AVP. - * The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP. - * The "app_id" field may be set to restrict the messages matching to a specific app id. - * The "command" field may also be set to a valid DICT_COMMAND object. - * The content of the "value" field is ignored. - * - * DISP_HOW_AVP_ENUMVAL. - * All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set - * and points to a valid DICT_ENUMVAL object. - * - * Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored ) - * field: app_id command avp value - * APPID : M 0 0 0 - * CC : m M 0 0 - * AVP : m m M 0 - * ENUMVA: m m M M - */ - -/* The callbacks that are registered have the following prototype: - * - * int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, int * is_answer ); - * - * FUNCTION: dispatch_callback - * - * PARAMETERS: - * msg : the received message on function entry. may be updated to answer on return (see description) - * avp : for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP. - * session : if the message contains a Session-Id AVP, the corresponding session object. - * - * DESCRIPTION: - * Create a new AVP instance. - * - * RETURN VALUE: - * 0 : The AVP is created. - * EINVAL : A parameter is invalid. - * (other standard errors may be returned, too, with their standard meaning. Example: - * ENOMEM : Memory allocation for the new avp failed.) - */ - - -/*============================================================*/ /* MESSAGES */ /*============================================================*/ @@ -2195,6 +2077,201 @@ int fd_msg_update_length ( msg_or_avp * object ); +/*============================================================*/ +/* DISPATCH */ +/*============================================================*/ + +/* Dispatch module (passing incoming messages to extensions registered callbacks) + * is split between the library and the daemon. + * + * The library provides the support for associating dispatch callbacks with + * dictionary objects. + * + * The daemon is responsible for calling the callbacks for a message when appropriate. + * + * + * The dispatch module has two main roles: + * - help determine if a message can be handled locally (during the routing step) + * This decision involves only the application-id of the message. + * - pass the message to the callback(s) that will handle it (during the dispatch step) + * + * The first role is handled by the daemon. + * + * About the second, these are the possibilities for registering a dispatch callback: + * + * -> For All messages. + * This callback is called for all messages that are handled locally. This should be used only + * for debug purpose. + * + * -> by AVP value (constants only). + * This callback will be called when a message is received and contains an AVP with a specified enumerated value. + * + * -> by AVP. + * This callback will be called when the received message contains a certain AVP. + * + * -> by command-code. + * This callback will be called when the message is a specific command (and 'R' flag). + * + * -> by application. + * This callback will be called when the message has a specific application-id. + * + * ( by vendor: would this be useful? it may be added later) + */ +enum disp_how { + DISP_HOW_ANY = 1, /* Any message. This should be only used for debug. */ + DISP_HOW_APPID, /* Any message with the specified application-id */ + DISP_HOW_CC, /* Messages of the specified command-code (request or answer). App id may be specified. */ + DISP_HOW_AVP, /* Messages containing a specific AVP. Command-code and App id may be specified. */ + DISP_HOW_AVP_ENUMVAL /* Messages containing a specific AVP with a specific enumerated value. Command-code and App id may be specified. */ +}; +/* + * Several criteria may be selected at the same time, for example command-code AND application id. + * + * If several callbacks are registered for the same object, they are called in the order they were registered. + * The order in which the callbacks are called is: + * DISP_HOW_ANY + * DISP_HOW_AVP_ENUMVAL & DISP_HOW_AVP + * DISP_HOW_CC + * DISP_HOW_APPID + */ + +/* When a callback is registered, a "when" argument is passed in addition to the disp_how value, + * to specify which values the criteria must match. */ +struct disp_when { + struct dict_object * app; + struct dict_object * command; + struct dict_object * avp; + struct dict_object * value; +}; + +/* Note that all the dictionary objects should really belong to the same dictionary! + * + * Here is the details on this "when" argument, depending on the disp_how value. + * + * DISP_HOW_ANY. + * In this case, "when" must be NULL. + * + * DISP_HOW_APPID. + * Only the "app_id" field must be set, other fields are ignored. It points to a dictionary object of type DICT_APPLICATION. + * + * DISP_HOW_CC. + * The "command" field must be defined and point to a dictionary object of type DICT_COMMAND. + * The "app_id" may be also set. In the case it is set, it restricts the callback to be called only with this command-code and app id. + * The other fields are ignored. + * + * DISP_HOW_AVP. + * The "avp" field of the structure must be set and point to a dictionary object of type DICT_AVP. + * The "app_id" field may be set to restrict the messages matching to a specific app id. + * The "command" field may also be set to a valid DICT_COMMAND object. + * The content of the "value" field is ignored. + * + * DISP_HOW_AVP_ENUMVAL. + * All fields have the same constraints and meaning as in DISP_REG_AVP. In addition, the "value" field must be set + * and points to a valid DICT_ENUMVAL object. + * + * Here is a sumary of the fields: ( M : must be set; m : may be set; 0 : ignored ) + * field: app_id command avp value + * APPID : M 0 0 0 + * CC : m M 0 0 + * AVP : m m M 0 + * ENUMVA: m m M M + */ + +enum disp_action { + DISP_ACT_CONT, /* The next handler should be called, unless *msg == NULL. */ + DISP_ACT_SEND /* The updated message must be sent. No further callback is called. */ +}; +/* The callbacks that are registered have the following prototype: + * int dispatch_callback( struct msg ** msg, struct avp * avp, struct session * session, enum disp_action * action ); + * + * CALLBACK: dispatch_callback + * + * PARAMETERS: + * msg : the received message on function entry. may be updated to answer on return (see description) + * avp : for callbacks registered with DISP_HOW_AVP or DISP_HOW_AVP_ENUMVAL, direct link to the triggering AVP. + * session : if the message contains a Session-Id AVP, the corresponding session object, NULL otherwise. + * action : upon return, this tells the daemon what to do next. + * + * DESCRIPTION: + * Called when a received message matchs the condition for which the callback was registered. + * This callback may do any kind of processing on the message, including: + * - create an answer for a request. + * - proxy a request or message, add / remove the Proxy-Info AVP, then forward the message. + * - update a routing table or start a connection with a new peer, then forward the message. + * - ... + * + * When *action == DISP_ACT_SEND on callback return, the msg pointed by *msg is passed to the routing module for sending. + * When *action == DISP_ACT_CONT, the next registered callback is called. + * When the last callback gives also DISP_ACT_CONT action value, a default handler is called. It's behavior is as follow: + * - if the message is an answer, it is discarded. + * - if the message is a request, it is passed again to the routing stack, and marked as non-local handling. + * + * RETURN VALUE: + * 0 : The callback executed successfully and updated *action appropriately. + * !0 : standard errors. In case of error, the message is discarded. + */ + +/* This structure represents a handler for a registered callback, allowing its de-registration */ +struct disp_hdl; + +/* + * FUNCTION: fd_disp_register + * + * PARAMETERS: + * cb : The callback function to register (see dispatch_callback description above). + * how : How the callback must be registered. + * when : Values that must match, depending on the how argument. + * handle : On success, a handler to the registered callback is stored here if not NULL. + * This handler can be used to unregister the cb. + * + * DESCRIPTION: + * Register a new callback to handle messages delivered locally. + * + * RETURN VALUE: + * 0 : The callback is registered. + * EINVAL : A parameter is invalid. + * ENOMEM : Not enough memory to complete the operation + */ +int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, enum disp_action *), + enum disp_how how, struct disp_when * when, struct disp_hdl ** handle ); + +/* + * FUNCTION: fd_disp_unregister + * + * PARAMETERS: + * handle : Location of the handle of the callback that must be unregistered. + * + * DESCRIPTION: + * Removes a callback previously registered by fd_disp_register. + * + * RETURN VALUE: + * 0 : The callback is unregistered. + * EINVAL : A parameter is invalid. + */ +int fd_disp_unregister ( struct disp_hdl ** handle ); + +/* + * FUNCTION: fd_msg_dispatch + * + * PARAMETERS: + * msg : A msg object that have already been fd_msg_parse_dict. + * session : The session corresponding to this object, if any. + * action : Upon return, the action that must be taken on the message + * + * DESCRIPTION: + * Call all handlers registered for a given message. + * The session must have already been resolved on entry. + * The msg pointed may be updated during this process. + * Upon return, the action parameter points to what must be done next. + * + * RETURN VALUE: + * 0 : Success. + * EINVAL : A parameter is invalid. + * (other errors) + */ +int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action ); + + /*============================================================*/ /* MESSAGE QUEUES */ diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/CMakeLists.txt --- a/libfreeDiameter/CMakeLists.txt Thu Sep 03 16:03:25 2009 +0900 +++ b/libfreeDiameter/CMakeLists.txt Fri Sep 04 18:05:25 2009 +0900 @@ -6,10 +6,11 @@ # List of source files for the library SET(LFD_SRC libfD.h + dictionary.c + dispatch.c init.c + lists.c log.c - lists.c - dictionary.c messages.c mqueues.c sessions.c diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/dictionary.c --- a/libfreeDiameter/dictionary.c Thu Sep 03 16:03:25 2009 +0900 +++ b/libfreeDiameter/dictionary.c Fri Sep 04 18:05:25 2009 +0900 @@ -409,9 +409,11 @@ } /* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */ + CHECK_POSIX_DO( pthread_rwlock_wrlock(&fd_disp_lock), /* continue */ ); while (!FD_IS_LIST_EMPTY(&obj->disp_cbs)) { fd_list_unlink( obj->disp_cbs.next ); } + CHECK_POSIX_DO( pthread_rwlock_unlock(&fd_disp_lock), /* continue */ ); /* Last, destroy the object */ free(obj); diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/dispatch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libfreeDiameter/dispatch.c Fri Sep 04 18:05:25 2009 +0900 @@ -0,0 +1,209 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, 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 "libfD.h" + +/* The dispatch module in the library is quite simple: callbacks are saved in a global list + * in no particular order. In addition, they are also linked from the dictionary objects they + * refer to. */ + +/* Protection for the lists managed in this module. */ +pthread_rwlock_t fd_disp_lock = PTHREAD_RWLOCK_INITIALIZER; + +/* List of all registered handlers -- useful if we want to cleanup properly at some point... */ +static struct fd_list all_handlers; + +/* List of handlers registered for DISP_HOW_ANY. Other handlers are stored in the dictionary */ +static struct fd_list any_handlers; + +/* The structure to store a callback */ +struct disp_hdl { + int eyec; /* Eye catcher, DISP_EYEC */ + struct fd_list all; /* link in the all_handlers list */ + struct fd_list parent;/* link in dictionary cb_list or in any_handlers */ + enum disp_how how; /* Copy of registration parameter */ + struct disp_when when; /* Copy of registration parameter */ + int (*cb)( struct msg **, struct avp *, struct session *, enum disp_action *); /* The callback itself */ +}; + +#define DISP_EYEC 0xD15241C1 +#define VALIDATE_HDL( _hdl ) \ + ( ( ( _hdl ) != NULL ) && ( ((struct disp_hdl *)( _hdl ))->eyec == DISP_EYEC ) ) + +/**************************************************************************************/ +/* Initialize the module lists */ +void fd_disp_init(void) +{ + TRACE_ENTRY(); + fd_list_init(&all_handlers, NULL); + fd_list_init(&any_handlers, NULL); + /* if PTHREAD_RWLOCK_INITIALIZER is not supported on all platforms, we may initialize the lock here */ +} + +/* Call CBs from a given list (any_handlers if cb_list is NULL) -- must have locked fd_disp_lock before */ +int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, + struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu) +{ + struct fd_list * senti, *li; + TRACE_ENTRY("%p %p %p %p %p %p %p %p %p", cb_list, msg, avp, sess, action, obj_app, obj_cmd, obj_avp, obj_enu); + CHECK_PARAMS(msg && action); + + senti = cb_list; + if (!senti) + senti = &any_handlers; + + for (li = senti->next; li != senti; li = li->next) { + struct disp_hdl * hdl = (struct disp_hdl *)(li->o); + + TRACE_DEBUG(ANNOYING, "when: %p %p %p %p", hdl->when.app, hdl->when.command, hdl->when.avp, hdl->when.value); + + /* Check this handler matches this message / avp */ + if (hdl->when.app && (hdl->when.app != obj_app)) + continue; + if (hdl->when.command && (hdl->when.command != obj_cmd)) + continue; + if (hdl->when.avp && (hdl->when.avp != obj_avp)) + continue; + if (hdl->when.value && (hdl->when.value != obj_enu)) + continue; + + /* We have a match, the cb must be called. */ + CHECK_FCT( (*hdl->cb)(msg, avp, sess, action) ); + + if (*action != DISP_ACT_CONT) + break; + + if ( *msg == NULL ) + break; + } + + /* We're done on this list */ + return 0; +} + +/**************************************************************************************/ + +/* Create a new handler and link it */ +int fd_disp_register ( int (*cb)( struct msg **, struct avp *, struct session *, enum disp_action *), + enum disp_how how, struct disp_when * when, struct disp_hdl ** handle ) +{ + struct fd_list * cb_list = NULL; + struct disp_hdl * new; + struct dict_object * type_enum, * type_avp; + struct dictionary * dict = NULL; + + TRACE_ENTRY("%p %d %p %p", cb, how, when, handle); + CHECK_PARAMS( cb && ( (how == DISP_HOW_ANY) || when )); + + switch (how) { + case DISP_HOW_ANY: + cb_list = &any_handlers; + break; + + case DISP_HOW_APPID: + CHECK_FCT( fd_dict_disp_cb(DICT_APPLICATION, when->app, &cb_list) ); + break; + + case DISP_HOW_CC: + CHECK_FCT( fd_dict_disp_cb(DICT_COMMAND, when->command, &cb_list) ); + break; + + case DISP_HOW_AVP_ENUMVAL: + CHECK_FCT( fd_dict_disp_cb(DICT_ENUMVAL, when->value, &cb_list) ); /* cb_list is then overwriten */ + CHECK_FCT( fd_dict_getdict(when->value, &dict) ); + CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_ENUMVAL, when->value, &type_enum, EINVAL) ); + case DISP_HOW_AVP: + CHECK_FCT( fd_dict_disp_cb(DICT_AVP, when->avp, &cb_list) ); + if (dict) { + CHECK_FCT( fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, when->avp, &type_avp, EINVAL) ); + CHECK_PARAMS( type_enum == type_avp ); + } + break; + + default: + CHECK_PARAMS(how = 0); + } + /* We might further check optional fields, but we trust the caller ^^ */ + + /* Create the new handler */ + CHECK_MALLOC( new = malloc( sizeof(struct disp_hdl) ) ); + memset(new, 0, sizeof(struct disp_hdl)); + new->eyec = DISP_EYEC; + fd_list_init(&new->all, new); + fd_list_init(&new->parent, new); + new->how = how; + switch (how) { + case DISP_HOW_AVP_ENUMVAL: + new->when.value = when->value; + case DISP_HOW_AVP: + new->when.avp = when->avp; + case DISP_HOW_CC: + new->when.command = when->command; + case DISP_HOW_APPID: + new->when.app = when->app; + } + new->cb = cb; + + /* Now, link this new element in the appropriate lists */ + CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) ); + fd_list_insert_before(&all_handlers, &new->all); + fd_list_insert_before(cb_list, &new->parent); + CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) ); + + /* We're done */ + if (handle) + *handle = new; + + return 0; +} + +/* Delete a handler */ +int fd_disp_unregister ( struct disp_hdl ** handle ) +{ + struct disp_hdl * del; + TRACE_ENTRY("%p", handle); + CHECK_PARAMS( handle && VALIDATE_HDL(*handle) ); + del = *handle; + *handle = NULL; + + CHECK_POSIX( pthread_rwlock_wrlock(&fd_disp_lock) ); + fd_list_unlink(&del->all); + fd_list_unlink(&del->parent); + CHECK_POSIX( pthread_rwlock_unlock(&fd_disp_lock) ); + + free(del); + return 0; +} + diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/init.c --- a/libfreeDiameter/init.c Thu Sep 03 16:03:25 2009 +0900 +++ b/libfreeDiameter/init.c Fri Sep 04 18:05:25 2009 +0900 @@ -48,6 +48,7 @@ /* Initialize the modules that need it */ fd_msg_eteid_init(); + fd_disp_init(); CHECK_FCT( fd_sess_init() ); return 0; diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/libfD.h --- a/libfreeDiameter/libfD.h Thu Sep 03 16:03:25 2009 +0900 +++ b/libfreeDiameter/libfD.h Fri Sep 04 18:05:25 2009 +0900 @@ -44,11 +44,16 @@ /* Internal to the library */ extern const char * type_base_name[]; void fd_msg_eteid_init(void); +void fd_disp_init(void); int fd_sess_init(void); /* Iterator on the rules of a parent object */ int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) ); - +/* Dispatch / messages / dictionary API */ +int fd_dict_disp_cb(enum dict_object_type type, struct dict_object *obj, struct fd_list ** cb_list); +int fd_disp_call_cb_int( struct fd_list * cb_list, struct msg ** msg, struct avp *avp, struct session *sess, enum disp_action *action, + struct dict_object * obj_app, struct dict_object * obj_cmd, struct dict_object * obj_avp, struct dict_object * obj_enu); +extern pthread_rwlock_t fd_disp_lock; #endif /* _LIBFD_H */ diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/messages.c --- a/libfreeDiameter/messages.c Thu Sep 03 16:03:25 2009 +0900 +++ b/libfreeDiameter/messages.c Fri Sep 04 18:05:25 2009 +0900 @@ -2137,3 +2137,97 @@ } /***************************************************************************************************************/ +/* Macro to check if further callbacks must be called */ +#define TEST_ACTION_STOP() \ + if ((*msg == NULL) || (*action != DISP_ACT_CONT)) \ + goto no_error; + +/* Call all dispatch callbacks for a given message */ +int fd_msg_dispatch ( struct msg ** msg, struct session * session, enum disp_action *action) +{ + struct dictionary * dict; + struct dict_object * app; + struct dict_object * cmd; + struct avp * avp; + struct fd_list * cb_list; + int ret = 0; + + TRACE_ENTRY("%p %p %p", msg, session, action); + CHECK_PARAMS( msg && CHECK_MSG(*msg) && action); + + /* Take the dispatch lock */ + CHECK_FCT( pthread_rwlock_rdlock(&fd_disp_lock) ); + pthread_cleanup_push( fd_cleanup_rwlock, &fd_disp_lock ); + + /* First, call the DISP_HOW_ANY callbacks */ + CHECK_FCT_DO( ret = fd_disp_call_cb_int( NULL, msg, NULL, session, action, NULL, NULL, NULL, NULL ), goto error ); + + TEST_ACTION_STOP(); + + /* If we don't know the model at this point, we stop cause we cannot get the dictionary. It's invalid: an error should already have been trigged by ANY callbacks */ + CHECK_PARAMS_DO(cmd = (*msg)->msg_model, { ret = EINVAL; goto error; } ); + + /* Now resolve message application */ + CHECK_FCT_DO( ret = fd_dict_getdict( cmd, &dict ), goto error ); + CHECK_FCT_DO( ret = fd_dict_search( dict, DICT_APPLICATION, APPLICATION_BY_ID, &(*msg)->msg_public.msg_appl, &app, 0 ), goto error ); + + if (app == NULL) { + /* In that case, maybe we should answer a DIAMETER_APPLICATION_UNSUPPORTED error ? Do we do this here ? */ + TRACE_DEBUG(NONE, "Reply DIAMETER_APPLICATION_UNSUPPORTED if it's a request ?"); + } + + /* So start browsing the message */ + CHECK_FCT_DO( ret = fd_msg_browse( *msg, MSG_BRW_FIRST_CHILD, &avp, NULL ), goto error ); + while (avp != NULL) { + /* Sanity */ + ASSERT( avp->avp_public.avp_value ); + + /* For unknown AVP, we don't have a callback registered, so just skip */ + if (avp->avp_model) { + struct dict_object * type, * enumval; + + /* Get the list of callback for this AVP */ + CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_AVP, avp->avp_model, &cb_list), goto error ); + + /* Check if the AVP has a constant value */ + CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_TYPE, TYPE_OF_AVP, avp->avp_model, &type, 0), goto error ); + if (type) { + struct dict_enumval_request req; + memset(&req, 0, sizeof(struct dict_enumval_request)); + req.type_obj = type; + memcpy( &req.search.enum_value, avp->avp_public.avp_value, sizeof(union avp_value) ); + CHECK_FCT_DO( ret = fd_dict_search(dict, DICT_ENUMVAL, ENUMVAL_BY_STRUCT, &req, &enumval, 0), goto error ); + } else { + /* No enumerated value in this case */ + enumval = NULL; + } + + /* Call the callbacks */ + CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, avp, session, action, app, cmd, avp->avp_model, enumval ), goto error ); + TEST_ACTION_STOP(); + } + /* Go to next AVP */ + CHECK_FCT_DO( ret = fd_msg_browse( avp, MSG_BRW_WALK, &avp, NULL ), goto error ); + } + + /* Now call command and application callbacks */ + CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_COMMAND, cmd, &cb_list), goto error ); + CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto error ); + TEST_ACTION_STOP(); + + if (app) { + CHECK_FCT_DO( ret = fd_dict_disp_cb(DICT_APPLICATION, app, &cb_list), goto error ); + CHECK_FCT_DO( ret = fd_disp_call_cb_int( cb_list, msg, NULL, session, action, app, cmd, NULL, NULL ), goto error ); + TEST_ACTION_STOP(); + } + + pthread_cleanup_pop(0); + +no_error: + CHECK_POSIX(pthread_rwlock_unlock(&fd_disp_lock) ); + return 0; + +error: + CHECK_POSIX_DO(pthread_rwlock_unlock(&fd_disp_lock), /* ignore */ ); + return ret; +} diff -r b0d377c79d80 -r e5af94b04946 libfreeDiameter/sessions.c --- a/libfreeDiameter/sessions.c Thu Sep 03 16:03:25 2009 +0900 +++ b/libfreeDiameter/sessions.c Fri Sep 04 18:05:25 2009 +0900 @@ -104,7 +104,7 @@ /* Sessions hash table, to allow fast sid to session retrieval */ static struct { struct fd_list sentinel; /* sentinel element for this sublist */ - pthread_mutex_t lock; /* the mutex for this sublist */ + pthread_mutex_t lock; /* the mutex for this sublist -- we might probably change it to rwlock for a little optimization */ } sess_hash [ 1 << SESS_HASH_SIZE ] ; #define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1)) #define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel)) @@ -112,7 +112,7 @@ /* The following are used to generate sid values that are eternaly unique */ static uint32_t sid_h; /* initialized to the current time in fd_sess_init */ -static uint32_t sid_l; /* incremented each time a session id is created -- could use atomic operation probably */ +static uint32_t sid_l; /* incremented each time a session id is created */ static pthread_mutex_t sid_lock = PTHREAD_MUTEX_INITIALIZER; /* Expiring sessions management */ @@ -129,7 +129,7 @@ /********************************************************************************************************/ -/* Initialize a session object. It is not linked now. sid must be already alloc'ed. */ +/* Initialize a session object. It is not linked now. sid must be already malloc'ed. */ static struct session * new_session(char * sid, size_t sidlen) { struct session * sess; @@ -156,9 +156,6 @@ return sess; } - - - /* The expiry thread */ static void * exp_fct(void * arg) {