# HG changeset patch # User Sebastien Decugis # Date 1298280678 -32400 # Node ID 6a7323cd78b3c2ae9b9b2b4021a91234831445aa # Parent 01f796160f7fd6656834b352cd2f366859d8c162 New app_redirect.fdx code (UNTESTED) diff -r 01f796160f7f -r 6a7323cd78b3 contrib/debian/changelog --- a/contrib/debian/changelog Mon Feb 21 18:30:45 2011 +0900 +++ b/contrib/debian/changelog Mon Feb 21 18:31:18 2011 +0900 @@ -20,8 +20,10 @@ * Improved peer state machine algorithm to counter SCTP multistream race condition. * New extension rt_redirect.fdx that handles the Diameter Redirect errors. + * New extension app_redirect that allows sending Redirect indications to + other peers. See doc/app_redirect.conf.sample for more information. - -- Sebastien Decugis Fri, 18 Feb 2011 14:47:24 +0900 + -- Sebastien Decugis Fri, 18 Feb 2011 15:25:19 +0900 freediameter (1.0.4) UNRELEASED; urgency=low diff -r 01f796160f7f -r 6a7323cd78b3 doc/app_redirect.conf.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/app_redirect.conf.sample Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,97 @@ +# This file contains the configuration for the app_redirect extension of freeDiameter. +# +# This extension provides configurable Redirect messages. + +# Lines starting with a # are comments and ignored. +# Spaces and newlines are not meaningful, except inside quoted areas. + +######################################################################################### +# See Diameter RFC for a detailed explanation on Redirects semantics # +######################################################################################### + +## default_redirect_cache_time +# Specify the default value for Redirect-Max-Cache-Time. +# This value can be overwriten for each rule as specified bellow. +# If this value is not specified, the default is: +#default_redirect_cache_time = 86400; ## => 1 day + +# The remaining of this file contains a list of RULE elements. +# Each RULE consists in three parts: +# - a CRITERIA that specifies which messages the RULE applies to. +# - a REDIRECT_TYPE that specifies what type of redirect is to be sent, and its duration. +# - a TARGET_HOSTS list that specifies the host(s) to send the message to. +# +# The rules are matched in the order they appear in this file. Once a rule has matched, the +# remaining rules are not processed. +# +# The basic format of a rule is: +# REDIRECT_TYPE : CRITERIA to TARGET_HOSTS ; + +# These are a few examples. The definition of each term follows. +# +# 1) REALM_AND_APPLICATION : app=3 "Destination-Realm"="myrealm.net" to "aaas://acct1.myrealm.net" "aaas://acct2.myrealm.net"; +# will ask all peers sending a Base Accounting message for realm "myrealm.net" to send +# this message directly to either 'acct1.myrealm.net' or 'acct2.myrealm.net'. +# +# 2) ALL_SESSION 3600 : "Origin-AAA-Protocol"=1 "Destination-Realm"="myrealm.net" to "aaas://raddiam.myrealm.net"; +# Will ask any peer sending messages translated from RADIUS and targeted to this realm +# to address all the messages from the same session to 'raddiam.myrealm.net'. The +# redirect entry should be stored for 1 hour. +# +# 3) ALL_HOST : from.realm=[".*\.(fr|de|es)"] to "aaas://relay-EU.myrealm.net"; +# ALL_HOST : from.realm=[".*\.(cn|jp|vn)"] to "aaas://relay-ASIA.myrealm.net"; +# Redirect messages to different relays depending on where they come from. +# +# 4) ALL_HOST : to "aaas://newserv.myrealm.net"; +# This server was relocated, tell all peers to go directly to the new one. +# This rule should appear last because it matches all messages, so further rules will never be used. + + +# +# REDIRECT_TYPE +# + +# The redirect_type is one of the following (see Redirect-Host-Usage AVP definition in RFC for semantics): +# DONT_CACHE +# ALL_SESSION +# ALL_REALM +# REALM_AND_APPLICATION +# ALL_APPLICATION +# ALL_HOST +# ALL_USER + +# In addition, an integer can follow. If specified, it overwrites the default_redirect_cache_time +# value for this rule. The value is always specified in seconds. + +# +# CRITERIA +# + +# Each RULE can contain 0 or more criteria. +# If no criteria is specified, all messages are assumed to match (wildcard). +# If more than one criteria is specified, an "AND" relationship is assumed. +# If you need to specify "OR", just create separate rules. +# +# In the following definitions, "STR/REG" stands for: +# - a quoted string "some.peer" that will match exactly this string (case-insensitive), or +# - a bracket-quoted string ["some regex"] that will be interpreted as a POSIX extended regular expression (case-sensitive), and attempt to match the string. +# +# A criteria is one of the following: +# from.id="STR/REG" -> matches messages received from peer with this Diameter Identity. +# from.realm="STR/REG" -> matches messages received from peer with this Realm. +# app=U32_VALUE -> matches messages with this Diameter Application-Id value in its header. +# "AVP-name"=U32_VALUE -> matches messages that contain an avp "AVP-name" (replace with the realm name) with this value. +# "AVP-name"="STR/REG" -> matches messages that contain an avp "AVP-name" (replace with the realm name) with this . + +# +# TARGET_HOSTS +# + +# This is a simple list of DiameterURI that must be sent back. +# See the RFC for valid format of Diameter URI: +# "aaa://" FQDN [ port ] [ transport ] [ protocol ] +# "aaas://" FQDN [ port ] [ transport ] [ protocol ] + +###################################################################################### + + diff -r 01f796160f7f -r 6a7323cd78b3 extensions/CMakeLists.txt --- a/extensions/CMakeLists.txt Mon Feb 21 18:30:45 2011 +0900 +++ b/extensions/CMakeLists.txt Mon Feb 21 18:31:18 2011 +0900 @@ -57,6 +57,8 @@ FD_EXTENSION_SUBDIR(app_radgw "RADIUS/Diameter gateway translation - RADIUS client to Diameter server" OFF) FD_EXTENSION_SUBDIR(app_sip "Diameter SIP Authentication and Authorization server (RFC 4740)" OFF) +FD_EXTENSION_SUBDIR(app_redirect "Diameter Redirect server: send configurable Redirect indications to other peers" OFF) + #### # Routing extensions diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/CMakeLists.txt Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,42 @@ +# The rt_default extension +PROJECT("Configurable Redirects server extension" C) + +# Check if REG_STARTEND is provided on the host +SET(CHECK_REG_STARTEND_SOURCE_CODE " + #include + #include + int main() { + return regexec(NULL, NULL, 0, NULL, REG_STARTEND); + } + ") +CHECK_C_SOURCE_COMPILES("${CHECK_REG_STARTEND_SOURCE_CODE}" HAVE_REG_STARTEND) +# Generate the host.h file +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/ard-host.h.in ${CMAKE_CURRENT_BINARY_DIR}/ard-host.h) + +# Parser files +BISON_FILE(ard_conf.y) +FLEX_FILE(ard_conf.l) +SET_SOURCE_FILES_PROPERTIES(lex.ard_conf.c ard_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}") + +# List of source files +SET( APP_REDIR_SRC + app_redir.c + app_redir.h + lex.ard_conf.c + ard_conf.tab.c + ard_conf.tab.h + ard_rules.c +) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +# Compile these files as a freeDiameter extension +FD_ADD_EXTENSION(app_redirect ${APP_REDIR_SRC}) + + +#### +## INSTALL section ## + +INSTALL(TARGETS app_redirect + LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX} + COMPONENT freeDiameter-app_redirect) diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/app_redir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/app_redir.c Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,100 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* + * Configurable routing of messages for freeDiameter. + */ + +#include "app_redir.h" + +static struct fd_rt_fwd_hdl * ard_fwd_cb = NULL; + +/* entry point */ +static int ard_entry(char * conffile) +{ + TRACE_ENTRY("%p", conffile); + + /* Parse the configuration file */ + CHECK_FCT( ard_conf_handle(conffile) ); + + /* Resolve the dictionary objects we use */ + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Redirect-Host", &avp_Redirect_Host, ENOENT )); + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Redirect-Host-Usage", &avp_Redirect_Host_Usage, ENOENT )); + CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Redirect-Max-Cache-Time", &avp_Redirect_Max_Cache_Time, ENOENT )); + + /* Register the proxy callback */ + CHECK_FCT( fd_rt_fwd_register(ard_rule_apply, NULL, RT_FWD_REQ, &ard_fwd_cb) ); + + /* We're done */ + return 0; +} + +/* Unload */ +void fd_ext_fini(void) +{ + TRACE_ENTRY(); + + /* Unregister the proxy callback */ + if (ard_fwd_cb) { + CHECK_FCT_DO( fd_rt_fwd_unregister(ard_fwd_cb, NULL), ); + } + + /* Destroy all rules */ + while (!FD_IS_LIST_EMPTY(&ard_conf->rules)) { + struct ard_rule * r = ard_conf->rules.next->o; + fd_list_unlink(&r->chain); + while (!FD_IS_LIST_EMPTY(&r->criteria)) { + struct ard_criteria * c = r->criteria.next->o; + fd_list_unlink(&c->chain); + if (c->is_regex) { + regfree(&c->preg); + } + free(c->s); + free(c); + } + while (!FD_IS_LIST_EMPTY(&r->targets)) { + struct ard_target * t = r->targets.next->o; + fd_list_unlink(&t->chain); + free(t->s); + free(t); + } + free(r); + } + + /* Done */ + return ; +} + +EXTENSION_ENTRY("app_redirect", ard_entry); diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/app_redir.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/app_redir.h Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,121 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* Header file for the app_redirect extension. + * + * See the app_redirect.conf.sample file for the format of the configuration file. + */ + +/* FreeDiameter's common include file */ +#include + +/* Host configuration for this specific extension */ +#include +#include + +/* Extension's configuration */ +struct ard_config { + uint32_t default_rct; /* redirect-cache-time to use unless overwritten by a rule */ + struct fd_list rules; /* the list of rules in the order they appear in the conf file. */ +}; +extern struct ard_config * ard_conf; /* initialized in ard_conf.y */ + +/* The types of redirects (from Redirect-Host-Usage AVP value) */ +enum redir_h_u { + DONT_CACHE = 0, + ALL_SESSION, + ALL_REALM, + REALM_AND_APPLICATION, + ALL_APPLICATION, + ALL_HOST, + ALL_USER +}; +#define H_U_MAX ALL_USER + +/* A rule */ +struct ard_rule { + struct fd_list chain; /* link in configuration */ + + enum redir_h_u type; /* What kind of rule is this? */ + uint32_t rct; /* overwrite default_rct is not 0 */ + + struct fd_list criteria; /* list of criteria to match. The rule is applied if all criteria match */ + + struct fd_list targets; /* list of Redirect-Host values to send. */ +}; + +/* What kind of criteria exist */ +enum rule_criteria { /* note: the order of the values reflects the complexity of matching -- it should be kept this way */ + APP_ID, + FROM_ID, + FROM_REALM, + AVP_INT, + AVP_STR +}; + +/* A criteria in the list */ +struct ard_criteria { + struct fd_list chain; /* link in ard_rule->criteria */ + enum rule_criteria type; /* What is this rule */ + + /* the data that must be matched -- everything is not used by all criteria types */ + char * s; + size_t sl; + int is_regex; + regex_t preg; + uint32_t i; + struct dict_avp_data avp_info; +}; + +/* A target entry in the ard_rule->targets list */ +struct ard_target { + struct fd_list chain; + os0_t s; /* must be freed afterwards */ + size_t l; +}; + +/* The AVPs we use */ +extern struct dict_object * avp_Redirect_Host; +extern struct dict_object * avp_Redirect_Host_Usage; +extern struct dict_object * avp_Redirect_Max_Cache_Time; + +/* Parse the configuration file */ +int ard_conf_handle(char * conffile); + +/* Dump a rule (debug) */ +void ard_rule_dump(struct ard_rule * r); + +/* Check if a rule applies, and if found, create the reply */ +int ard_rule_apply(void * cbdata, struct msg ** msg); diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/ard-host.h.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/ard-host.h.in Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,42 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* Configuration from compile-time */ +#ifndef RTD_IS_CONFIG +#define RTD_IS_CONFIG + +#cmakedefine HAVE_REG_STARTEND + +#endif /* RTD_IS_CONFIG */ diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/ard_conf.l --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/ard_conf.l Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,140 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* Tokenizer + * + */ + +%{ +#include "app_redir.h" +/* Include yacc tokens definitions */ +#include "ard_conf.tab.h" + +/* Update the column information */ +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ +} + +/* Avoid warning with newer flex */ +#define YY_NO_INPUT + +%} + +qstring \"[^\"\n]*\" + + +%option bison-bridge bison-locations +%option noyywrap +%option nounput + +%% + + /* Update the line count */ +\n { + yylloc->first_line++; + yylloc->last_line++; + yylloc->last_column=0; + } + + /* Eat all spaces but not new lines */ +([[:space:]]{-}[\n])+ ; + /* Eat all comments */ +#.*$ ; + + /* Recognize any integer */ +[[:digit:]]+ { + /* Convert this to an u32 value */ + int ret=0; + ret = sscanf(yytext, "%u", &yylval->u32); + if (ret != 1) { + /* No matching: an error occurred */ + fd_log_debug("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno)); + return TOK_LEX_ERROR; /* trig an error in yacc parser */ + /* Maybe we could REJECT instead of failing here? */ + } + return TOK_U32VAL; + } + + /* Recognize bracketed quoted strings */ +[[]{qstring}[]] { + /* Match a quoted string containing a regex */ + CHECK_MALLOC_DO( yylval->tstring.str = strdup(yytext+2), + { + TRACE_DEBUG(INFO, "Unable to copy the string '%s': %s\n", yytext, strerror(errno)); + return TOK_LEX_ERROR; /* trig an error in yacc parser */ + } ); + yylval->tstring.str[strlen(yytext) - 4] = '\0'; + yylval->tstring.regex = 1; + return TOK_TSTRING; + } + + /* Recognize quoted strings (since it comes after the previous rule, the string should not be quoted) */ +{qstring} { + /* Match a quoted string. */ + CHECK_MALLOC_DO( yylval->tstring.str = strdup(yytext+1), + { + TRACE_DEBUG(INFO, "Unable to copy the string '%s': %s\n", yytext, strerror(errno)); + return TOK_LEX_ERROR; /* trig an error in yacc parser */ + } ); + yylval->tstring.str[strlen(yytext) - 2] = '\0'; + yylval->tstring.regex = 0; + return TOK_TSTRING; + } + + /* The key words */ +(?i:"default_redirect_cache_time") { return TOK_DEFAULT_RCT; } +(?i:"to") { return TOK_TO; } +(?i:"DONT_CACHE") { return TOK_DONT_CACHE; } +(?i:"ALL_SESSION") { return TOK_ALL_SESSION; } +(?i:"ALL_REALM") { return TOK_ALL_REALM; } +(?i:"REALM_AND_APPLICATION") { return TOK_REALM_AND_APPLICATION; } +(?i:"ALL_APPLICATION") { return TOK_ALL_APPLICATION; } +(?i:"ALL_HOST") { return TOK_ALL_HOST; } +(?i:"ALL_USER") { return TOK_ALL_USER; } +(?i:"from.id") { return TOK_FROM_ID; } +(?i:"from.realm") { return TOK_FROM_REALM; } +(?i:"app") { return TOK_APP; } + + /* Valid single characters for yyparse */ +[:=;] { return yytext[0]; } + + /* Unrecognized sequence, if it did not match any previous pattern */ +[^[:space:]\":=;\n]+ { + fd_log_debug("Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext); + return TOK_LEX_ERROR; + } + +%% diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/ard_conf.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/ard_conf.y Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,451 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* Yacc extension's configuration parser. + * See doc/app_redirect.conf.sample for configuration file format + */ + +/* For development only : */ +%debug +%error-verbose + +/* The parser receives the configuration file filename as parameter */ +%parse-param {char * conffile} + +/* Keep track of location */ +%locations +%pure-parser + +%{ +#include "app_redir.h" +#include "ard_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */ + +/* Forward declaration */ +int yyparse(char * conffile); + +static int rules_added = 0; + +/* We initialize statically the config */ +static struct ard_config local_conf = { .default_rct = 86400, .rules = FD_LIST_INITIALIZER(local_conf.rules) }; +struct ard_config * ard_conf = &local_conf; + +/* We use these lists in the rules parsing */ +static struct fd_list temp_list_criteria = FD_LIST_INITIALIZER(temp_list_criteria); +static struct fd_list temp_list_target = FD_LIST_INITIALIZER(temp_list_target); + +/* Local variable */ +static struct ard_criteria * c; + +/* Dump the configuration */ +static void ard_conf_dump() +{ + struct fd_list * li; + if (!TRACE_BOOL(FULL)) + return; + + fd_log_debug("app_redirect: configuration dump:\n"); + fd_log_debug(" default_redirect_cache_time : %u sec\n", ard_conf->default_rct); + for (li = ard_conf->rules.next; li != &ard_conf->rules; li = li->next) { + ard_rule_dump(li->o); + } + fd_log_debug("app_redirect: end of configuration dump\n"); +} + +/* Parse the configuration file */ +int ard_conf_handle(char * conffile) +{ + extern FILE * ard_confin; + int ret; + + TRACE_ENTRY("%p", conffile); + + TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); + + ard_confin = fopen(conffile, "r"); + if (ard_confin == NULL) { + ret = errno; + TRACE_DEBUG(INFO, "Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret)); + return ret; + } + + ret = yyparse(conffile); + + fclose(ard_confin); + + if (ret != 0) { + TRACE_DEBUG (INFO, "Unable to parse the configuration file."); + return EINVAL; + } else { + TRACE_DEBUG(FULL, "Added %d Redirect RULES successfully.", rules_added); + ard_conf_dump(); + } + + return 0; +} + +/* The Lex parser prototype */ +int ard_conflex(YYSTYPE *lvalp, YYLTYPE *llocp); + +/* Function to report the errors */ +void yyerror (YYLTYPE *ploc, char * conffile, char const *s) +{ + TRACE_DEBUG(INFO, "Error in configuration parsing"); + + if (ploc->first_line != ploc->last_line) { + TRACE_DEBUG (INFO, "%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); + } else if (ploc->first_column != ploc->last_column) { + TRACE_DEBUG (INFO, "%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + } else { + TRACE_DEBUG (INFO, "%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s); + } +} + +/* Compile a regular expression pattern */ +static int compile_regex( regex_t * preg, char * str ) +{ + int err; + + /* Compile the regular expression */ + err = regcomp(preg, str, REG_EXTENDED | REG_NOSUB); + if (err != 0) { + char * buf; + size_t bl; + + /* Error while compiling the regex */ + TRACE_DEBUG(INFO, "Error while compiling the regular expression '%s':", str); + + /* Get the error message size */ + bl = regerror(err, preg, NULL, 0); + + /* Alloc the buffer for error message */ + CHECK_MALLOC( buf = malloc(bl) ); + + /* Get the error message content */ + regerror(err, preg, buf, bl); + TRACE_DEBUG(INFO, "\t%s", buf); + + /* Free the buffer, return the error */ + free(buf); + return EINVAL; + } + + return 0; +} + + +%} + +/* Values returned by lex for token */ +%union { + /* returned by lex */ + uint32_t u32; /* Store integer values */ + struct { + char * str; + int regex; /* true or false */ + } tstring; /* typed string */ +} + +/* In case of error in the lexical analysis */ +%token TOK_LEX_ERROR + +/* A string (malloc'd in lex parser; it must be freed after use):*/ +%token TOK_TSTRING + +/* An integer value */ +%token TOK_U32VAL + +%type rule_type +%type rule_duration + +/* Tokens */ +%token TOK_DEFAULT_RCT + +%token TOK_TO + +%token TOK_DONT_CACHE +%token TOK_ALL_SESSION +%token TOK_ALL_REALM +%token TOK_REALM_AND_APPLICATION +%token TOK_ALL_APPLICATION +%token TOK_ALL_HOST +%token TOK_ALL_USER + +%token TOK_FROM_ID +%token TOK_FROM_REALM + +%token TOK_APP + + + + + +/* -------------------------------------- */ +%% + + /* The grammar definition */ +conffile: /* empty grammar is OK */ + | conffile def_rct + | conffile rule + ; + + /* Overwrite default cache time value */ +def_rct: TOK_DEFAULT_RCT '=' TOK_U32VAL ';' + { + ard_conf->default_rct = $3; + } + ; + + /* a RULE entry */ +rule: rule_type rule_duration ':' criteria_list TOK_TO target_list ';' + { + struct ard_rule * r; + /* Create the new rule with data in file */ + CHECK_MALLOC_DO( r = malloc(sizeof(struct ard_rule)), + { + yyerror (&yylloc, conffile, "Error while allocating new memory..."); + YYERROR; + } ); + memset(r, 0, sizeof(struct ard_rule)); + fd_list_init(&r->chain, r); + r->type = $1; + r->rct = $2; + fd_list_init(&r->criteria, NULL); + fd_list_move_end(&r->criteria, &temp_list_criteria); + fd_list_init(&r->targets, NULL); + fd_list_move_end(&r->targets, &temp_list_target); + + /* Add the new rule in config */ + fd_list_insert_before(&ard_conf->rules, &r->chain); + rules_added++; + } + ; + +rule_type: TOK_DONT_CACHE + { + $$ = DONT_CACHE; + } + | TOK_ALL_SESSION + { + $$ = ALL_SESSION; + } + | TOK_ALL_REALM + { + $$ = ALL_REALM; + } + | TOK_REALM_AND_APPLICATION + { + $$ = REALM_AND_APPLICATION; + } + | TOK_ALL_APPLICATION + { + $$ = ALL_APPLICATION; + } + | TOK_ALL_HOST + { + $$ = ALL_HOST; + } + | TOK_ALL_USER + { + $$ = ALL_USER; + } + ; + +rule_duration: /* empty */ + { + $$ = 0; + } + | + TOK_U32VAL + { + $$ = $1; + } + ; + +criteria_list: /* empty is OK */ + | criteria_list criteria_item; + ; + +criteria_item: { + /* Create the new criteria */ + CHECK_MALLOC_DO( c = malloc(sizeof(struct ard_criteria)), + { + yyerror (&yylloc, conffile, "Error while allocating new memory..."); + YYERROR; + } ); + memset(c, 0, sizeof(struct ard_criteria)); + fd_list_init(&c->chain, c); + } + criteria_item_inside + { + struct fd_list * li; + /* If there is a string, save its length */ + if (c->s) + c->sl = strlen(c->s); + /* If the criteria contains a regex, parse it */ + if (c->is_regex) { + CHECK_FCT_DO( compile_regex( &c->preg, c->s ), + { + yyerror (&yylloc, conffile, "Error parsing a regular expression..."); + YYERROR; + } ); + } + + /* Now link this new criteria in the list. Order by criteria type to accelerate the search */ + for (li = temp_list_criteria.next; li != &temp_list_criteria; li = li->next) { + struct ard_criteria * nc = li->o; + if (nc->type >= c->type) + break; + } + fd_list_insert_before(li, &c->chain); + } + ; + +criteria_item_inside: TOK_FROM_ID '=' TOK_TSTRING + { + c->type = FROM_ID; + c->s = $3.str; + c->is_regex = $3.regex; + } + | + TOK_FROM_REALM '=' TOK_TSTRING + { + c->type = FROM_REALM; + c->s = $3.str; + c->is_regex = $3.regex; + } + | + TOK_APP '=' TOK_U32VAL + { + c->type = APP_ID; + c->i = $3; + } + | + TOK_TSTRING '=' TOK_U32VAL + { + struct dict_object * avp = NULL; + if ($1.regex) { + yyerror(&yylloc, conffile, "Error: the AVP name cannot be specified as regular expression"); + YYERROR; + } + CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, $1.str, &avp, ENOENT ), + { + TRACE_DEBUG(INFO, "Error while searching for AVP '%s'. Did you load the relevant dictionary extensions?", $1.str); + yyerror(&yylloc, conffile, "Unable to resolve specified AVP name."); + YYERROR; + } ); + CHECK_FCT_DO( fd_dict_getval(avp, &c->avp_info), + { + TRACE_DEBUG(INFO, "Error while retrieving the description for AVP '%s'", $1.str); + yyerror(&yylloc, conffile, "Unable to retrieve specified AVP's data."); + YYERROR; + } ); + if (c->avp_info.avp_basetype != AVP_TYPE_UNSIGNED32) { + TRACE_DEBUG(INFO, "The AVP '%s' is not of type UNSIGNED32, matching is not supported (yet)", $1.str); + yyerror(&yylloc, conffile, "Invalid AVP for this operation."); + YYERROR; + } + + c->type = AVP_INT; + c->i = $3; + } + | + TOK_TSTRING '=' TOK_TSTRING + { + struct dict_object * avp = NULL; + if ($1.regex) { + yyerror(&yylloc, conffile, "Error: the AVP name cannot be specified as regular expression"); + YYERROR; + } + CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, $1.str, &avp, ENOENT ), + { + TRACE_DEBUG(INFO, "Error while searching for AVP '%s'. Did you load the relevant dictionary extensions?", $1.str); + yyerror(&yylloc, conffile, "Unable to resolve specified AVP name."); + YYERROR; + } ); + CHECK_FCT_DO( fd_dict_getval(avp, &c->avp_info), + { + TRACE_DEBUG(INFO, "Error while retrieving the description for AVP '%s'", $1.str); + yyerror(&yylloc, conffile, "Unable to retrieve specified AVP's data."); + YYERROR; + } ); + if (c->avp_info.avp_basetype != AVP_TYPE_OCTETSTRING) { + TRACE_DEBUG(INFO, "The AVP '%s' is not of type OCTETSTRING, matching is not supported (yet)", $1.str); + yyerror(&yylloc, conffile, "Invalid AVP for this operation."); + YYERROR; + } + + c->type = AVP_STR; + c->s = $3.str; + c->is_regex = $3.regex; + } + ; + +target_list: /* This list cannot be empty */ + target_item + | target_list target_item + ; + +target_item: TOK_TSTRING + { + struct ard_target * t; + + if ($1.regex) { + yyerror(&yylloc, conffile, "Regular expressions are not allowed in Redirect-Host specification."); + YYERROR; + } + + /* Check if the format is valid */ + CHECK_FCT_DO( fd_os_parse_DiameterURI((uint8_t *)$1.str, strlen($1.str), NULL, NULL, NULL, NULL, NULL, NULL), + { + TRACE_DEBUG(INFO, "Error while parsing DiameterURI '%s'", $1.str); + yyerror(&yylloc, conffile, "Specified DiameterURI is invalid."); + YYERROR; + } ); + + /* Ok. we create the new target */ + CHECK_MALLOC_DO( t = malloc(sizeof(struct ard_target)), + { + yyerror (&yylloc, conffile, "Error while allocating new memory..."); + YYERROR; + } ); + memset(t, 0, sizeof(struct ard_target)); + fd_list_init(&t->chain, t); + + t->s = (os0_t) $1.str; + t->l = strlen($1.str); + + fd_list_insert_before(&temp_list_target, &t->chain); + } + ; diff -r 01f796160f7f -r 6a7323cd78b3 extensions/app_redirect/ard_rules.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_redirect/ard_rules.c Mon Feb 21 18:31:18 2011 +0900 @@ -0,0 +1,300 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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 "app_redir.h" + +static const char * redir_type_str[] = { + "DONT_CACHE", + "ALL_SESSION", + "ALL_REALM", + "REALM_AND_APPLICATION", + "ALL_APPLICATION", + "ALL_HOST", + "ALL_USER" +}; + +struct dict_object * avp_Redirect_Host = NULL; +struct dict_object * avp_Redirect_Host_Usage = NULL; +struct dict_object * avp_Redirect_Max_Cache_Time = NULL; + + +void ard_rule_dump(struct ard_rule * r) +{ + struct fd_list * li; + fd_log_debug(" rule @%p: %s, %us\n", r, redir_type_str[r->type], r->rct); + for (li = r->criteria.next; li != &r->criteria; li = li->next) { + struct ard_criteria * c = li->o; + fd_log_debug(" Criteria: "); + switch (c->type) { + case FROM_ID: + fd_log_debug("received from peer %s'%s'", c->is_regex?"REGEX":"", c->s); + break; + case FROM_REALM: + fd_log_debug("received from realm %s'%s'", c->is_regex?"REGEX":"", c->s); + break; + case APP_ID: + fd_log_debug("application id is %u", c->i); + break; + case AVP_INT: + fd_log_debug("contains '%s' AVP with value '%d'", c->avp_info.avp_name, c->i); + break; + case AVP_STR: + fd_log_debug("contains '%s' AVP with value %s'%s'", c->avp_info.avp_name, c->is_regex?"REGEX":"", c->s); + break; + + default: + fd_log_debug("invalid (%d)!", c->type); + } + fd_log_debug("\n"); + } + for (li = r->targets.next; li != &r->targets; li = li->next) { + struct ard_target * t = li->o; + fd_log_debug(" Redirect to: '%s'\n", t->s); + } +} + +/* Tells if the string in s (is0term or not) matches the string in the criteria (regex or not) */ +static int str_match(struct ard_criteria * c, uint8_t *s, size_t l, int is0term, int * match) +{ + TRACE_ENTRY("%p %p %zd %d %p", c, s, l, is0term, match); + + *match = 0; + + if (c->is_regex == 0) { + if ( ! fd_os_almostcasecmp(c->s, c->sl, s, l) ) + *match = 1; + } else { + int err; +#ifdef HAVE_REG_STARTEND + regmatch_t pmatch[1]; + memset(pmatch, 0, sizeof(pmatch)); + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = l; + err = regexec(&c->preg, s, 0, pmatch, REG_STARTEND); +#else /* HAVE_REG_STARTEND */ + if (!is0term) { + /* We have to create a copy of the string in this case */ + char *mystrcpy; + CHECK_MALLOC( mystrcpy = os0dup(s, l) ); + err = regexec(&c->preg, mystrcpy, 0, NULL, 0); + free(mystrcpy); + } else { + err = regexec(&c->preg, s, 0, NULL, 0); + } +#endif /* HAVE_REG_STARTEND */ + + /* Now check the result */ + if (err == 0) { + /* We have a match */ + *match = 1; + } else if (err != REG_NOMATCH) { + /* An error occurred */ + char * buf; + size_t bl; + + /* Error while compiling the regex */ + TRACE_DEBUG(INFO, "Error while executing the regular expression '%s':", c->s); + + /* Get the error message size */ + bl = regerror(err, &c->preg, NULL, 0); + + /* Alloc the buffer for error message */ + CHECK_MALLOC( buf = malloc(bl) ); + + /* Get the error message content */ + regerror(err, &c->preg, buf, bl); + TRACE_DEBUG(INFO, "\t%s", buf); + + /* Free the buffer, return the error */ + free(buf); + return (err == REG_ESPACE) ? ENOMEM : EINVAL; + } + } + return 0; +} + +/* Search the first matching rule in the config */ +static int find_rule(struct msg * msg, struct ard_rule ** found) +{ + struct fd_list * li; + struct msg_hdr * mhdr = NULL; + struct peer_hdr * phdr = NULL; + + ASSERT(msg && found); + *found = NULL; + + /* Get the message's header */ + CHECK_FCT( fd_msg_hdr(msg, &mhdr) ); + + /* Get the message's origin */ + { + DiamId_t id; + size_t len; + CHECK_FCT( fd_msg_source_get(msg, &id, &len) ); + CHECK_FCT( fd_peer_getbyid(id, len, 0, &phdr) ); + } + + /* Now for each rule check if all criteria match */ + for (li = ard_conf->rules.next; li != &ard_conf->rules; li = li->next) { + struct fd_list * lic; + struct ard_rule * r = li->o; + int is_match = 1; + + for (lic = r->criteria.next; is_match && (lic != &r->criteria); lic = lic->next) { + struct ard_criteria * c = lic->o; + + /* Does this criteria match ? */ + switch (c->type) { + case APP_ID: + if (c->i != mhdr->msg_appl) + is_match = 0; + break; + + case FROM_ID: + CHECK_FCT( str_match(c, phdr->info.pi_diamid, phdr->info.pi_diamidlen, 1, &is_match) ); + break; + + case FROM_REALM: + if (phdr->info.runtime.pir_realm) { + CHECK_FCT( str_match(c, phdr->info.runtime.pir_realm, phdr->info.runtime.pir_realmlen, 1, &is_match) ); + } else { + /* since we don't have the realm it was received from, assume it does not match */ + TRACE_DEBUG(INFO, "Missing realm info for peer '%s', skipping rule %p", phdr->info.pi_diamid, r); + is_match = 0; + } + break; + + case AVP_INT: + case AVP_STR: + /* We have to search the whole message for the matching AVP */ + { + is_match = 0; + struct avp * avp = NULL; + CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + while (avp && !is_match) { + struct avp_hdr * ahdr = NULL; + CHECK_FCT( fd_msg_avp_hdr(avp, &ahdr) ); + + if ( (ahdr->avp_code == c->avp_info.avp_code) + && (ahdr->avp_vendor == c->avp_info.avp_vendor) ) /* always 0 if no V flag */ + { + /* dict-parse this AVP to ensure it has a value */ + CHECK_FCT( fd_msg_parse_dict( avp, fd_g_config->cnf_dict, NULL ) ); + + /* Now check if the value matches our criteria */ + if (c->type == AVP_INT) { + if (ahdr->avp_value->u32 == c->i) + is_match = 1; + } else { + /* it is AVP_STR */ + CHECK_FCT( str_match(c, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0, &is_match) ); + } + + if (is_match) + break; + } + + /* go to next */ + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); + } + + } + + break; + + } + } + + if (is_match) { + /* We found the first rule that matches for this message */ + *found = r; + break; + } + } + + return 0; +} + +/* The forward callback */ +int ard_rule_apply(void * cbdata, struct msg ** msg) +{ + struct ard_rule * rule = NULL; + + TRACE_ENTRY("%p %p", cbdata, msg); + CHECK_PARAMS(msg && *msg); + + /* First, check if we have a rule that applies to this message */ + CHECK_FCT( find_rule(*msg, &rule) ); + + if (rule) { + struct avp * avp; + union avp_value val; + struct fd_list * li; + + /* We have to reply a Redirect message in this case */ + CHECK_FCT( fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR) ); + + CHECK_FCT( fd_msg_rescode_set( *msg, "DIAMETER_REDIRECT_INDICATION", NULL, NULL, 1 ) ); + + /* Now add the Redirect-* AVPs */ + CHECK_FCT( fd_msg_avp_new( avp_Redirect_Host_Usage, 0, &avp ) ); + val.u32 = rule->type; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( *msg, MSG_BRW_LAST_CHILD, avp ) ); + + if (rule->type) { + CHECK_FCT( fd_msg_avp_new( avp_Redirect_Max_Cache_Time, 0, &avp ) ); + val.u32 = rule->rct ?: ard_conf->default_rct; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( *msg, MSG_BRW_LAST_CHILD, avp ) ); + } + + for (li = rule->targets.next; li != &rule->targets; li = li->next) { + struct ard_target * t = li->o; + + CHECK_FCT( fd_msg_avp_new( avp_Redirect_Host, 0, &avp ) ); + val.os.data = t->s; + val.os.len = t->l; + CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); + CHECK_FCT( fd_msg_avp_add( *msg, MSG_BRW_LAST_CHILD, avp ) ); + } + + /* Send this answer */ + CHECK_FCT( fd_msg_send( msg, NULL, NULL) ); + } + + return 0; +} +