diff extensions/rt_rewrite/rt_rewrite_conf.y @ 1341:b0401251d8c0

rt_rewrite: new extension This extension allows rewriting messages: putting data from one AVP into another, or removing AVPs altogether. Written for Effortel Technologies SA and published with their consent.
author Thomas Klausner <tk@giga.or.at>
date Tue, 09 Apr 2019 16:01:29 +0200
parents
children edfb2b662b91
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/rt_rewrite/rt_rewrite_conf.y	Tue Apr 09 16:01:29 2019 +0200
@@ -0,0 +1,421 @@
+/*********************************************************************************************************
+ * Software License Agreement (BSD License)                                                               *
+ * Author: Thomas Klausner <tk@giga.or.at>                                                                *
+ *                                                                                                        *
+ * Copyright (c) 2018, Thomas Klausner                                                                    *
+ * All rights reserved.                                                                                   *
+ *                                                                                                        *
+ * Written under contract by Effortel Technologies SA, http://effortel.com/                               *
+ *                                                                                                        *
+ * 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.                                                            *
+ *                                                                                                        *
+ * 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.
+ */
+
+/* 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_rewrite.h"
+#include "rt_rewrite_conf.tab.h"	/* bison is not smart enough to define the YYLTYPE before including this code, so... */
+
+/* Forward declaration */
+int yyparse(char * conffile);
+void rt_rewrite_confrestart(FILE *input_file);
+
+/* copied from libfdproto/dictionary.c because the symbol is not public */
+static const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */
+	"Grouped", 	/* AVP_TYPE_GROUPED */
+	"Octetstring", 	/* AVP_TYPE_OCTETSTRING */
+	"Integer32", 	/* AVP_TYPE_INTEGER32 */
+	"Integer64", 	/* AVP_TYPE_INTEGER64 */
+	"Unsigned32", 	/* AVP_TYPE_UNSIGNED32 */
+	"Unsigned64", 	/* AVP_TYPE_UNSIGNED64 */
+	"Float32", 	/* AVP_TYPE_FLOAT32 */
+	"Float64"	/* AVP_TYPE_FLOAT64 */
+	};
+
+static struct avp_match *avp_match_new(char *name);
+
+static struct avp_match *source_target = NULL, *drop_target = NULL;
+static struct avp_target *dest_target = NULL;
+
+static void print_target(struct avp_target *target, char *prefix)
+{
+	char *output = NULL;
+	if (asprintf(&output, "%s -> /TOP/%s", prefix, target->name) == -1) {
+		fd_log_error("rt_rewrite: print_target: setup:  asprintf failed: %s", strerror(errno));
+		return;
+	}
+	for (target=target->child; target != NULL; target=target->child) {
+		char *new_output = NULL;
+		if (asprintf(&new_output, "%s/%s", output, target->name) == -1) {
+			fd_log_error("rt_rewrite: print_target: asprintf failed: %s", strerror(errno));
+			free(output);
+			return;
+		}
+		free(output);
+		output = new_output;
+		new_output = NULL;
+	}
+	fd_log_debug(output);
+	free(output);
+	return;
+}
+
+static void compare_avp_type(const char *source, const char *dest)
+{
+	struct dict_object *model_source, *model_dest;
+	struct dict_avp_data dictdata_source, dictdata_dest;
+
+	if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, source, &model_source, ENOENT) != 0) {
+		fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", source);
+		return;
+	}
+	if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, dest, &model_dest, ENOENT) != 0) {
+		fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", dest);
+		return;
+	}
+	fd_dict_getval(model_source, &dictdata_source);
+	fd_dict_getval(model_dest, &dictdata_dest);
+	if (dictdata_source.avp_basetype != dictdata_dest.avp_basetype) {
+		fd_log_notice("rt_rewrite: type mismatch: %s (type %s) mapped to %s (type %s) (continuing anyway)", source, type_base_name[dictdata_source.avp_basetype], dest, type_base_name[dictdata_dest.avp_basetype]);
+	}
+	return;
+}
+
+static void compare_avp_types(struct avp_match *start)
+{
+	struct avp_match *iter;
+	for (iter=start; iter != NULL; iter=iter->next) {
+		compare_avp_types(iter->children);
+		if (iter->target) {
+			struct avp_target *final;
+			final = iter->target;
+			while (final->child) {
+				final = final->child;
+			}
+			compare_avp_type(iter->name, final->name);
+		}
+	}
+	return;
+}
+
+static void dump_config(struct avp_match *start, char *prefix)
+{
+	char *new_prefix = NULL;
+	struct avp_match *iter;
+	for (iter=start; iter != NULL; iter=iter->next) {
+		if (asprintf(&new_prefix, "%s/%s", prefix, iter->name) == -1) {
+			fd_log_error("rt_rewrite: dump_config: asprintf failed: %s", strerror(errno));
+			return;
+		}
+		dump_config(iter->children, new_prefix);
+		if (iter->target) {
+			print_target(iter->target, new_prefix);
+		}
+		if (iter->drop) {
+			fd_log_debug("%s -> DROP", new_prefix);
+		}
+		free(new_prefix);
+		new_prefix = NULL;
+	}
+	return;
+}
+
+/* Parse the configuration file */
+int rt_rewrite_conf_handle(char * conffile)
+{
+	extern FILE * rt_rewrite_confin;
+	int ret;
+	char *top;
+
+	TRACE_ENTRY("%p", conffile);
+
+	TRACE_DEBUG (FULL, "Parsing configuration file: '%s'", conffile);
+
+	/* to match other entries */
+	if ((top=strdup("TOP")) == NULL) {
+		fd_log_error("strdup error: %s", strerror(errno));
+		return EINVAL;
+	}
+	if ((avp_match_start=avp_match_new(top)) == NULL) {
+		fd_log_error("malloc error: %s", strerror(errno));
+		free(top);
+		return EINVAL;
+	}
+	rt_rewrite_confin = fopen(conffile, "r");
+	if (rt_rewrite_confin == NULL) {
+		ret = errno;
+		fd_log_debug("Unable to open extension configuration file '%s' for reading: %s", conffile, strerror(ret));
+		TRACE_DEBUG (INFO, "rt_rewrite: error occurred, message logged -- configuration file.");
+		avp_match_free(avp_match_start);
+		avp_match_start = NULL;
+		return ret;
+	}
+
+	rt_rewrite_confrestart(rt_rewrite_confin);
+	ret = yyparse(conffile);
+
+	fclose(rt_rewrite_confin);
+
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "rt_rewrite: unable to parse the configuration file.");
+		avp_match_free(avp_match_start);
+		avp_match_start = NULL;
+		return EINVAL;
+	}
+
+	compare_avp_types(avp_match_start);
+	dump_config(avp_match_start, "");
+
+	return 0;
+}
+
+static int verify_avp(const char *name)
+{
+	struct dict_object *model;
+	struct dict_avp_data dictdata;
+
+	if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, name, &model, ENOENT) != 0) {
+		fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", name);
+		return -1;
+	}
+	fd_dict_getval(model, &dictdata);
+	if (dictdata.avp_basetype == AVP_TYPE_GROUPED) {
+		return 1;
+	}
+	return 0;
+}
+
+static struct avp_match *avp_match_new(char *name) {
+	struct avp_match *ret;
+
+	if ((ret=malloc(sizeof(*ret))) == NULL) {
+		fd_log_error("malloc error");
+		return NULL;
+	}
+	ret->name = name;
+	ret->next = NULL;
+	ret->children = NULL;
+	ret->target = NULL;
+	ret->drop = 0;
+	return ret;
+}
+
+static void avp_target_free(struct avp_target *target) {
+	struct avp_target *iter;
+
+	for (iter=target; iter != NULL; ) {
+		struct avp_target *next;
+		free(iter->name);
+		next = iter->child;
+		free(iter);
+		iter = next;
+	}
+}
+
+void avp_match_free(struct avp_match *match) {
+	struct avp_match *iter;
+
+	for (iter=match; iter != NULL; ) {
+		struct avp_match *next;
+		free(iter->name);
+		next = iter->next;
+		avp_match_free(iter->children);
+		avp_target_free(iter->target);
+		free(iter);
+		iter = next;
+	}
+}
+
+static struct avp_target *target_new(char *name) {
+	struct avp_target *ret;
+
+	if ((ret=malloc(sizeof(*ret))) == NULL) {
+		fd_log_error("malloc error");
+		return NULL;
+	}
+	ret->name = name;
+	ret->child = NULL;
+	return ret;
+}
+
+static struct avp_match *add_avp_next_to(char *name, struct avp_match *target)
+{
+	struct avp_match *iter, *prev;
+
+	if (target == NULL) {
+		return avp_match_new(name);
+	}
+
+	for (prev=iter=target; iter != NULL; iter=iter->next) {
+		if (strcmp(iter->name, name) == 0) {
+			return iter;
+		}
+		prev = iter;
+	}
+	prev->next = avp_match_new(name);
+	return prev->next;
+}
+
+static int add(struct avp_match **target, char *name)
+{
+	struct avp_match *temp;
+	if (verify_avp(name) < 0) {
+		return -1;
+	}
+	temp = add_avp_next_to(name, (*target)->children);
+	if ((*target)->children == NULL) {
+		(*target)->children = temp;
+	}
+	*target = temp;
+	return 0;
+}
+
+/* build tree for source */
+static int source_add(char *name)
+{
+	if (source_target == NULL) {
+		source_target = avp_match_start;
+	}
+	return add(&source_target, name);
+}
+
+/* build tree for destination */
+static int dest_add(char *name)
+{
+	struct avp_target *temp;
+
+	if (verify_avp(name) < 0) {
+		return -1;
+	}
+	if ((temp=target_new(name)) == NULL) {
+		dest_target = NULL;
+		source_target = NULL;
+		return -1;
+	}
+	if (dest_target == NULL) {
+		dest_target = temp;
+		source_target->target = dest_target;
+		source_target = NULL;
+		return 0;
+	}
+	dest_target->child = temp;
+	dest_target = temp;
+	return 0;
+}
+
+static void dest_finish(void)
+{
+	dest_target = NULL;
+}
+
+/* same as source_add, but for drop */
+static int drop_add(char *name)
+{
+	if (drop_target == NULL) {
+		drop_target = avp_match_start;
+	}
+	return add(&drop_target, name);
+}
+
+/* mark as to-drop */
+static void drop_finish(void)
+{
+	drop_target->drop = 1;
+	drop_target = NULL;
+}
+
+/* The Lex parser prototype */
+int rt_rewrite_conflex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* Function to report the errors */
+void yyerror (YYLTYPE *ploc, char * conffile, char const *s)
+{
+	TRACE_DEBUG(INFO, "rt_rewrite: 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 {
+	char 		*string;	/* The string is allocated by strdup in lex.*/
+}
+
+/* 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
+
+/* Tokens */
+%token 		MAP
+%token 		DROP
+
+
+/* -------------------------------------- */
+%%
+
+ /* The grammar definition */
+rules:			/* empty ok */
+			| rules map
+			| rules drop
+			;
+
+/* source -> destination mapping */
+map:			MAP '=' source_part '>' dest_part { dest_finish(); }
+			';'
+			;
+
+source_part: 		source_part ':' QSTRING { if (source_add($3) < 0) { YYERROR; } }
+			| QSTRING { if (source_add($1) < 0) { YYERROR; } }
+			;
+
+dest_part: 		dest_part ':' QSTRING { if (dest_add($3) < 0) { YYERROR; } }
+			| QSTRING { if (dest_add($1) < 0) { YYERROR; } }
+			;
+
+/* for dropping an AVP */
+drop:			DROP '=' drop_part { drop_finish(); }
+			';'
+			;
+
+drop_part: 		drop_part ':' QSTRING { if (drop_add($3) < 0) { YYERROR; } }
+			| QSTRING { if (drop_add($1) < 0) { YYERROR; } }
+			;
"Welcome to our mercurial repository"