blob: 14a1280225aaccabad8f72694b55a39b0df74445 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2013, 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/* Yacc extension's configuration parser.
37 * See doc/app_redirect.conf.sample for configuration file format
38 */
39
40/* For development only : */
41%debug
42%error-verbose
43
44/* The parser receives the configuration file filename as parameter */
45%parse-param {char * conffile}
46
47/* Keep track of location */
48%locations
49%pure-parser
50
51%{
52#include "app_redir.h"
53#include "ard_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */
54
55/* Forward declaration */
56int yyparse(char * conffile);
57
58static int rules_added = 0;
59
60/* We initialize statically the config */
61static struct ard_config local_conf = { .default_rct = 86400, .rules = FD_LIST_INITIALIZER(local_conf.rules) };
62struct ard_config * ard_conf = &local_conf;
63
64/* We use these lists in the rules parsing */
65static struct fd_list temp_list_criteria = FD_LIST_INITIALIZER(temp_list_criteria);
66static struct fd_list temp_list_target = FD_LIST_INITIALIZER(temp_list_target);
67
68/* Local variable */
69static struct ard_criteria * c;
70
71/* Dump the configuration */
72static void ard_conf_dump()
73{
74 struct fd_list * li;
75 if (!TRACE_BOOL(FULL))
76 return;
77
78 fd_log_debug("app_redirect: configuration dump:");
79 fd_log_debug(" default_redirect_cache_time : %u sec", ard_conf->default_rct);
80 for (li = ard_conf->rules.next; li != &ard_conf->rules; li = li->next) {
81 ard_rule_dump(li->o);
82 }
83 fd_log_debug("app_redirect: end of configuration dump");
84}
85
86/* Parse the configuration file */
87int ard_conf_handle(char * conffile)
88{
89 extern FILE * ard_confin;
90 int ret;
91
92 TRACE_ENTRY("%p", conffile);
93
94 TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile);
95
96 ard_confin = fopen(conffile, "r");
97 if (ard_confin == NULL) {
98 ret = errno;
99 TRACE_DEBUG(INFO, "Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret));
100 return ret;
101 }
102
103 ret = yyparse(conffile);
104
105 fclose(ard_confin);
106
107 if (ret != 0) {
108 TRACE_DEBUG (INFO, "Unable to parse the configuration file.");
109 return EINVAL;
110 } else {
111 TRACE_DEBUG(FULL, "Added %d Redirect RULES successfully.", rules_added);
112 ard_conf_dump();
113 }
114
115 return 0;
116}
117
118/* The Lex parser prototype */
119int ard_conflex(YYSTYPE *lvalp, YYLTYPE *llocp);
120
121/* Function to report the errors */
122void yyerror (YYLTYPE *ploc, char * conffile, char const *s)
123{
124 TRACE_DEBUG(INFO, "Error in configuration parsing");
125
126 if (ploc->first_line != ploc->last_line) {
127 TRACE_DEBUG (INFO, "%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
128 } else if (ploc->first_column != ploc->last_column) {
129 TRACE_DEBUG (INFO, "%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
130 } else {
131 TRACE_DEBUG (INFO, "%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
132 }
133}
134
135/* Compile a regular expression pattern */
136static int compile_regex( regex_t * preg, char * str )
137{
138 int err;
139
140 /* Compile the regular expression */
141 err = regcomp(preg, str, REG_EXTENDED | REG_NOSUB);
142 if (err != 0) {
143 char * buf;
144 size_t bl;
145
146 /* Error while compiling the regex */
147 TRACE_DEBUG(INFO, "Error while compiling the regular expression '%s':", str);
148
149 /* Get the error message size */
150 bl = regerror(err, preg, NULL, 0);
151
152 /* Alloc the buffer for error message */
153 CHECK_MALLOC( buf = malloc(bl) );
154
155 /* Get the error message content */
156 regerror(err, preg, buf, bl);
157 TRACE_DEBUG(INFO, "\t%s", buf);
158
159 /* Free the buffer, return the error */
160 free(buf);
161 return EINVAL;
162 }
163
164 return 0;
165}
166
167
168%}
169
170/* Values returned by lex for token */
171%union {
172 /* returned by lex */
173 uint32_t u32; /* Store integer values */
174 struct {
175 char * str;
176 int regex; /* true or false */
177 } tstring; /* typed string */
178}
179
180/* In case of error in the lexical analysis */
181%token TOK_LEX_ERROR
182
183/* A string (malloc'd in lex parser; it must be freed after use):*/
184%token <tstring> TOK_TSTRING
185
186/* An integer value */
187%token <u32> TOK_U32VAL
188
189%type <u32> rule_type
190%type <u32> rule_duration
191
192/* Tokens */
193%token TOK_DEFAULT_RCT
194
195%token TOK_TO
196
197%token TOK_DONT_CACHE
198%token TOK_ALL_SESSION
199%token TOK_ALL_REALM
200%token TOK_REALM_AND_APPLICATION
201%token TOK_ALL_APPLICATION
202%token TOK_ALL_HOST
203%token TOK_ALL_USER
204
205%token TOK_FROM_ID
206%token TOK_FROM_REALM
207
208%token TOK_APP
209
210
211
212
213
214/* -------------------------------------- */
215%%
216
217 /* The grammar definition */
218conffile: /* empty grammar is OK */
219 | conffile def_rct
220 | conffile rule
221 ;
222
223 /* Overwrite default cache time value */
224def_rct: TOK_DEFAULT_RCT '=' TOK_U32VAL ';'
225 {
226 ard_conf->default_rct = $3;
227 }
228 ;
229
230 /* a RULE entry */
231rule: rule_type rule_duration ':' criteria_list TOK_TO target_list ';'
232 {
233 struct ard_rule * r;
234 /* Create the new rule with data in file */
235 CHECK_MALLOC_DO( r = malloc(sizeof(struct ard_rule)),
236 {
237 yyerror (&yylloc, conffile, "Error while allocating new memory...");
238 YYERROR;
239 } );
240 memset(r, 0, sizeof(struct ard_rule));
241 fd_list_init(&r->chain, r);
242 r->type = $1;
243 r->rct = $2;
244 fd_list_init(&r->criteria, NULL);
245 fd_list_move_end(&r->criteria, &temp_list_criteria);
246 fd_list_init(&r->targets, NULL);
247 fd_list_move_end(&r->targets, &temp_list_target);
248
249 /* Add the new rule in config */
250 fd_list_insert_before(&ard_conf->rules, &r->chain);
251 rules_added++;
252 }
253 ;
254
255rule_type: TOK_DONT_CACHE
256 {
257 $$ = DONT_CACHE;
258 }
259 | TOK_ALL_SESSION
260 {
261 $$ = ALL_SESSION;
262 }
263 | TOK_ALL_REALM
264 {
265 $$ = ALL_REALM;
266 }
267 | TOK_REALM_AND_APPLICATION
268 {
269 $$ = REALM_AND_APPLICATION;
270 }
271 | TOK_ALL_APPLICATION
272 {
273 $$ = ALL_APPLICATION;
274 }
275 | TOK_ALL_HOST
276 {
277 $$ = ALL_HOST;
278 }
279 | TOK_ALL_USER
280 {
281 $$ = ALL_USER;
282 }
283 ;
284
285rule_duration: /* empty */
286 {
287 $$ = 0;
288 }
289 |
290 TOK_U32VAL
291 {
292 $$ = $1;
293 }
294 ;
295
296criteria_list: /* empty is OK */
297 | criteria_list criteria_item;
298 ;
299
300criteria_item: {
301 /* Create the new criteria */
302 CHECK_MALLOC_DO( c = malloc(sizeof(struct ard_criteria)),
303 {
304 yyerror (&yylloc, conffile, "Error while allocating new memory...");
305 YYERROR;
306 } );
307 memset(c, 0, sizeof(struct ard_criteria));
308 fd_list_init(&c->chain, c);
309 }
310 criteria_item_inside
311 {
312 struct fd_list * li;
313 /* If there is a string, save its length */
314 if (c->s)
315 c->sl = strlen(c->s);
316 /* If the criteria contains a regex, parse it */
317 if (c->is_regex) {
318 CHECK_FCT_DO( compile_regex( &c->preg, c->s ),
319 {
320 yyerror (&yylloc, conffile, "Error parsing a regular expression...");
321 YYERROR;
322 } );
323 }
324
325 /* Now link this new criteria in the list. Order by criteria type to accelerate the search */
326 for (li = temp_list_criteria.next; li != &temp_list_criteria; li = li->next) {
327 struct ard_criteria * nc = li->o;
328 if (nc->type >= c->type)
329 break;
330 }
331 fd_list_insert_before(li, &c->chain);
332 }
333 ;
334
335criteria_item_inside: TOK_FROM_ID '=' TOK_TSTRING
336 {
337 c->type = FROM_ID;
338 c->s = $3.str;
339 c->is_regex = $3.regex;
340 }
341 |
342 TOK_FROM_REALM '=' TOK_TSTRING
343 {
344 c->type = FROM_REALM;
345 c->s = $3.str;
346 c->is_regex = $3.regex;
347 }
348 |
349 TOK_APP '=' TOK_U32VAL
350 {
351 c->type = APP_ID;
352 c->i = $3;
353 }
354 |
355 TOK_TSTRING '=' TOK_U32VAL
356 {
357 struct dict_object * avp = NULL;
358 if ($1.regex) {
359 yyerror(&yylloc, conffile, "Error: the AVP name cannot be specified as regular expression");
360 YYERROR;
361 }
362 CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, $1.str, &avp, ENOENT ),
363 {
364 TRACE_DEBUG(INFO, "Error while searching for AVP '%s'. Did you load the relevant dictionary extensions?", $1.str);
365 yyerror(&yylloc, conffile, "Unable to resolve specified AVP name.");
366 YYERROR;
367 } );
368 CHECK_FCT_DO( fd_dict_getval(avp, &c->avp_info),
369 {
370 TRACE_DEBUG(INFO, "Error while retrieving the description for AVP '%s'", $1.str);
371 yyerror(&yylloc, conffile, "Unable to retrieve specified AVP's data.");
372 YYERROR;
373 } );
374 if (c->avp_info.avp_basetype != AVP_TYPE_UNSIGNED32) {
375 TRACE_DEBUG(INFO, "The AVP '%s' is not of type UNSIGNED32, matching is not supported (yet)", $1.str);
376 yyerror(&yylloc, conffile, "Invalid AVP for this operation.");
377 YYERROR;
378 }
379
380 c->type = AVP_INT;
381 c->i = $3;
382 }
383 |
384 TOK_TSTRING '=' TOK_TSTRING
385 {
386 struct dict_object * avp = NULL;
387 if ($1.regex) {
388 yyerror(&yylloc, conffile, "Error: the AVP name cannot be specified as regular expression");
389 YYERROR;
390 }
391 CHECK_FCT_DO( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, $1.str, &avp, ENOENT ),
392 {
393 TRACE_DEBUG(INFO, "Error while searching for AVP '%s'. Did you load the relevant dictionary extensions?", $1.str);
394 yyerror(&yylloc, conffile, "Unable to resolve specified AVP name.");
395 YYERROR;
396 } );
397 CHECK_FCT_DO( fd_dict_getval(avp, &c->avp_info),
398 {
399 TRACE_DEBUG(INFO, "Error while retrieving the description for AVP '%s'", $1.str);
400 yyerror(&yylloc, conffile, "Unable to retrieve specified AVP's data.");
401 YYERROR;
402 } );
403 if (c->avp_info.avp_basetype != AVP_TYPE_OCTETSTRING) {
404 TRACE_DEBUG(INFO, "The AVP '%s' is not of type OCTETSTRING, matching is not supported (yet)", $1.str);
405 yyerror(&yylloc, conffile, "Invalid AVP for this operation.");
406 YYERROR;
407 }
408
409 c->type = AVP_STR;
410 c->s = $3.str;
411 c->is_regex = $3.regex;
412 }
413 ;
414
415target_list: /* This list cannot be empty */
416 target_item
417 | target_list target_item
418 ;
419
420target_item: TOK_TSTRING
421 {
422 struct ard_target * t;
423
424 if ($1.regex) {
425 yyerror(&yylloc, conffile, "Regular expressions are not allowed in Redirect-Host specification.");
426 YYERROR;
427 }
428
429 /* Check if the format is valid */
430 CHECK_FCT_DO( fd_os_parse_DiameterURI((uint8_t *)$1.str, strlen($1.str), NULL, NULL, NULL, NULL, NULL, NULL),
431 {
432 TRACE_DEBUG(INFO, "Error while parsing DiameterURI '%s'", $1.str);
433 yyerror(&yylloc, conffile, "Specified DiameterURI is invalid.");
434 YYERROR;
435 } );
436
437 /* Ok. we create the new target */
438 CHECK_MALLOC_DO( t = malloc(sizeof(struct ard_target)),
439 {
440 yyerror (&yylloc, conffile, "Error while allocating new memory...");
441 YYERROR;
442 } );
443 memset(t, 0, sizeof(struct ard_target));
444 fd_list_init(&t->chain, t);
445
446 t->s = (os0_t) $1.str;
447 t->l = strlen($1.str);
448
449 fd_list_insert_before(&temp_list_target, &t->chain);
450 }
451 ;