changeset 168:6db078b955e3

Completed rt_default extension
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 02 Feb 2010 10:15:05 +0900
parents 967e579beb64
children f8507f57a3c0
files doc/rt_default.conf.sample extensions/rt_default/CMakeLists.txt extensions/rt_default/rt_default-host.h.in extensions/rt_default/rt_default.c extensions/rt_default/rt_default.h extensions/rt_default/rtd_conf.l extensions/rt_default/rtd_conf.y extensions/rt_default/rtd_rules.c freeDiameter/routing_dispatch.c include/freeDiameter/CMakeLists.txt include/freeDiameter/libfreeDiameter.h libfreeDiameter/rt_data.c
diffstat 12 files changed, 593 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/doc/rt_default.conf.sample	Fri Jan 29 18:57:40 2010 +0900
+++ b/doc/rt_default.conf.sample	Tue Feb 02 10:15:05 2010 +0900
@@ -44,15 +44,15 @@
 #  - and a SCORE, that is added to the matching peer's current score.
 #
 # In the following definitions, "STR/REG" stands for:
-#   - a quoted string "some.peer" that will match exactly this string, or
-#   - a bracket-quoted string ["some regex"] that will be interpreted as a POSIX regular expression, and attempt to match the string.
+#   - 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.
 #
 # The RULE is specified as:
 #    CRITERIA : TARGET += SCORE ;
 #
 # The CRITERIA can be:
 #    *   		-> matches any message.
-#    oh="STR/REG"	-> selects the message if the string or regular expression matches the message's Origin-Host
+#    oh="STR/REG"	-> selects the message if the string or regular expression matches the message's Origin-Host AVP content
 #    or="STR/REG"   	-> idem with Origin-Realm
 #    dh="STR/REG"   	-> idem with Destination-Host
 #    dr="STR/REG"   	-> idem with Destination-Realm
@@ -60,8 +60,8 @@
 #    si="STR/REG"   	-> idem with Session-Id
 #
 # The TARGET is also of a similar form:
-#    i="STR/REG"	-> Will apply the score to this peer if its Diameter-Id is matched by the string or regular expression.
-#    r="STR/REG"	-> Idem with the peer's advertized Diameter-Realm.
+#    "STR/REG"		-> Will apply the score to this peer if its Diameter-Id is matched by the string or regular expression.
+#    rlm="STR/REG"	-> Idem with the peer's advertized Diameter-Realm.
 #
 # The SCORE is either numeric (positive or negative), or one of the following constants (see values in freeDiameter.h):
 #    NO_DELIVERY
@@ -78,14 +78,14 @@
 #
 #
 # Here are some examples:
-#  1) Rule to add a default next-hop to all messages:
-#    * : i="proxy.testbed.aaa" += DEFAULT ;
+#  1) Rule to add a default next-hop peer to all messages:
+#    * : "proxy.testbed.aaa" += DEFAULT ;
 #
 #  2) Rule to route messages for a given realm (realmA) through another realm (realmB):
-#    dr="realmA" : r="realmB" += DEFAULT_REALM ;
+#    dr="realmA" : rlm="realmB" += DEFAULT_REALM ;
 #
 #  3) Avoid sending messages with decorated NAI to the proxy A:
-#    un=[".+!.+@.+"] : i="proxy.A" += NO_DELIVERY ;
+#    un=[".+!.+@.+"] : "proxy.A" += NO_DELIVERY ;
 
 
 
--- a/extensions/rt_default/CMakeLists.txt	Fri Jan 29 18:57:40 2010 +0900
+++ b/extensions/rt_default/CMakeLists.txt	Tue Feb 02 10:15:05 2010 +0900
@@ -1,6 +1,18 @@
 # The rt_default extension
 PROJECT("Configurable routing 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}/rt_default-host.h.in ${CMAKE_CURRENT_BINARY_DIR}/rt_default-host.h)
+
 # Parser files
 BISON_FILE(rtd_conf.y)
 FLEX_FILE(rtd_conf.l)
@@ -16,5 +28,7 @@
 	rtd_rules.c
 )
 
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
 # Compile these files as a freeDiameter extension
 FD_ADD_EXTENSION(rt_default ${RT_DEFAULT_SRC})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/rt_default/rt_default-host.h.in	Tue Feb 02 10:15:05 2010 +0900
