1 /*********************************************************************************************************
2 * Software License Agreement (BSD License)                                                               *
3 * Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
4 *													 *
5 * Copyright (c) 2019, WIDE Project and NICT								 *
6 * All rights reserved.											 *
7 * 													 *
8 * Redistribution and use of this software in source and binary forms, with or without modification, are  *
9 * permitted provided that the following conditions are met:						 *
10 * 													 *
11 * * Redistributions of source code must retain the above 						 *
12 *   copyright notice, this list of conditions and the 							 *
13 *   following disclaimer.										 *
14 *    													 *
15 * * Redistributions in binary form must reproduce the above 						 *
16 *   copyright notice, this list of conditions and the 							 *
17 *   following disclaimer in the documentation and/or other						 *
18 *   materials provided with the distribution.								 *
19 * 													 *
20 * * Neither the name of the WIDE Project or NICT nor the 						 *
21 *   names of its contributors may be used to endorse or 						 *
22 *   promote products derived from this software without 						 *
23 *   specific prior written permission of WIDE Project and 						 *
24 *   NICT.												 *
25 * 													 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
34 *********************************************************************************************************/
35 
36 #include "rt_default.h"
37 
38 /* The regular expressions header */
39 #include <regex.h>
40 
41 /* We will search for each candidate peer all the rules that are defined, and check which one applies to the message
42  * Therefore our repository is organized hierarchicaly.
43  *  At the top level, we have two lists of TARGETS (one for IDENTITY, one for REALM), ordered as follow:
44  *   - first, the TARGETS defined with a regular expression. We will try matching all regexp to all candidates in the list.
45  *   - 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.
46  *
47  *  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.
48  *
49  * Note: Access these only when holding rtd_lock; config reload may change the underlying data.
50  */
51 
52 /* Structure to hold the data that is used for matching. */
53 struct match_data {
54 	int	 is_regex;	/* determines how the string is matched */
55 	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. */
56 	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. */
57 };
58 
59 /* The sentinels for the TARGET lists */
60 static struct fd_list	TARGETS[RTD_TAR_MAX];
61 
62 /* Structure of a TARGET element */
63 struct target {
64 	struct fd_list		chain;			/* link in the top-level list */
65 	struct match_data	md;			/* the data to determine if the current candidate matches this element */
66 	struct fd_list		rules[RTD_CRI_MAX];	/* Sentinels for the lists of rules applying to this target. One list per rtd_crit_type */
67 	/* 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 */
68 };
69 
70 /* Structure of a RULE element */
71 struct rule {
72 	struct fd_list		chain;	/* link in the parent target list */
73 	struct match_data	md;	/* the data that the criteria must match, -- ignored for RTD_CRI_ALL */
74 	int			score;	/* The score added to the candidate, if the message matches this criteria */
75 	/* The type of rule depends on the sentinel */
76 };
77 
78 /*********************************************************************/
79 
80 /* Compile a regular expression pattern */
compile_regex(regex_t * preg,char * str)81 static int compile_regex( regex_t  * preg, char * str )
82 {
83 	int err;
84 
85 	/* Compile the regular expression */
86 	err = regcomp(preg, str, REG_EXTENDED | REG_NOSUB);
87 	if (err != 0) {
88 		char * buf;
89 		size_t bl;
90 
91 		/* Error while compiling the regex */
92 		TRACE_DEBUG(INFO, "Error while compiling the regular expression '%s':", str);
93 
94 		/* Get the error message size */
95 		bl = regerror(err, preg, NULL, 0);
96 
97 		/* Alloc the buffer for error message */
98 		CHECK_MALLOC( buf = malloc(bl) );
99 
100 		/* Get the error message content */
101 		regerror(err, preg, buf, bl);
102 		TRACE_DEBUG(INFO, "\t%s", buf);
103 
104 		/* Free the buffer, return the error */
105 		free(buf);
106 		return EINVAL;
107 	}
108 
109 	return 0;
110 }
111 
112 /* Create a target item and initialize its content */
new_target(char * str,int regex)113 static struct target * new_target(char * str, int regex)
114 {
115 	int i;
116 	struct target *new = NULL;
117 	CHECK_MALLOC_DO( new = malloc(sizeof(struct target)), return NULL );
118 	memset(new, 0, sizeof(struct target));
119 
120 	fd_list_init(&new->chain, new);
121 	new->md.plain = str;
122 	new->md.is_regex = regex;
123 	if (regex) {
124 		CHECK_FCT_DO( compile_regex(&new->md.preg, str),
125 			{
126 				free(new);
127 				return NULL;
128 			} );
129 	}
130 	for (i = 0; i < RTD_CRI_MAX; i++) {
131 		fd_list_init(&new->rules[i], new);
132 	}
133 	return new;
134 }
135 
136 /* Create a rule item and initialize its content; return NULL in case of error */
new_rule(char * str,int regex,int score)137 static struct rule * new_rule(char * str, int regex, int score)
138 {
139 	struct rule *new = NULL;
140 	CHECK_MALLOC_DO( new = malloc(sizeof(struct rule)), return NULL );
141 	memset(new, 0, sizeof(struct rule));
142 
143 	fd_list_init(&new->chain, new);
144 	new->md.plain = str;
145 	new->md.is_regex = regex;
146 	if (regex) {
147 		CHECK_FCT_DO( compile_regex(&new->md.preg, str),
148 			{
149 				free(new);
150 				return NULL;
151 			} );
152 	}
153 	new->score = score;
154 	return new;
155 }
156 
157 /* Debug functions */
dump_rule(int indent,struct rule * rule)158 static void dump_rule(int indent, struct rule * rule)
159 {
160 	fd_log_debug("%*s%s%s%s += %d",
161 		indent, "",
162 		rule->md.is_regex ? "[" : "'",
163 		rule->md.plain,
164 		rule->md.is_regex ? "]" : "'",
165 		rule->score);
166 }
dump_target(int indent,struct target * target)167 static void dump_target(int indent, struct target * target)
168 {
169 	int i;
170 	fd_log_debug("%*s%s%s%s :",
171 		indent, "",
172 		target->md.is_regex ? "[" : "'",
173 		target->md.plain ?: "(empty)",
174 		target->md.is_regex ? "]" : "'");
175 	for (i = 0; i < RTD_CRI_MAX; i++) {
176 		if (! FD_IS_LIST_EMPTY(&target->rules[i])) {
177 			struct fd_list * li;
178 			fd_log_debug("%*s  rules[%d]:",
179 				indent, "", i);
180 			for (li = target->rules[i].next; li != &target->rules[i]; li = li->next) {
181 				dump_rule(indent + 3, (struct rule *)li);
182 			}
183 		}
184 	}
185 }
186 
clear_md(struct match_data * md)187 static void clear_md(struct match_data * md)
188 {
189 	/* delete the string */
190 	if (md->plain) {
191 		free(md->plain);
192 		md->plain = NULL;
193 	}
194 
195 	/* delete the preg if needed */
196 	if (md->is_regex) {
197 		regfree(&md->preg);
198 		md->is_regex = 0;
199 	}
200 }
201 
202 /* Destroy a rule item */
del_rule(struct rule * del)203 static void del_rule(struct rule * del)
204 {
205 	/* Unlink this rule */
206 	fd_list_unlink(&del->chain);
207 
208 	/* Delete the match data */
209 	clear_md(&del->md);
210 
211 	free(del);
212 }
213 
214 /* Destroy a target item, and all its rules */
del_target(struct target * del)215 static void del_target(struct target * del)
216 {
217 	int i;
218 
219 	/* Unlink this target */
220 	fd_list_unlink(&del->chain);
221 
222 	/* Delete the match data */
223 	clear_md(&del->md);
224 
225 	/* Delete the children rules */
226 	for (i = 0; i < RTD_CRI_MAX; i++) {
227 		while (! FD_IS_LIST_EMPTY(&del->rules[i]) ) {
228 			del_rule((struct rule *)(del->rules[i].next));
229 		}
230 	}
231 
232 	free(del);
233 }
234 
235 
236 /* Compare a string with a match_data value. *res contains the result of the comparison (always >0 for regex non-match situations) */
compare_match(char * str,size_t len,struct match_data * md,int * res)237 static int compare_match(char * str, size_t len, struct match_data * md, int * res)
238 {
239 	int err;
240 
241 	CHECK_PARAMS( str && md && res );
242 
243 	/* Plain strings: we compare with strncasecmp */
244 	if (md->is_regex == 0) {
245 		*res = strncasecmp(str, md->plain, len);
246 		return 0;
247 	}
248 
249 	/* Regexp */
250 	*res = 1;
251 
252 #ifdef HAVE_REG_STARTEND
253 	{
254 		regmatch_t pmatch[1];
255 		memset(pmatch, 0, sizeof(pmatch));
256 		pmatch[0].rm_so = 0;
257 		pmatch[0].rm_eo = len;
258 		err = regexec(&md->preg, str, 0, pmatch, REG_STARTEND);
259 	}
260 #else /* HAVE_REG_STARTEND */
261 	{
262 		/* We have to create a copy of the string in this case */
263 		char *mystrcpy;
264 		CHECK_MALLOC( mystrcpy = os0dup(str, len) );
265 		err = regexec(&md->preg, mystrcpy, 0, NULL, 0);
266 		free(mystrcpy);
267 	}
268 #endif /* HAVE_REG_STARTEND */
269 
270 	/* Now check the result */
271 	if (err == 0) {
272 		/* We have a match */
273 		*res = 0;
274 		return 0;
275 	}
276 
277 	if (err == REG_NOMATCH) {
278 		*res = 1;
279 		return 0;
280 	}
281 
282 	/* In other cases, we have an error */
283 	{
284 		char * buf;
285 		size_t bl;
286 
287 		/* Error while compiling the regex */
288 		TRACE_DEBUG(INFO, "Error while executing the regular expression '%s':", md->plain);
289 
290 		/* Get the error message size */
291 		bl = regerror(err, &md->preg, NULL, 0);
292 
293 		/* Alloc the buffer for error message */
294 		CHECK_MALLOC( buf = malloc(bl) );
295 
296 		/* Get the error message content */
297 		regerror(err, &md->preg, buf, bl);
298 		TRACE_DEBUG(INFO, "\t%s", buf);
299 
300 		/* Free the buffer, return the error */
301 		free(buf);
302 	}
303 
304 	return (err == REG_ESPACE) ? ENOMEM : EINVAL;
305 }
306 
307 /* 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. */
get_next_match(struct fd_list * list,char * str,size_t len,struct fd_list ** next_match)308 static int get_next_match(struct fd_list * list, char * str, size_t len, struct fd_list ** next_match)
309 {
310 	struct fd_list * li;
311 
312 	TRACE_ENTRY("%p %p %zd %p", list, str, len, next_match);
313 	CHECK_PARAMS(list && str && len && next_match);
314 
315 	if (*next_match)
316 		li = (*next_match)->next;
317 	else
318 		li = list->next;
319 
320 	/* Initialize the return value */
321 	*next_match = NULL;
322 
323 	for ( ; li != list; li = li->next) {
324 		int cmp;
325 		struct {
326 			struct fd_list 		chain;
327 			struct match_data 	md;
328 		} * next_item = (void *)li;
329 
330 		/* Check if the string matches this next item */
331 		CHECK_FCT( compare_match(str, len, &next_item->md, &cmp) );
332 
333 		if (cmp == 0) {
334 			/* matched! */
335 			*next_match = li;
336 			return 0;
337 		}
338 
339 		if (cmp < 0) /* we can stop searching */
340 			break;
341 	}
342 
343 	/* We're done with the list */
344 	return 0;
345 }
346 
347 static struct dict_object * AVP_MODELS[RTD_CRI_MAX];
348 
349 /*********************************************************************/
350 
351 /* Prepare the module */
rtd_init(void)352 int rtd_init(void)
353 {
354 	int i;
355 
356 	TRACE_ENTRY();
357 
358 	for (i = 0; i < RTD_TAR_MAX; i++) {
359 		fd_list_init(&TARGETS[i], NULL);
360 	}
361 
362 	for (i = 1; i < RTD_CRI_MAX; i++) {
363 		switch (i) {
364 			case RTD_CRI_OH:
365 				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &AVP_MODELS[i], ENOENT ));
366 				break;
367 			case RTD_CRI_OR:
368 				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &AVP_MODELS[i], ENOENT ));
369 				break;
370 			case RTD_CRI_DH:
371 				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &AVP_MODELS[i], ENOENT ));
372 				break;
373 			case RTD_CRI_DR:
374 				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &AVP_MODELS[i], ENOENT ));
375 				break;
376 			case RTD_CRI_UN:
377 				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &AVP_MODELS[i], ENOENT ));
378 				break;
379 			case RTD_CRI_SI:
380 				CHECK_FCT( fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &AVP_MODELS[i], ENOENT ));
381 				break;
382 			default:
383 				TRACE_DEBUG(INFO, "Missing definition in extension initializer");
384 				ASSERT( 0 );
385 				return EINVAL;
386 		}
387 	}
388 
389 	return 0;
390 }
391 
free_targets(void)392 static void free_targets(void)
393 {
394 	int i;
395 
396 	for (i = 0; i < RTD_TAR_MAX; i++) {
397 		while (!FD_IS_LIST_EMPTY(&TARGETS[i])) {
398 			del_target((struct target *) TARGETS[i].next);
399 		}
400 	}
401 }
402 
403 /* Destroy the module's data */
rtd_fini(void)404 void rtd_fini(void)
405 {
406 	TRACE_ENTRY();
407 
408 	free_targets();
409 }
410 
411 /* Add a new rule in the repository. this is called when the configuration file is being parsed */
rtd_add(enum rtd_crit_type ct,char * criteria,enum rtd_targ_type tt,char * target,int score,int flags)412 int rtd_add(enum rtd_crit_type ct, char * criteria, enum rtd_targ_type tt, char * target, int score, int flags)
413 {
414 	struct fd_list * target_suiv = NULL;
415 	struct fd_list * rule_suiv = NULL;
416 	struct target * trg = NULL;
417 	struct rule * rul = NULL;
418 
419 	TRACE_ENTRY("%d %p %d %p %d %x", ct, criteria, tt, target, score, flags);
420 	CHECK_PARAMS((ct < RTD_CRI_MAX) && ((ct == RTD_CRI_ALL) || criteria) && (tt < RTD_TAR_MAX) && target);
421 
422 	/* First, search in the TARGET list if we already have this target */
423 	for (target_suiv = TARGETS[tt].next; target_suiv != &TARGETS[tt]; target_suiv = target_suiv->next) {
424 		int cmp;
425 		struct target * cur = (struct target *)target_suiv;
426 
427 		if (flags & RTD_TARG_REG) {
428 			/* We are adding a regexp, it is saved in the list before the plain expressions */
429 			if (cur->md.is_regex == 0)
430 				break;
431 		} else {
432 			/* We are adding a plain expression, save it after all regexps */
433 			if (cur->md.is_regex != 0)
434 				continue;
435 		}
436 
437 		/* At this point, the type is the same, so compare the plain string value */
438 		cmp = strcmp(cur->md.plain, target);
439 		if (cmp < 0)
440 			continue;
441 
442 		if (cmp == 0) /* We already have a target with the same string */
443 			trg = cur;
444 
445 		break;
446 	}
447 
448 	if (trg) {
449 		/* Ok, we can free the target string, we will use the previously allocated one */
450 		free(target);
451 	} else {
452 		CHECK_MALLOC( trg = new_target(target, flags & RTD_TARG_REG) );
453 		fd_list_insert_before( target_suiv, &trg->chain );
454 	}
455 
456 	/* Now, search for the rule position in this target's list */
457 	if (ct == RTD_CRI_ALL) {
458 		/* Special case: we don't have a criteria -- always create a rule element */
459 		CHECK_MALLOC( rul = new_rule(NULL, 0, score) );
460 		fd_list_insert_before( &trg->rules[RTD_CRI_ALL], &rul->chain );
461 	} else {
462 		for (rule_suiv = trg->rules[ct].next; rule_suiv != &trg->rules[ct]; rule_suiv = rule_suiv->next) {
463 			int cmp;
464 			struct rule * cur = (struct rule *)rule_suiv;
465 
466 			if (flags & RTD_CRIT_REG) {
467 				/* We are adding a regexp, it is saved in the list before the plain expressions */
468 				if (cur->md.is_regex == 0)
469 					break;
470 			} else {
471 				/* We are adding a plain expression, save it after all regexps */
472 				if (cur->md.is_regex != 0)
473 					continue;
474 			}
475 
476 			/* At this point, the type is the same, so compare the plain string value */
477 			cmp = strcmp(cur->md.plain, criteria);
478 			if (cmp < 0)
479 				continue;
480 
481 			if (cmp == 0) /* We already have a target with the same string */
482 				rul = cur;
483 
484 			break;
485 		}
486 
487 		if (rul) {
488 			/* Ok, we can free the target string, we will use the previously allocated one */
489 			free(criteria);
490 			TRACE_DEBUG(INFO, "Warning: duplicate rule (%s : %s) found, merging score...", rul->md.plain, trg->md.plain);
491 			rul->score += score;
492 		} else {
493 			CHECK_MALLOC( rul = new_rule(criteria, flags & RTD_CRIT_REG, score) );
494 			fd_list_insert_before( rule_suiv, &rul->chain );
495 		}
496 	}
497 
498 	return 0;
499 }
500 
rtd_conf_reload(char * config_file)501 void rtd_conf_reload(char *config_file)
502 {
503 	/* save old config in case reload goes wrong */
504 	struct fd_list old_config[RTD_TAR_MAX];
505 	int i;
506 
507 	for (i = 0; i < RTD_TAR_MAX; i++) {
508 		old_config[i] = TARGETS[i];
509 	}
510 	memset(TARGETS, 0, sizeof(*TARGETS) * RTD_TAR_MAX);
511 	for (i = 0; i < RTD_TAR_MAX; i++) {
512 		fd_list_init(&TARGETS[i], NULL);
513 	}
514 
515 	if (rtd_conf_handle(config_file) != 0) {
516 		fd_log_notice("rt_default: error reloading configuration, restoring previous configuration");
517 		free_targets();
518 		for (i = 0; i < RTD_TAR_MAX; i++) {
519 			TARGETS[i] = old_config[i];
520 		}
521 	} else {
522 		/* this has to be done in this weird way because the items contain back pointers referencing TARGETS */
523 		struct fd_list save_config[RTD_TAR_MAX];
524 		for (i = 0; i < RTD_TAR_MAX; i++) {
525 			save_config[i] = TARGETS[i];
526 			TARGETS[i] = old_config[i];
527 		}
528 		free_targets();
529 		for (i = 0; i < RTD_TAR_MAX; i++) {
530 			TARGETS[i] = save_config[i];
531 		}
532 	}
533 }
534 
535 /* Check if a message and list of eligible candidate match any of our rules, and update its score according to it. */
rtd_process(struct msg * msg,struct fd_list * candidates)536 int rtd_process( struct msg * msg, struct fd_list * candidates )
537 {
538 	struct fd_list * li;
539 	struct {
540 		enum { NOT_RESOLVED_YET = 0, NOT_FOUND, FOUND } status;
541 		union avp_value * avp;
542 	} parsed_msg_avp[RTD_CRI_MAX];
543 
544 	TRACE_ENTRY("%p %p", msg, candidates);
545 	CHECK_PARAMS(msg && candidates);
546 
547 	/* 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. */
548 	memset(parsed_msg_avp, 0, sizeof(parsed_msg_avp));
549 
550 	/* For each candidate in the list */
551 	for (li = candidates->next; li != candidates; li = li->next) {
552 		struct rtd_candidate * cand = (struct rtd_candidate *)li;
553 		int i;
554 		struct {
555 			char * str;
556 			size_t len;
557 		} cand_data[RTD_TAR_MAX] = {
558 			{ cand->diamid,  strlen(cand->diamid) },
559 			{ cand->realm,   strlen(cand->realm)  }
560 		};
561 
562 		for (i = 0; i < RTD_TAR_MAX; i++) {
563 			/* Search the next rule matching this candidate in the i-th target list */
564 			struct target * target = NULL;
565 
566 			do {
567 				int j;
568 				struct fd_list * l;
569 				struct rule * r;
570 
571 				CHECK_FCT ( get_next_match( &TARGETS[i], cand_data[i].str, cand_data[i].len, (void *)&target) );
572 				if (!target)
573 					break;
574 
575 				/* First, apply all rules of criteria RTD_CRI_ALL */
576 				for ( l = target->rules[RTD_CRI_ALL].next; l != &target->rules[RTD_CRI_ALL]; l = l->next ) {
577 					r = (struct rule *)l;
578 					cand->score += r->score;
579 					TRACE_DEBUG(ANNOYING, "Applied rule {'*' : '%s' += %d} to candidate '%s'", target->md.plain, r->score, cand->diamid);
580 				}
581 
582 				/* The target is matching this candidate, check if there are additional rules criteria matching this message. */
583 				for ( j = 1; j < RTD_CRI_MAX; j++ ) {
584 					if ( ! FD_IS_LIST_EMPTY(&target->rules[j]) ) {
585 						/* if needed, find the required data in the message */
586 						if (parsed_msg_avp[j].status == NOT_RESOLVED_YET) {
587 							struct avp * avp = NULL;
588 							/* Search for the AVP in the message */
589 							CHECK_FCT( fd_msg_search_avp ( msg, AVP_MODELS[j], &avp ) );
590 							if (avp == NULL) {
591 								parsed_msg_avp[j].status = NOT_FOUND;
592 							} else {
593 								struct avp_hdr * ahdr = NULL;
594 								CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
595 								if (ahdr->avp_value == NULL) {
596 									/* This should not happen, but anyway let's just ignore it */
597 									parsed_msg_avp[j].status = NOT_FOUND;
598 								} else {
599 									/* OK, we got the AVP */
600 									parsed_msg_avp[j].status = FOUND;
601 									parsed_msg_avp[j].avp = ahdr->avp_value;
602 								}
603 							}
604 						}
605 
606 						/* If we did not find the data for these rules in the message, just skip the series */
607 						if (parsed_msg_avp[j].status == NOT_FOUND) {
608 							TRACE_DEBUG(ANNOYING, "Skipping series of rules %d of target '%s', criteria absent from the message", j, target->md.plain);
609 							continue;
610 						}
611 
612 						/* OK, we can now check if one of our rule's criteria match the message content */
613 						r = NULL;
614 						do {
615 							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) );
616 							if (!r)
617 								break;
618 
619 							cand->score += r->score;
620 							TRACE_DEBUG(ANNOYING, "Applied rule {'%s' : '%s' += %d} to candidate '%s'", r->md.plain, target->md.plain, r->score, cand->diamid);
621 						} while (1);
622 					}
623 				}
624 			} while (1);
625 		}
626 	}
627 
628 	return 0;
629 }
630 
rtd_dump(void)631 void rtd_dump(void)
632 {
633 	int i;
634 	fd_log_debug("[rt_default] Dumping rules repository...");
635 	for (i = 0; i < RTD_TAR_MAX; i++) {
636 		if (!FD_IS_LIST_EMPTY( &TARGETS[i] )) {
637 			struct fd_list * li;
638 			fd_log_debug("  Targets list %d:", i);
639 			for (li = TARGETS[i].next; li != &TARGETS[i]; li = li->next) {
640 				dump_target(4, (struct target *)li);
641 			}
642 		}
643 	}
644 
645 	fd_log_debug("[rt_default] End of dump");
646 }
647