1341
|
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 ; |