@@ -0,0 +1,42 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Configuration from compile-time */
+#ifndef RTD_IS_CONFIG
+#define RTD_IS_CONFIG
+
+#cmakedefine HAVE_REG_STARTEND
+
+#endif /* RTD_IS_CONFIG */
--- a/extensions/rt_default/rt_default.c	Fri Jan 29 18:57:40 2010 +0900
+++ b/extensions/rt_default/rt_default.c	Tue Feb 02 10:15:05 2010 +0900
@@ -68,7 +68,7 @@
 	/* Parse the configuration file */
 	CHECK_FCT( rtd_conf_handle(conffile) );
 	
-#if 1
+#if 0
 	/* Dump the rules */
 	rtd_dump();
 #endif /* 0 */
--- a/extensions/rt_default/rt_default.h	Fri Jan 29 18:57:40 2010 +0900
+++ b/extensions/rt_default/rt_default.h	Tue Feb 02 10:15:05 2010 +0900
@@ -38,8 +38,12 @@
  *  See the rt_default.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 <rt_default-host.h>
+
 /* Parse the configuration file */
 int rtd_conf_handle(char * conffile);
 
--- a/extensions/rt_default/rtd_conf.l	Fri Jan 29 18:57:40 2010 +0900
+++ b/extensions/rt_default/rtd_conf.l	Tue Feb 02 10:15:05 2010 +0900
@@ -132,8 +132,7 @@
 (?i:"dr")	 	{	return DR;	}
 (?i:"un")	 	{	return UN;	}
 (?i:"si")	 	{	return SI;	}
-(?i:"i")	 	{	return IDENTITY;}
-(?i:"r")	 	{	return REALM;	}
+(?i:"rlm")	 	{	return REALM;	}
 			
 	/* Valid single characters for yyparse */
 [*:=+;]			{ return yytext[0]; }
--- a/extensions/rt_default/rtd_conf.y	Fri Jan 29 18:57:40 2010 +0900
+++ b/extensions/rt_default/rtd_conf.y	Tue Feb 02 10:15:05 2010 +0900
@@ -151,7 +151,6 @@
 %token 		UN
 %token 		SI
 
-%token 		IDENTITY
 %token 		REALM
 
 
@@ -179,6 +178,8 @@
 						yyerror (&yylloc, conffile, "An error occurred while adding a rule, aborting...");
 						YYERROR;
 					} );
+				
+				rules_added++;
 			}
 			;
 	
@@ -241,10 +242,10 @@
 			;
 
 	/* Details of the TARGET type */
-TARGET:			IDENTITY '=' TSTRING
+TARGET:			TSTRING
 			{
-				$$.str = $3.str;
-				$$.regex =$3.regex;
+				$$.str = $1.str;
+				$$.regex =$1.regex;
 				$$.type = RTD_TAR_ID;
 			}
 			| REALM '=' TSTRING
--- a/extensions/rt_default/rtd_rules.c	Fri Jan 29 18:57:40 2010 +0900
+++ b/extensions/rt_default/rtd_rules.c	Tue Feb 02 10:15:05 2010 +0900
@@ -49,65 +49,562 @@
  * Note: Except during configuration parsing and module termination, the lists are only ever accessed read-only, so we do not need a lock.
  */
 
