Index: contrib/debian/changelog
===================================================================
--- contrib/debian/changelog	(revision 717)
+++ contrib/debian/changelog	(revision 722)
@@ -21,6 +21,8 @@
     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 <sdecugis@nict.go.jp>  Fri, 18 Feb 2011 14:47:24 +0900
+ -- Sebastien Decugis <sdecugis@nict.go.jp>  Fri, 18 Feb 2011 15:25:19 +0900
 
 freediameter (1.0.4) UNRELEASED; urgency=low
Index: doc/app_redirect.conf.sample
===================================================================
--- doc/app_redirect.conf.sample	(revision 722)
+++ doc/app_redirect.conf.sample	(revision 722)
@@ -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 ]
+
+######################################################################################
+
+
Index: extensions/CMakeLists.txt
===================================================================
--- extensions/CMakeLists.txt	(revision 717)
+++ extensions/CMakeLists.txt	(revision 722)
@@ -58,4 +58,6 @@
 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)
+
 
 ####
Index: extensions/app_redirect/CMakeLists.txt
===================================================================
--- extensions/app_redirect/CMakeLists.txt	(revision 722)
+++ extensions/app_redirect/CMakeLists.txt	(revision 722)
@@ -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 <unistd.h>
+	#include <regex.h>
+	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)
Index: extensions/app_redirect/app_redir.c
===================================================================
--- extensions/app_redirect/app_redir.c	(revision 722)
+++ extensions/app_redirect/app_redir.c	(revision 722)
@@ -0,0 +1,100 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 
+ * 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);
Index: extensions/app_redirect/app_redir.h
===================================================================
--- extensions/app_redirect/app_redir.h	(revision 722)
+++ extensions/app_redirect/app_redir.h	(revision 722)
@@ -0,0 +1,121 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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 <freeDiameter/extension.h>
+
+/* Host configuration for this specific extension */
+#include <ard-host.h>
+#include <regex.h>
+
+/* 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);
Index: extensions/app_redirect/ard-host.h.in
===================================================================
--- extensions/app_redirect/ard-host.h.in	(revision 722)
+++ extensions/app_redirect/ard-host.h.in	(revision 722)
@@ -0,0 +1,42 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Configuration from compile-time */
+#ifndef RTD_IS_CONFIG
+#define RTD_IS_CONFIG
+
+#cmakedefine HAVE_REG_STARTEND
+
+#endif /* RTD_IS_CONFIG */
Index: extensions/app_redirect/ard_conf.l
===================================================================
--- extensions/app_redirect/ard_conf.l	(revision 722)
+++ extensions/app_redirect/ard_conf.l	(revision 722)
@@ -0,0 +1,140 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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; 
+			}
+
+%%
Index: extensions/app_redirect/ard_conf.y
===================================================================
--- extensions/app_redirect/ard_conf.y	(revision 722)
+++ extensions/app_redirect/ard_conf.y	(revision 722)
@@ -0,0 +1,451 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 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 <tstring> TOK_TSTRING
+
+/* An integer value */
+%token <u32> 	TOK_U32VAL
+
+%type <u32>	rule_type
+%type <u32>	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);
+			}
+			;
Index: extensions/app_redirect/ard_rules.c
===================================================================
--- extensions/app_redirect/ard_rules.c	(revision 722)
+++ extensions/app_redirect/ard_rules.c	(revision 722)
@@ -0,0 +1,300 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "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;
+}
+
