Mercurial > hg > freeDiameter
comparison 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 |
comparison
equal
deleted
inserted
replaced
1340:daf61e573fee | 1341:b0401251d8c0 |
---|---|
1 /********************************************************************************************************* | |
2 * Software License Agreement (BSD License) * | |
3 * Author: Thomas Klausner <tk@giga.or.at> * | |
4 * * | |
5 * Copyright (c) 2018, Thomas Klausner * | |
6 * All rights reserved. * | |
7 * * | |
8 * Written under contract by Effortel Technologies SA, http://effortel.com/ * | |
9 * * | |
10 * Redistribution and use of this software in source and binary forms, with or without modification, are * | |
11 * permitted provided that the following conditions are met: * | |
12 * * | |
13 * * Redistributions of source code must retain the above * | |
14 * copyright notice, this list of conditions and the * | |
15 * following disclaimer. * | |
16 * * | |
17 * * Redistributions in binary form must reproduce the above * | |
18 * copyright notice, this list of conditions and the * | |
19 * following disclaimer in the documentation and/or other * | |
20 * materials provided with the distribution. * | |
21 * * | |
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * | |
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * | |
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * | |
25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * | |
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | |
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * | |
28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * | |
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | |
30 *********************************************************************************************************/ | |
31 | |
32 /* Yacc extension's configuration parser. | |
33 */ | |
34 | |
35 /* For development only : */ | |
36 %debug | |
37 %error-verbose | |
38 | |
39 /* The parser receives the configuration file filename as parameter */ | |
40 %parse-param {char * conffile} | |
41 | |
42 /* Keep track of location */ | |
43 %locations | |
44 %pure-parser | |
45 | |
46 %{ | |
47 #include "rt_rewrite.h" | |
48 #include "rt_rewrite_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */ | |
49 | |
50 /* Forward declaration */ | |
51 int yyparse(char * conffile); | |
52 void rt_rewrite_confrestart(FILE *input_file); | |
53 | |
54 /* copied from libfdproto/dictionary.c because the symbol is not public */ | |
55 static const char * type_base_name[] = { /* must keep in sync with dict_avp_basetype */ | |
56 "Grouped", /* AVP_TYPE_GROUPED */ | |
57 "Octetstring", /* AVP_TYPE_OCTETSTRING */ | |
58 "Integer32", /* AVP_TYPE_INTEGER32 */ | |
59 "Integer64", /* AVP_TYPE_INTEGER64 */ | |
60 "Unsigned32", /* AVP_TYPE_UNSIGNED32 */ | |
61 "Unsigned64", /* AVP_TYPE_UNSIGNED64 */ | |
62 "Float32", /* AVP_TYPE_FLOAT32 */ | |
63 "Float64" /* AVP_TYPE_FLOAT64 */ | |
64 }; | |
65 | |
66 static struct avp_match *avp_match_new(char *name); | |
67 | |
68 static struct avp_match *source_target = NULL, *drop_target = NULL; | |
69 static struct avp_target *dest_target = NULL; | |
70 | |
71 static void print_target(struct avp_target *target, char *prefix) | |
72 { | |
73 char *output = NULL; | |
74 if (asprintf(&output, "%s -> /TOP/%s", prefix, target->name) == -1) { | |
75 fd_log_error("rt_rewrite: print_target: setup: asprintf failed: %s", strerror(errno)); | |
76 return; | |
77 } | |
78 for (target=target->child; target != NULL; target=target->child) { | |
79 char *new_output = NULL; | |
80 if (asprintf(&new_output, "%s/%s", output, target->name) == -1) { | |
81 fd_log_error("rt_rewrite: print_target: asprintf failed: %s", strerror(errno)); | |
82 free(output); | |
83 return; | |
84 } | |
85 free(output); | |
86 output = new_output; | |
87 new_output = NULL; | |
88 } | |
89 fd_log_debug(output); | |
90 free(output); | |
91 return; | |
92 } | |
93 | |
94 static void compare_avp_type(const char *source, const char *dest) | |
95 { | |
96 struct dict_object *model_source, *model_dest; | |
97 struct dict_avp_data dictdata_source, dictdata_dest; | |
98 | |
99 if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, source, &model_source, ENOENT) != 0) { | |
100 fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", source); | |
101 return; | |
102 } | |
103 if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, dest, &model_dest, ENOENT) != 0) { | |
104 fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", dest); | |
105 return; | |
106 } | |
107 fd_dict_getval(model_source, &dictdata_source); | |
108 fd_dict_getval(model_dest, &dictdata_dest); | |
109 if (dictdata_source.avp_basetype != dictdata_dest.avp_basetype) { | |
110 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]); | |
111 } | |
112 return; | |
113 } | |
114 | |
115 static void compare_avp_types(struct avp_match *start) | |
116 { | |
117 struct avp_match *iter; | |
118 for (iter=start; iter != NULL; iter=iter->next) { | |
119 compare_avp_types(iter->children); | |
120 if (iter->target) { | |
121 struct avp_target *final; | |
122 final = iter->target; | |
123 while (final->child) { | |
124 final = final->child; | |
125 } | |
126 compare_avp_type(iter->name, final->name); | |
127 } | |
128 } | |
129 return; | |
130 } | |
131 | |
132 static void dump_config(struct avp_match *start, char *prefix) | |
133 { | |
134 char *new_prefix = NULL; | |
135 struct avp_match *iter; | |
136 for (iter=start; iter != NULL; iter=iter->next) { | |
137 if (asprintf(&new_prefix, "%s/%s", prefix, iter->name) == -1) { | |
138 fd_log_error("rt_rewrite: dump_config: asprintf failed: %s", strerror(errno)); | |
139 return; | |
140 } | |
141 dump_config(iter->children, new_prefix); | |
142 if (iter->target) { | |
143 print_target(iter->target, new_prefix); | |
144 } | |
145 if (iter->drop) { | |
146 fd_log_debug("%s -> DROP", new_prefix); | |
147 } | |
148 free(new_prefix); | |
149 new_prefix = NULL; | |
150 } | |
151 return; | |
152 } | |
153 | |
154 /* Parse the configuration file */ | |
155 int rt_rewrite_conf_handle(char * conffile) | |
156 { | |
157 extern FILE * rt_rewrite_confin; | |
158 int ret; | |
159 char *top; | |
160 | |
161 TRACE_ENTRY("%p", conffile); | |
162 | |
163 TRACE_DEBUG (FULL, "Parsing configuration file: '%s'", conffile); | |
164 | |
165 /* to match other entries */ | |
166 if ((top=strdup("TOP")) == NULL) { | |
167 fd_log_error("strdup error: %s", strerror(errno)); | |
168 return EINVAL; | |
169 } | |
170 if ((avp_match_start=avp_match_new(top)) == NULL) { | |
171 fd_log_error("malloc error: %s", strerror(errno)); | |
172 free(top); | |
173 return EINVAL; | |
174 } | |
175 rt_rewrite_confin = fopen(conffile, "r"); | |
176 if (rt_rewrite_confin == NULL) { | |
177 ret = errno; | |
178 fd_log_debug("Unable to open extension configuration file '%s' for reading: %s", conffile, strerror(ret)); | |
179 TRACE_DEBUG (INFO, "rt_rewrite: error occurred, message logged -- configuration file."); | |
180 avp_match_free(avp_match_start); | |
181 avp_match_start = NULL; | |
182 return ret; | |
183 } | |
184 | |
185 rt_rewrite_confrestart(rt_rewrite_confin); | |
186 ret = yyparse(conffile); | |
187 | |
188 fclose(rt_rewrite_confin); | |
189 | |
190 if (ret != 0) { | |
191 TRACE_DEBUG(INFO, "rt_rewrite: unable to parse the configuration file."); | |
192 avp_match_free(avp_match_start); | |
193 avp_match_start = NULL; | |
194 return EINVAL; | |
195 } | |
196 | |
197 compare_avp_types(avp_match_start); | |
198 dump_config(avp_match_start, ""); | |
199 | |
200 return 0; | |
201 } | |
202 | |
203 static int verify_avp(const char *name) | |
204 { | |
205 struct dict_object *model; | |
206 struct dict_avp_data dictdata; | |
207 | |
208 if (fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, name, &model, ENOENT) != 0) { | |
209 fd_log_error("Unable to find '%s' AVP in the loaded dictionaries", name); | |
210 return -1; | |
211 } | |
212 fd_dict_getval(model, &dictdata); | |
213 if (dictdata.avp_basetype == AVP_TYPE_GROUPED) { | |
214 return 1; | |
215 } | |
216 return 0; | |
217 } | |
218 | |
219 static struct avp_match *avp_match_new(char *name) { | |
220 struct avp_match *ret; | |
221 | |
222 if ((ret=malloc(sizeof(*ret))) == NULL) { | |
223 fd_log_error("malloc error"); | |
224 return NULL; | |
225 } | |
226 ret->name = name; | |
227 ret->next = NULL; | |
228 ret->children = NULL; | |
229 ret->target = NULL; | |
230 ret->drop = 0; | |
231 return ret; | |
232 } | |
233 | |
234 static void avp_target_free(struct avp_target *target) { | |
235 struct avp_target *iter; | |
236 | |
237 for (iter=target; iter != NULL; ) { | |
238 struct avp_target *next; | |
239 free(iter->name); | |
240 next = iter->child; | |
241 free(iter); | |
242 iter = next; | |
243 } | |
244 } | |
245 | |
246 void avp_match_free(struct avp_match *match) { | |
247 struct avp_match *iter; | |
248 | |
249 for (iter=match; iter != NULL; ) { | |
250 struct avp_match *next; | |
251 free(iter->name); | |
252 next = iter->next; | |
253 avp_match_free(iter->children); | |
254 avp_target_free(iter->target); | |
255 free(iter); | |
256 iter = next; | |
257 } | |
258 } | |
259 | |
260 static struct avp_target *target_new(char *name) { | |
261 struct avp_target *ret; | |
262 | |
263 if ((ret=malloc(sizeof(*ret))) == NULL) { | |
264 fd_log_error("malloc error"); | |
265 return NULL; | |
266 } | |
267 ret->name = name; | |
268 ret->child = NULL; | |
269 return ret; | |
270 } | |
271 | |
272 static struct avp_match *add_avp_next_to(char *name, struct avp_match *target) | |
273 { | |
274 struct avp_match *iter, *prev; | |
275 | |
276 if (target == NULL) { | |
277 return avp_match_new(name); | |
278 } | |
279 | |
280 for (prev=iter=target; iter != NULL; iter=iter->next) { | |
281 if (strcmp(iter->name, name) == 0) { | |
282 return iter; | |
283 } | |
284 prev = iter; | |
285 } | |
286 prev->next = avp_match_new(name); | |
287 return prev->next; | |
288 } | |
289 | |
290 static int add(struct avp_match **target, char *name) | |
291 { | |
292 struct avp_match *temp; | |
293 if (verify_avp(name) < 0) { | |
294 return -1; | |
295 } | |
296 temp = add_avp_next_to(name, (*target)->children); | |
297 if ((*target)->children == NULL) { | |
298 (*target)->children = temp; | |
299 } | |
300 *target = temp; | |
301 return 0; | |
302 } | |
303 | |
304 /* build tree for source */ | |
305 static int source_add(char *name) | |
306 { | |
307 if (source_target == NULL) { | |
308 source_target = avp_match_start; | |
309 } | |
310 return add(&source_target, name); | |
311 } | |
312 | |
313 /* build tree for destination */ | |
314 static int dest_add(char *name) | |
315 { | |
316 struct avp_target *temp; | |
317 | |
318 if (verify_avp(name) < 0) { | |
319 return -1; | |
320 } | |
321 if ((temp=target_new(name)) == NULL) { | |
322 dest_target = NULL; | |
323 source_target = NULL; | |
324 return -1; | |
325 } | |
326 if (dest_target == NULL) { | |
327 dest_target = temp; | |
328 source_target->target = dest_target; | |
329 source_target = NULL; | |
330 return 0; | |
331 } | |
332 dest_target->child = temp; | |
333 dest_target = temp; | |
334 return 0; | |
335 } | |
336 | |
337 static void dest_finish(void) | |
338 { | |
339 dest_target = NULL; | |
340 } | |
341 | |
342 /* same as source_add, but for drop */ | |
343 static int drop_add(char *name) | |
344 { | |
345 if (drop_target == NULL) { | |
346 drop_target = avp_match_start; | |
347 } | |
348 return add(&drop_target, name); | |
349 } | |
350 | |
351 /* mark as to-drop */ | |
352 static void drop_finish(void) | |
353 { | |
354 drop_target->drop = 1; | |
355 drop_target = NULL; | |
356 } | |
357 | |
358 /* The Lex parser prototype */ | |
359 int rt_rewrite_conflex(YYSTYPE *lvalp, YYLTYPE *llocp); | |
360 | |
361 /* Function to report the errors */ | |
362 void yyerror (YYLTYPE *ploc, char * conffile, char const *s) | |
363 { | |
364 TRACE_DEBUG(INFO, "rt_rewrite: error in configuration parsing"); | |
365 | |
366 if (ploc->first_line != ploc->last_line) | |
367 fd_log_debug("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); | |
368 else if (ploc->first_column != ploc->last_column) | |
369 fd_log_debug("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); | |
370 else | |
371 fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s); | |
372 } | |
373 | |
374 %} | |
375 | |
376 /* Values returned by lex for token */ | |
377 %union { | |
378 char *string; /* The string is allocated by strdup in lex.*/ | |
379 } | |
380 | |
381 /* In case of error in the lexical analysis */ | |
382 %token LEX_ERROR | |
383 | |
384 /* A (de)quoted string (malloc'd in lex parser; it must be freed after use) */ | |
385 %token <string> QSTRING | |
386 | |
387 /* Tokens */ | |
388 %token MAP | |
389 %token DROP | |
390 | |
391 | |
392 /* -------------------------------------- */ | |
393 %% | |
394 | |
395 /* The grammar definition */ | |
396 rules: /* empty ok */ | |
397 | rules map | |
398 | rules drop | |
399 ; | |
400 | |
401 /* source -> destination mapping */ | |
402 map: MAP '=' source_part '>' dest_part { dest_finish(); } | |
403 ';' | |
404 ; | |
405 | |
406 source_part: source_part ':' QSTRING { if (source_add($3) < 0) { YYERROR; } } | |
407 | QSTRING { if (source_add($1) < 0) { YYERROR; } } | |
408 ; | |
409 | |
410 dest_part: dest_part ':' QSTRING { if (dest_add($3) < 0) { YYERROR; } } | |
411 | QSTRING { if (dest_add($1) < 0) { YYERROR; } } | |
412 ; | |
413 | |
414 /* for dropping an AVP */ | |
415 drop: DROP '=' drop_part { drop_finish(); } | |
416 ';' | |
417 ; | |
418 | |
419 drop_part: drop_part ':' QSTRING { if (drop_add($3) < 0) { YYERROR; } } | |
420 | QSTRING { if (drop_add($1) < 0) { YYERROR; } } | |
421 ; |