-/* Structure to hold the data that is used for matching. Note that in case is_regex is true, the string to be matched must be \0-terminated. */
+/* Structure to hold the data that is used for matching. */
 struct match_data {
 	int	 is_regex;	/* determines how the string is matched */
 	char 	*plain;		/* match this value with strcasecmp if is_regex is false. The string is allocated by the lex tokenizer, must be freed at the end. */
 	regex_t  preg;		/* match with regexec if is_regex is true. regfree must be called at the end. A copy of the original string is anyway saved in plain. */
 };
 
+/* The sentinels for the TARGET lists */
+static struct fd_list	TARGETS[RTD_TAR_MAX];
+
 /* Structure of a TARGET element */
 struct target {
-	struct fd_list		chain;	/* link in the top-level list */
-	struct match_data	md;	/* the data to determine if the current candidate matches this element */
-	struct fd_list		rules;	/* Sentinel for the list of rules applying to this target */
+	struct fd_list		chain;			/* link in the top-level list */
+	struct match_data	md;			/* the data to determine if the current candidate matches this element */
+	struct fd_list		rules[RTD_CRI_MAX];	/* Sentinels for the lists of rules applying to this target. One list per rtd_crit_type */
 	/* note : we do not need the rtd_targ_type here, it is implied by the root of the list this target element is attached to */
 };
 
 /* Structure of a RULE element */
 struct rule {
 	struct fd_list		chain;	/* link in the parent target list */
-	enum rtd_crit_type	type;	/* What kind of criteria the message must match for the rule to apply */
-	struct match_data	md;	/* the data that the criteria must match, if it is different from RTD_CRI_ALL */
+	struct match_data	md;	/* the data that the criteria must match, -- ignored for RTD_CRI_ALL */
 	int			score;	/* The score added to the candidate, if the message matches this criteria */
+	/* The type of rule depends on the sentinel */
 };
 
