Initial commit
Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/rt_default/CMakeLists.txt b/extensions/rt_default/CMakeLists.txt
new file mode 100644
index 0000000..bdaa42c
--- /dev/null
+++ b/extensions/rt_default/CMakeLists.txt
@@ -0,0 +1,43 @@
+# 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)
+SET_SOURCE_FILES_PROPERTIES(lex.rtd_conf.c rtd_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET( RT_DEFAULT_SRC
+ rt_default.c
+ rt_default.h
+ lex.rtd_conf.c
+ rtd_conf.tab.c
+ rtd_conf.tab.h
+ rtd_rules.c
+)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+# Compile these files as a freeDiameter extension
+FD_ADD_EXTENSION(rt_default ${RT_DEFAULT_SRC})
+
+
+####
+## INSTALL section ##
+
+# We install with the daemon component because it is a base feature.
+INSTALL(TARGETS rt_default
+ LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+ COMPONENT freeDiameter-daemon)
diff --git a/extensions/rt_default/rt_default-host.h.in b/extensions/rt_default/rt_default-host.h.in
new file mode 100644
index 0000000..34e05a5
--- /dev/null
+++ b/extensions/rt_default/rt_default-host.h.in
@@ -0,0 +1,42 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 --git a/extensions/rt_default/rt_default.c b/extensions/rt_default/rt_default.c
new file mode 100644
index 0000000..dd4057a
--- /dev/null
+++ b/extensions/rt_default/rt_default.c
@@ -0,0 +1,99 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, 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 "rt_default.h"
+
+/* The callback called on new messages */
+static int rtd_out(void * cbdata, struct msg ** pmsg, struct fd_list * candidates)
+{
+ struct msg * msg = *pmsg;
+ TRACE_ENTRY("%p %p %p", cbdata, msg, candidates);
+
+ CHECK_PARAMS(msg && candidates);
+
+ /* Simply pass it to the appropriate function */
+ if (FD_IS_LIST_EMPTY(candidates)) {
+ return 0;
+ } else {
+ return rtd_process( msg, candidates );
+ }
+}
+
+/* handler */
+static struct fd_rt_out_hdl * rtd_hdl = NULL;
+
+/* entry point */
+static int rtd_entry(char * conffile)
+{
+ TRACE_ENTRY("%p", conffile);
+
+ /* Initialize the repo */
+ CHECK_FCT( rtd_init() );
+
+ /* Parse the configuration file */
+ CHECK_FCT( rtd_conf_handle(conffile) );
+
+#if 0
+ /* Dump the rules */
+ rtd_dump();
+#endif /* 0 */
+
+ /* Register the callback */
+ CHECK_FCT( fd_rt_out_register( rtd_out, NULL, 5, &rtd_hdl ) );
+
+ /* We're done */
+ return 0;
+}
+
+/* Unload */
+void fd_ext_fini(void)
+{
+ TRACE_ENTRY();
+
+ /* Unregister the cb */
+ CHECK_FCT_DO( fd_rt_out_unregister ( rtd_hdl, NULL ), /* continue */ );
+
+ /* Destroy the data */
+ rtd_fini();
+
+ /* Done */
+ return ;
+}
+
+EXTENSION_ENTRY("rt_default", rtd_entry);
diff --git a/extensions/rt_default/rt_default.h b/extensions/rt_default/rt_default.h
new file mode 100644
index 0000000..8ecaa5e
--- /dev/null
+++ b/extensions/rt_default/rt_default.h
@@ -0,0 +1,86 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* 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 rt_default extension.
+ *
+ * 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);
+
+/* Initialize the rules repository */
+int rtd_init(void);
+
+/* Destroy the rules repository */
+void rtd_fini(void);
+
+/* Some constants definitions */
+enum rtd_crit_type {
+ RTD_CRI_ALL = 0,
+ RTD_CRI_OH,
+ RTD_CRI_OR,
+ RTD_CRI_DH,
+ RTD_CRI_DR,
+ RTD_CRI_UN,
+ RTD_CRI_SI,
+
+ RTD_CRI_MAX
+};
+
+enum rtd_targ_type {
+ RTD_TAR_ID = 0,
+ RTD_TAR_REALM,
+
+ RTD_TAR_MAX
+};
+
+#define RTD_CRIT_REG 0x1
+#define RTD_TARG_REG 0x2
+
+/* Add a rule */
+int rtd_add(enum rtd_crit_type ct, char * criteria, enum rtd_targ_type tt, char * target, int score, int flags);
+
+/* Process a message & peer list through the rules repository, updating the scores */
+int rtd_process( struct msg * msg, struct fd_list * candidates );
+
+/* For debug: dump the rule repository */
+void rtd_dump(void);
diff --git a/extensions/rt_default/rtd_conf.l b/extensions/rt_default/rtd_conf.l
new file mode 100644
index 0000000..b256aa8
--- /dev/null
+++ b/extensions/rt_default/rtd_conf.l
@@ -0,0 +1,146 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, 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 "rt_default.h"
+/* Include yacc tokens definitions */
+#include "rtd_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 integer value */
+ int ret=0;
+ ret = sscanf(yytext, "%i", &yylval->integer);
+ if (ret != 1) {
+ /* No matching: an error occurred */
+ fd_log_debug("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno));
+ return LEX_ERROR; /* trig an error in yacc parser */
+ /* Maybe we could REJECT instead of failing here? */
+ }
+ return INTEGER;
+ }
+
+ /* In addition, recognize some constant names as integers also */
+(?i:"NO_DELIVERY") { yylval->integer = FD_SCORE_NO_DELIVERY; return INTEGER; }
+(?i:"DEFAULT") { yylval->integer = FD_SCORE_DEFAULT; return INTEGER; }
+(?i:"DEFAULT_REALM") { yylval->integer = FD_SCORE_DEFAULT_REALM; return INTEGER; }
+(?i:"REALM") { yylval->integer = FD_SCORE_REALM; return INTEGER; }
+(?i:"REDIR_HOST") { yylval->integer = FD_SCORE_REDIR_HOST; return INTEGER; }
+(?i:"REDIR_APP") { yylval->integer = FD_SCORE_REDIR_APP; return INTEGER; }
+(?i:"REDIR_REALM") { yylval->integer = FD_SCORE_REDIR_REALM; return INTEGER; }
+(?i:"REDIR_REALM_APP") { yylval->integer = FD_SCORE_REDIR_REALM_APP; return INTEGER; }
+(?i:"REDIR_USER") { yylval->integer = FD_SCORE_REDIR_USER; return INTEGER; }
+(?i:"REDIR_SESSION") { yylval->integer = FD_SCORE_REDIR_SESSION; return INTEGER; }
+(?i:"FINALDEST") { yylval->integer = FD_SCORE_FINALDEST; return INTEGER; }
+
+ /* Recognize bracketed quoted strings */
+[[]{qstring}[]] {
+ /* Match a quoted string containing a regex */
+ CHECK_MALLOC_DO( yylval->string = strdup(yytext+2),
+ {
+ TRACE_DEBUG(INFO, "Unable to copy the string '%s': %s", yytext, strerror(errno));
+ return LEX_ERROR; /* trig an error in yacc parser */
+ } );
+ yylval->string[strlen(yytext) - 4] = '\0';
+ return BQSTRING;
+ }
+
+ /* Recognize quoted strings */
+{qstring} {
+ /* Match a quoted string. */
+ CHECK_MALLOC_DO( yylval->string = strdup(yytext+1),
+ {
+ TRACE_DEBUG(INFO, "Unable to copy the string '%s': %s", yytext, strerror(errno));
+ return LEX_ERROR; /* trig an error in yacc parser */
+ } );
+ yylval->string[strlen(yytext) - 2] = '\0';
+ return QSTRING;
+ }
+
+ /* The key words */
+(?i:"oh") { return OH; }
+(?i:"or") { return OR; }
+(?i:"dh") { return DH; }
+(?i:"dr") { return DR; }
+(?i:"un") { return UN; }
+(?i:"si") { return SI; }
+(?i:"rlm") { return REALM; }
+
+ /* 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'.", yylloc->first_line, yylloc->first_column, yytext);
+ return LEX_ERROR;
+ }
+
+%%
diff --git a/extensions/rt_default/rtd_conf.y b/extensions/rt_default/rtd_conf.y
new file mode 100644
index 0000000..d6ed329
--- /dev/null
+++ b/extensions/rt_default/rtd_conf.y
@@ -0,0 +1,273 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, 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/rt_default.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 "rt_default.h"
+#include "rtd_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;
+
+/* Parse the configuration file */
+int rtd_conf_handle(char * conffile)
+{
+ extern FILE * rtd_confin;
+ int ret;
+
+ TRACE_ENTRY("%p", conffile);
+
+ TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile);
+
+ rtd_confin = fopen(conffile, "r");
+ if (rtd_confin == NULL) {
+ ret = errno;
+ fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret));
+ TRACE_DEBUG (INFO, "Error occurred, message logged -- configuration file.");
+ return ret;
+ }
+
+ ret = yyparse(conffile);
+
+ fclose(rtd_confin);
+
+ if (ret != 0) {
+ TRACE_DEBUG (INFO, "Unable to parse the configuration file.");
+ return EINVAL;
+ } else {
+ TRACE_DEBUG(FULL, "Added %d RULES routing entries successfully.", rules_added);
+ }
+
+ return 0;
+}
+
+/* The Lex parser prototype */
+int rtd_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)
+ fd_log_debug("%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)
+ fd_log_debug("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+ else
+ fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+%}
+
+/* Values returned by lex for token */
+%union {
+ int integer; /* Store integer values */
+ char *string; /* The string is allocated by strdup in lex.*/
+ struct {
+ char * str;
+ int regex;
+ } tstring; /* typed string */
+ struct {
+ char * str;
+ int regex;
+ enum rtd_crit_type type;
+ } criteria;
+ struct {
+ char * str;
+ int regex;
+ enum rtd_targ_type type;
+ } target;
+}
+
+/* In case of error in the lexical analysis */
+%token LEX_ERROR
+
+/* A (de)quoted string (malloc'd in lex parser; it must be freed after use) */
+%token <string> QSTRING
+/* A (de)bracket-quoted string (malloc'd in lex parser; it must be freed after use): ["blahblah"] */
+%token <string> BQSTRING
+
+/* An integer value */
+%token <integer> INTEGER
+
+/* The types for this gramar */
+%type <tstring> TSTRING
+%type <criteria> CRITERIA
+%type <target> TARGET
+%type <integer> EXPR_INT
+
+/* Tokens */
+%token OH
+%token OR
+%token DH
+%token DR
+%token UN
+%token SI
+
+%token REALM
+
+
+
+/* -------------------------------------- */
+%%
+
+ /* The grammar definition */
+conffile: /* empty grammar is OK */
+ | conffile rule
+ ;
+
+ /* a RULE entry */
+rule: CRITERIA ':' TARGET '+' '=' EXPR_INT ';'
+ {
+ int flag = 0;
+ if ($1.regex)
+ flag |= RTD_CRIT_REG;
+ if ($3.regex)
+ flag |= RTD_TARG_REG;
+
+ /* Add this rule to the repository */
+ CHECK_FCT_DO( rtd_add($1.type, $1.str, $3.type, $3.str, $6, flag),
+ {
+ yyerror (&yylloc, conffile, "An error occurred while adding a rule, aborting...");
+ YYERROR;
+ } );
+
+ rules_added++;
+ }
+ ;
+
+ /* QSTRING and BQSTRING are equivalent in the grammar */
+TSTRING: QSTRING
+ {
+ $$.str = $1;
+ $$.regex = 0;
+ }
+ | BQSTRING
+ {
+ $$.str = $1;
+ $$.regex = 1;
+ }
+ ;
+
+ /* Details of the CRITERIA type */
+CRITERIA: '*'
+ {
+ $$.str = NULL;
+ $$.regex = 0;
+ $$.type = RTD_CRI_ALL;
+ }
+ | OH '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_CRI_OH;
+ }
+ | OR '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_CRI_OR;
+ }
+ | DH '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_CRI_DH;
+ }
+ | DR '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_CRI_DR;
+ }
+ | UN '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_CRI_UN;
+ }
+ | SI '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_CRI_SI;
+ }
+ ;
+
+ /* Details of the TARGET type */
+TARGET: TSTRING
+ {
+ $$.str = $1.str;
+ $$.regex =$1.regex;
+ $$.type = RTD_TAR_ID;
+ }
+ | REALM '=' TSTRING
+ {
+ $$.str = $3.str;
+ $$.regex =$3.regex;
+ $$.type = RTD_TAR_REALM;
+ }
+ ;
+
+ /* An expression that has an integer value; we allow + and - operators cause it is convenient */
+EXPR_INT: INTEGER
+ {
+ $$ = $1;
+ }
+ | EXPR_INT '+' INTEGER
+ {
+ $$ = $1 + $3;
+ }
+ | EXPR_INT '-' INTEGER
+ {
+ $$ = $1 - $3;
+ }
+ ;
diff --git a/extensions/rt_default/rtd_rules.c b/extensions/rt_default/rtd_rules.c
new file mode 100644
index 0000000..8d3a53c
--- /dev/null
+++ b/extensions/rt_default/rtd_rules.c
@@ -0,0 +1,608 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License) *
+* Author: Sebastien Decugis <sdecugis@freediameter.net> *
+* *
+* Copyright (c) 2013, 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 "rt_default.h"
+
+/* The regular expressions header */
+#include <regex.h>
+
+/* We will search for each candidate peer all the rules that are defined, and check which one applies to the message
+ * Therefore our repository is organized hierarchicaly.
+ * At the top level, we have two lists of TARGETS (one for IDENTITY, one for REALM), ordered as follow:
+ * - first, the TARGETS defined with a regular expression. We will try matching all regexp to all candidates in the list.
+ * - then, the TARGETS defined by a plain text. We don't have to compare the whole list to each candidate since the list is ordered.
+ *
+ * Under each TARGET element, we have the list of RULES that are defined for this target, ordered by CRITERIA type, then is_regex, then string value.
+ *
+ * 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. */
+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[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 */
+ 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 */
+};
+
+/*********************************************************************/
+
+/* 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",
+ 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 :",
+ 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]:",
+ 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 = os0dup(str, len) );
+ 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 octet 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 == RTD_CRI_ALL) || 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 == RTD_CRI_ALL) {
+ /* 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[RTD_CRI_ALL], &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 )
+{
+ 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[RTD_CRI_ALL].next; l != &target->rules[RTD_CRI_ALL]; l = l->next ) {
+ r = (struct rule *)l;
+ cand->score += r->score;
+ TRACE_DEBUG(ANNOYING, "Applied rule {'*' : '%s' += %d} to candidate '%s'", 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], (char *) /* is this cast safe? */ 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...");
+ 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:", 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");
+}