-/* The sentinels for the TARGET lists */
-static struct fd_list	TARGETS[RTD_TAR_MAX];
+/*********************************************************************/
+
+/* 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;
+}
+
+/* Create a target item and initialize its content */
+static struct target * new_target(char * str, int regex)
+{
+	int i;
+	struct target *new = NULL;
+	CHECK_MALLOC_DO( new = malloc(sizeof(struct target)), return NULL );
+	memset(new, 0, sizeof(struct target));
+	
+	fd_list_init(&new->chain, new);
+	new->md.plain = str;
+	new->md.is_regex = regex;
+	if (regex) {
+		CHECK_FCT_DO( compile_regex(&new->md.preg, str),
+			{
+				free(new);
+				return NULL;
+			} );
+	}
+	for (i = 0; i < RTD_CRI_MAX; i++) {
+		fd_list_init(&new->rules[i], new);
+	}
+	return new;
+}
+
+/* Create a rule item and initialize its content; return NULL in case of error */
+static struct rule * new_rule(char * str, int regex, int score)
+{
+	struct rule *new = NULL;
+	CHECK_MALLOC_DO( new = malloc(sizeof(struct rule)), return NULL );
+	memset(new, 0, sizeof(struct rule));
+	
+	fd_list_init(&new->chain, new);
+	new->md.plain = str;
+	new->md.is_regex = regex;
+	if (regex) {
+		CHECK_FCT_DO( compile_regex(&new->md.preg, str),
+			{
+				free(new);
+				return NULL;
+			} );
+	}
+	new->score = score;
+	return new;
+}
+
+/* Debug functions */
+static void dump_rule(int indent, struct rule * rule)
+{
+	fd_log_debug("%*s%s%s%s += %d\n",
+		indent, "",
+		rule->md.is_regex ? "[" : "'",
+		rule->md.plain,
+		rule->md.is_regex ? "]" : "'",
+		rule->score);
+}
+static void dump_target(int indent, struct target * target)
+{
+	int i;
+	fd_log_debug("%*s%s%s%s :\n",
+		indent, "",
+		target->md.is_regex ? "[" : "'",
+		target->md.plain ?: "(empty)",
+		target->md.is_regex ? "]" : "'");
+	for (i = 0; i < RTD_CRI_MAX; i++) {
+		if (! FD_IS_LIST_EMPTY(&target->rules[i])) {
+			struct fd_list * li;
+			fd_log_debug("%*s  rules[%d]:\n",
+				indent, "", i);
+			for (li = target->rules[i].next; li != &target->rules[i]; li = li->next) {
+				dump_rule(indent + 3, (struct rule *)li);
+			}
+		}
+	}
+}
+
+static void clear_md(struct match_data * md)
+{
+	/* delete the string */
+	if (md->plain) {
+		free(md->plain);
+		md->plain = NULL;
+	}
+	
+	/* delete the preg if needed */
+	if (md->is_regex) {
+		regfree(&md->preg);
+		md->is_regex = 0;
+	}
+}
+
+/* Destroy a rule item */
+static void del_rule(struct rule * del)
+{
+	/* Unlink this rule */
+	fd_list_unlink(&del->chain);
+	
+	/* Delete the match data */
+	clear_md(&del->md);
+	
+	free(del);
+}
+
+/* Destroy a target item, and all its rules */
+static void del_target(struct target * del)
+{
+	int i;
+	
+	/* Unlink this target */
+	fd_list_unlink(&del->chain);
+	
+	/* Delete the match data */
+	clear_md(&del->md);
+	
+	/* Delete the children rules */
+	for (i = 0; i < RTD_CRI_MAX; i++) {
+		while (! FD_IS_LIST_EMPTY(&del->rules[i]) ) {
+			del_rule((struct rule *)(del->rules[i].next));
+		}
+	}
+	
+	free(del);
+}
 
 
+/* Compare a string with a match_data value. *res contains the result of the comparison (always >0 for regex non-match situations) */
+static int compare_match(char * str, size_t len, struct match_data * md, int * res)
+{
+	int err;
+	
+	CHECK_PARAMS( str && md && res );
+	
+	/* Plain strings: we compare with strncasecmp */
+	if (md->is_regex == 0) {
+		*res = strncasecmp(str, md->plain, len);
+		return 0;
+	}
 
+	/* Regexp */
+	*res = 1;
+	
+#ifdef HAVE_REG_STARTEND
+	{
+		regmatch_t pmatch[1];
+		memset(pmatch, 0, sizeof(pmatch));
+		pmatch[0].rm_so = 0;
+		pmatch[0].rm_eo = len;
+		err = regexec(&md->preg, str, 0, pmatch, REG_STARTEND);
+	}
+#else /* HAVE_REG_STARTEND */
+	{
+		/* We have to create a copy of the string in this case */
+		char *mystrcpy;
+		CHECK_MALLOC( mystrcpy = malloc(len + 1) );
+		memcpy(mystrcpy, str, len);
+		mystrcpy[len] = '\0';
+		err = regexec(&md->preg, mystrcpy, 0, NULL, 0);
+		free(mystrcpy);
+	}
+#endif /* HAVE_REG_STARTEND */
+	
+	/* Now check the result */
+	if (err == 0) {
+		/* We have a match */
+		*res = 0;
+		return 0;
+	}
+	
+	if (err == REG_NOMATCH) {
+		*res = 1;
+		return 0;
+	}
+	
+	/* In other cases, we have an error */
+	{
+		char * buf;
+		size_t bl;
+		
+		/* Error while compiling the regex */
+		TRACE_DEBUG(INFO, "Error while executing the regular expression '%s':", md->plain);
+		
+		/* Get the error message size */
+		bl = regerror(err, &md->preg, NULL, 0);
+		
+		/* Alloc the buffer for error message */
+		CHECK_MALLOC( buf = malloc(bl) );
+		
+		/* Get the error message content */
+		regerror(err, &md->preg, buf, bl);
+		TRACE_DEBUG(INFO, "\t%s", buf);
+		
+		/* Free the buffer, return the error */
+		free(buf);
+	}
+
+	return (err == REG_ESPACE) ? ENOMEM : EINVAL;
+}
+
+/* Search in list (targets or rules) the next matching item for string str(len). Returned in next_match, or *next_match == NULL if no more match. Re-enter with same next_match for the next one. */
+static int get_next_match(struct fd_list * list, char * str, size_t len, struct fd_list ** next_match)
+{
+	struct fd_list * li;
+	
+	TRACE_ENTRY("%p %p %zd %p", list, str, len, next_match);
+	CHECK_PARAMS(list && str && len && next_match);
+	
+	if (*next_match)
+		li = (*next_match)->next;
+	else
+		li = list->next;
+	
+	/* Initialize the return value */
+	*next_match = NULL;
+	
+	for ( ; li != list; li = li->next) {
+		int cmp;
+		struct {
+			struct fd_list 		chain;
+			struct match_data 	md;
+		} * next_item = (void *)li;
+		
+		/* Check if the string matches this next item */
+		CHECK_FCT( compare_match(str, len, &next_item->md, &cmp) );
+		
+		if (cmp == 0) {
+			/* matched! */
+			*next_match = li;
+			return 0;
+		}
+		
+		if (cmp < 0) /* we can stop searching */
+			break;
+	}
+	
+	/* We're done with the list */
+	return 0;
+}
+
+static struct dict_object * AVP_MODELS[RTD_CRI_MAX];
+
+/*********************************************************************/
+
+/* Prepare the module */
 int rtd_init(void)
 {
+	int i;
+	
 	TRACE_ENTRY();
 	
+	for (i = 0; i < RTD_TAR_MAX; i++) {
+		fd_list_init(&TARGETS[i], NULL);
+	}
+	
+	for (i = 1; i < RTD_CRI_MAX; i++) {
+		switch (i) {
+			case RTD_CRI_OH:
+				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &AVP_MODELS[i], ENOENT ));
+				break;
+			case RTD_CRI_OR:
+				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &AVP_MODELS[i], ENOENT ));
+				break;
+			case RTD_CRI_DH:
+				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &AVP_MODELS[i], ENOENT ));
+				break;
+			case RTD_CRI_DR:
+				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &AVP_MODELS[i], ENOENT ));
+				break;
+			case RTD_CRI_UN:
+				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &AVP_MODELS[i], ENOENT ));
+				break;
+			case RTD_CRI_SI:
+				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &AVP_MODELS[i], ENOENT ));
+				break;
+			default:
+				TRACE_DEBUG(INFO, "Missing definition in extension initializer");
+				ASSERT( 0 );
+				return EINVAL;
+		}
+	}
+	
 	return 0;
 }
 
+/* Destroy the module's data */
 void rtd_fini(void)
 {
+	int i;
+	
 	TRACE_ENTRY();
 
+	for (i = 0; i < RTD_TAR_MAX; i++) {
+		while (!FD_IS_LIST_EMPTY(&TARGETS[i])) {
+			del_target((struct target *) TARGETS[i].next);
+		}
+	}
+	
 }
 
+/* Add a new rule in the repository. this is called when the configuration file is being parsed */
 int rtd_add(enum rtd_crit_type ct, char * criteria, enum rtd_targ_type tt, char * target, int score, int flags)
 {
+	struct fd_list * target_suiv = NULL;
+	struct fd_list * rule_suiv = NULL;
+	struct target * trg = NULL;
+	struct rule * rul = NULL;
+	
 	TRACE_ENTRY("%d %p %d %p %d %x", ct, criteria, tt, target, score, flags);
-	
 	CHECK_PARAMS((ct < RTD_CRI_MAX) && ((ct == 0) || criteria) && (tt < RTD_TAR_MAX) && target);
 	
+	/* First, search in the TARGET list if we already have this target */
+	for (target_suiv = TARGETS[tt].next; target_suiv != &TARGETS[tt]; target_suiv = target_suiv->next) {
+		int cmp;
+		struct target * cur = (struct target *)target_suiv;
+		
+		if (flags & RTD_TARG_REG) {
+			/* We are adding a regexp, it is saved in the list before the plain expressions */
+			if (cur->md.is_regex == 0)
+				break;	
+		} else {
+			/* We are adding a plain expression, save it after all regexps */
+			if (cur->md.is_regex != 0)
+				continue;
+		}
+		
+		/* At this point, the type is the same, so compare the plain string value */
+		cmp = strcmp(cur->md.plain, target);
+		if (cmp < 0)
+			continue;
+		
+		if (cmp == 0) /* We already have a target with the same string */
+			trg = cur;
+		
+		break;
+	}
 	
+	if (trg) {
+		/* Ok, we can free the target string, we will use the previously allocated one */
+		free(target);
+	} else {
+		CHECK_MALLOC( trg = new_target(target, flags & RTD_TARG_REG) );
+		fd_list_insert_before( target_suiv, &trg->chain );
+	}
+	
+	/* Now, search for the rule position in this target's list */
+	if (ct == 0) {
+		/* Special case: we don't have a criteria -- always create a rule element */
+		CHECK_MALLOC( rul = new_rule(NULL, 0, score) );
+		fd_list_insert_before( &trg->rules[0], &rul->chain );
+	} else {
+		for (rule_suiv = trg->rules[ct].next; rule_suiv != &trg->rules[ct]; rule_suiv = rule_suiv->next) {
+			int cmp;
+			struct rule * cur = (struct rule *)rule_suiv;
+
+			if (flags & RTD_CRIT_REG) {
+				/* We are adding a regexp, it is saved in the list before the plain expressions */
+				if (cur->md.is_regex == 0)
+					break;	
+			} else {
+				/* We are adding a plain expression, save it after all regexps */
+				if (cur->md.is_regex != 0)
+					continue;
+			}
+
+			/* At this point, the type is the same, so compare the plain string value */
+			cmp = strcmp(cur->md.plain, criteria);
+			if (cmp < 0)
+				continue;
+
+			if (cmp == 0) /* We already have a target with the same string */
+				rul = cur;
+
+			break;
+		}
+	
+		if (rul) {
+			/* Ok, we can free the target string, we will use the previously allocated one */
+			free(criteria);
+			TRACE_DEBUG(INFO, "Warning: duplicate rule (%s : %s) found, merging score...", rul->md.plain, trg->md.plain);
+			rul->score += score;
+		} else {
+			CHECK_MALLOC( rul = new_rule(criteria, flags & RTD_CRIT_REG, score) );
+			fd_list_insert_before( rule_suiv, &rul->chain );
+		}
+	}
 	
 	return 0;
 }
 
+/* Check if a message and list of eligible candidate match any of our rules, and update its score according to it. */
 int rtd_process( struct msg * msg, struct fd_list * candidates )
 {
-	return ENOTSUP;
+	struct fd_list * li;
+	struct {
+		enum { NOT_RESOLVED_YET = 0, NOT_FOUND, FOUND } status;
+		union avp_value * avp;
+	} parsed_msg_avp[RTD_CRI_MAX];
+	
+	TRACE_ENTRY("%p %p", msg, candidates);
+	CHECK_PARAMS(msg && candidates);
+	
+	/* We delay looking for the AVPs in the message until we really need them. Another approach would be to parse the message once and save all needed AVPs. */
+	memset(parsed_msg_avp, 0, sizeof(parsed_msg_avp));
+	
+	/* For each candidate in the list */
+	for (li = candidates->next; li != candidates; li = li->next) {
+		struct rtd_candidate * cand = (struct rtd_candidate *)li;
+		int i;
+		struct {
+			char * str;
+			size_t len;
+		} cand_data[RTD_TAR_MAX] = {
+			{ cand->diamid,  strlen(cand->diamid) },
+			{ cand->realm,   strlen(cand->realm)  }
+		};
+		
+		for (i = 0; i < RTD_TAR_MAX; i++) {
+			/* Search the next rule matching this candidate in the i-th target list */
+			struct target * target = NULL;
+			
+			do {
+				int j;
+				struct fd_list * l;
+				struct rule * r;
+				
+				CHECK_FCT ( get_next_match( &TARGETS[i], cand_data[i].str, cand_data[i].len, (void *)&target) );
+				if (!target)
+					break;
+				
+				/* First, apply all rules of criteria RTD_CRI_ALL */
+				for ( l = target->rules[0].next; l != &target->rules[0]; l = l->next ) {
+					r = (struct rule *)l;
+					cand->score += r->score;
+					TRACE_DEBUG(ANNOYING, "Applied rule {'%s' : '%s' += %d} to candidate '%s'", r->md.plain, target->md.plain, r->score, cand->diamid);
+				}
+				
+				/* The target is matching this candidate, check if there are additional rules criteria matching this message. */
+				for ( j = 1; j < RTD_CRI_MAX; j++ ) {
+					if ( ! FD_IS_LIST_EMPTY(&target->rules[j]) ) {
+						/* if needed, find the required data in the message */
+						if (parsed_msg_avp[j].status == NOT_RESOLVED_YET) {
+							struct avp * avp = NULL;
+							/* Search for the AVP in the message */
+							CHECK_FCT( fd_msg_search_avp ( msg, AVP_MODELS[j], &avp ) );
+							if (avp == NULL) {
+								parsed_msg_avp[j].status = NOT_FOUND;
+							} else {
+								struct avp_hdr * ahdr = NULL;
+								CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
+								if (ahdr->avp_value == NULL) {
+									/* This should not happen, but anyway let's just ignore it */
+									parsed_msg_avp[j].status = NOT_FOUND;
+								} else {
+									/* OK, we got the AVP */
+									parsed_msg_avp[j].status = FOUND;
+									parsed_msg_avp[j].avp = ahdr->avp_value;
+								}
+							}
+						}
+						
+						/* If we did not find the data for these rules in the message, just skip the series */
+						if (parsed_msg_avp[j].status == NOT_FOUND) {
+							TRACE_DEBUG(ANNOYING, "Skipping series of rules %d of target '%s', criteria absent from the message", j, target->md.plain);
+							continue;
+						}
+						
+						/* OK, we can now check if one of our rule's criteria match the message content */
+						r = NULL;
+						do {
+							CHECK_FCT ( get_next_match( &target->rules[j], parsed_msg_avp[j].avp->os.data, parsed_msg_avp[j].avp->os.len, (void *)&r) );
+							if (!r)
+								break;
+							
+							cand->score += r->score;
+							TRACE_DEBUG(ANNOYING, "Applied rule {'%s' : '%s' += %d} to candidate '%s'", r->md.plain, target->md.plain, r->score, cand->diamid);
+						} while (1);
+					}
+				}
+			} while (1);
+		}
+	}
+	
+	return 0;
 }
 
 void rtd_dump(void)
 {
+	int i;
 	fd_log_debug("[rt_default] Dumping rules repository...\n");
+	for (i = 0; i < RTD_TAR_MAX; i++) {
+		if (!FD_IS_LIST_EMPTY( &TARGETS[i] )) {
+			struct fd_list * li;
+			fd_log_debug("  Targets list %d:\n", i);
+			for (li = TARGETS[i].next; li != &TARGETS[i]; li = li->next) {
+				dump_target(4, (struct target *)li);
+			}
+		}
+	}
+	
 	fd_log_debug("[rt_default] End of dump\n");
 }
--- a/freeDiameter/routing_dispatch.c	Fri Jan 29 18:57:40 2010 +0900
+++ b/freeDiameter/routing_dispatch.c	Tue Feb 02 10:15:05 2010 +0900
@@ -844,7 +844,7 @@
 		CHECK_FCT( pthread_rwlock_rdlock(&fd_g_activ_peers_rw) );
 		for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) {
 			struct fd_peer * p = (struct fd_peer *)li->o;
-			CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
+			CHECK_FCT_DO( ret = fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid, p->p_hdr.info.runtime.pir_realm), { CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), ); return ret; } );
 		}
 		CHECK_FCT( pthread_rwlock_unlock(&fd_g_activ_peers_rw) );
 
--- a/include/freeDiameter/CMakeLists.txt	Fri Jan 29 18:57:40 2010 +0900
+++ b/include/freeDiameter/CMakeLists.txt	Tue Feb 02 10:15:05 2010 +0900
@@ -64,7 +64,8 @@
 	ENDIF(NOT HAVE_NATIVE_SCTP)
 	# Now check the number of args of this function, since it changed between Ubuntu 9.04 and 9.10
    	SET(CHECK_SCTP_CONNECTX_4_ARGS_SOURCE_CODE "
-		#include <netinet/sctp.h>;
+		#include <unistd.h>
+		#include <netinet/sctp.h>
 		int main() {
 		   return sctp_connectx(0, NULL, 0, NULL);
 		}
--- a/include/freeDiameter/libfreeDiameter.h	Fri Jan 29 18:57:40 2010 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Tue Feb 02 10:15:05 2010 +0900
@@ -1631,7 +1631,7 @@
 /*============================================================*/
 
 /* The following functions are helpers for the routing module. 
-  The routing data is stored in the message it-self. */
+  The routing data is stored in the message itself. */
 
 /* Structure that contains the routing data for a message */
 struct rt_data;
@@ -1641,7 +1641,7 @@
 void fd_rtd_free(struct rt_data ** rtd);
 
 /* Add a peer to the candidates list */
-int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid);
+int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm);
 
 /* Remove a peer from the candidates (if it is found) */
 void fd_rtd_candidate_del(struct rt_data * rtd, char * peerid, size_t sz /* if !0, peerid does not need to be \0 terminated */);
@@ -1656,6 +1656,7 @@
 struct rtd_candidate {
 	struct fd_list	chain;	/* link in the list returned by the previous fct */
 	char *		diamid;	/* the diameter Id of the peer */
+	char *		realm;	/* the diameter realm of the peer (if known) */
 	int		score;	/* the current routing score for this peer, see fd_rt_out_register definition for details */
 };
 
--- a/libfreeDiameter/rt_data.c	Fri Jan 29 18:57:40 2010 +0900
+++ b/libfreeDiameter/rt_data.c	Tue Feb 02 10:15:05 2010 +0900
@@ -88,6 +88,7 @@
 		
 		fd_list_unlink(&c->chain);
 		free(c->diamid);
+		free(c->realm);
 		free(c);
 	}
 	
@@ -106,7 +107,7 @@
 }
 
 /* Add a peer to the candidates list */
-int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid)
+int  fd_rtd_candidate_add(struct rt_data * rtd, char * peerid, char * realm)
 {
 	struct fd_list * prev;
 	struct rtd_candidate * new;
@@ -129,6 +130,7 @@
 	memset(new, 0, sizeof(struct rtd_candidate) );
 	fd_list_init(&new->chain, NULL);
 	CHECK_MALLOC( new->diamid = strdup(peerid) );
+	CHECK_MALLOC( new->realm = strdup(realm) );
 	
 	fd_list_insert_after(prev, &new->chain);
 	
@@ -164,6 +166,7 @@
 			/* Found it! Remove it */
 			fd_list_unlink(&c->chain);
 			free(c->diamid);
+			free(c->realm);
 			free(c);
 			break;
 		}
"Welcome to our mercurial repository"