Brian Waters | 13d9601 | 2017-12-08 16:53:31 -0600 | [diff] [blame] | 1 | /********************************************************************************************************* |
| 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 | /* Lex configuration parser. |
| 37 | * |
| 38 | * This file defines the token for parsing the daemon's configuration file |
| 39 | * Note that each extension has a separate independant configuration file. |
| 40 | * |
| 41 | * Note : This module is NOT thread-safe. All processing must be done from one thread only. |
| 42 | */ |
| 43 | %{ |
| 44 | /* Include the daemon's header files */ |
| 45 | #include "fdcore-internal.h" |
| 46 | /* Include yacc tokens definitions */ |
| 47 | #include "fdd.tab.h" |
| 48 | |
| 49 | /* Update the column information */ |
| 50 | #ifdef DEBUG_LEX |
| 51 | #define YY_USER_ACTION { \ |
| 52 | yylloc->first_column = yylloc->last_column + 1; \ |
| 53 | yylloc->last_column = yylloc->first_column + yyleng - 1; \ |
| 54 | fd_log_debug( \ |
| 55 | "(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'", \ |
| 56 | yylloc->first_line, yylloc->first_column, \ |
| 57 | yylloc->last_line, yylloc->last_column, \ |
| 58 | yy_act, yyleng, yytext); \ |
| 59 | } |
| 60 | #else /* DEBUG_LEX */ |
| 61 | #define YY_USER_ACTION { \ |
| 62 | yylloc->first_column = yylloc->last_column + 1; \ |
| 63 | yylloc->last_column = yylloc->first_column + yyleng - 1; \ |
| 64 | } |
| 65 | #endif |
| 66 | |
| 67 | /* %option noinput ? */ |
| 68 | #define YY_NO_INPUT |
| 69 | |
| 70 | /* Additional for files inclusion */ |
| 71 | #include <glob.h> |
| 72 | #include <string.h> |
| 73 | |
| 74 | #define MAX_NESTED_CONF_FILES 5 |
| 75 | |
| 76 | struct nested_conffiles_t { |
| 77 | YY_BUFFER_STATE parent_level_state; |
| 78 | glob_t filelist; |
| 79 | int current_file; |
| 80 | } nested_conffiles[MAX_NESTED_CONF_FILES]; |
| 81 | |
| 82 | int current_nested_level = 0; |
| 83 | |
| 84 | int globerrfct(const char *epath, int eerrno) |
| 85 | { |
| 86 | TRACE_ERROR("Failed to scan %s: %s", epath, strerror(eerrno)); |
| 87 | return 1; |
| 88 | } |
| 89 | |
| 90 | %} |
| 91 | |
| 92 | %option bison-bridge bison-locations |
| 93 | %option noyywrap |
| 94 | %option nounput |
| 95 | |
| 96 | %x in_include |
| 97 | |
| 98 | /* Quoted string. Multilines do not match. */ |
| 99 | qstring \"[^\"\n]*\" |
| 100 | |
| 101 | %% |
| 102 | <*>\n { |
| 103 | /* Update the line count */ |
| 104 | yylloc->first_line++; |
| 105 | yylloc->last_line++; |
| 106 | yylloc->last_column=0; |
| 107 | } |
| 108 | |
| 109 | <*>([[:space:]]{-}[\n])+ ; /* Eat all spaces, not new lines */ |
| 110 | <*>#.*$ ; /* Eat all comments */ |
| 111 | |
| 112 | |
| 113 | include BEGIN(in_include); |
| 114 | /* Following an "include" keyword */ |
| 115 | <in_include>{ |
| 116 | {qstring} { /* Name of the file to include. This is directly sent to glob. */ |
| 117 | int globerror=0; |
| 118 | char * buf = strdup(yytext+1); |
| 119 | if (buf[yyleng-2] != '"') |
| 120 | { |
| 121 | TRACE_ERROR("Unterminated string: %s", yytext); |
| 122 | return LEX_ERROR; |
| 123 | } |
| 124 | buf[yyleng-2] = '\0'; |
| 125 | |
| 126 | if (current_nested_level >= MAX_NESTED_CONF_FILES) |
| 127 | { |
| 128 | TRACE_ERROR("Too many recursion levels in configuration files includes"); |
| 129 | return LEX_ERROR; |
| 130 | } |
| 131 | |
| 132 | /* glob the include */ |
| 133 | globerror = glob(buf, GLOB_ERR, globerrfct, &nested_conffiles[current_nested_level].filelist); |
| 134 | |
| 135 | if (globerror == GLOB_NOSPACE) |
| 136 | { |
| 137 | TRACE_ERROR("Not enough memory to parse include directive."); |
| 138 | return LEX_ERROR; |
| 139 | } |
| 140 | if (globerror == GLOB_ABORTED) |
| 141 | { |
| 142 | TRACE_ERROR("An error was encountered in include directive."); |
| 143 | return LEX_ERROR; |
| 144 | } |
| 145 | if (globerror == GLOB_NOMATCH) |
| 146 | { |
| 147 | globfree(&nested_conffiles[current_nested_level].filelist); |
| 148 | goto nomatch; |
| 149 | } |
| 150 | if (globerror) |
| 151 | { |
| 152 | TRACE_ERROR("Unexpected error in glob (%d).", globerror); |
| 153 | return LEX_ERROR; |
| 154 | } |
| 155 | |
| 156 | /* We have a list of files to include. */ |
| 157 | |
| 158 | /* save the current buffer for returning when this include has been parsed */ |
| 159 | nested_conffiles[current_nested_level].parent_level_state = YY_CURRENT_BUFFER; |
| 160 | |
| 161 | /* Start with the first match */ |
| 162 | nested_conffiles[current_nested_level].current_file = 0; |
| 163 | |
| 164 | yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[0], "r" ); |
| 165 | |
| 166 | if ( ! yyin ) |
| 167 | { |
| 168 | TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[0], strerror(errno)); |
| 169 | return LEX_ERROR; |
| 170 | } |
| 171 | |
| 172 | yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE )); |
| 173 | |
| 174 | /* In case of recursive includes */ |
| 175 | current_nested_level++; |
| 176 | |
| 177 | nomatch: |
| 178 | BEGIN(INITIAL); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | <<EOF>> { |
| 183 | if (current_nested_level == 0) |
| 184 | { |
| 185 | /* We are at the end of parsing */ |
| 186 | yyterminate(); |
| 187 | } |
| 188 | |
| 189 | /* Otherwise we are doing an include statement */ |
| 190 | --current_nested_level; |
| 191 | yy_delete_buffer(YY_CURRENT_BUFFER); |
| 192 | |
| 193 | /* Go to next file, if any */ |
| 194 | nested_conffiles[current_nested_level].current_file++; |
| 195 | if ( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file] == NULL ) |
| 196 | { |
| 197 | /* We have finished with this list of includes */ |
| 198 | globfree(&nested_conffiles[current_nested_level].filelist); |
| 199 | yy_switch_to_buffer(nested_conffiles[current_nested_level].parent_level_state); |
| 200 | } |
| 201 | else |
| 202 | { |
| 203 | /* Proceed to next included file */ |
| 204 | yyin = fopen( nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], "r" ); |
| 205 | |
| 206 | if ( ! yyin ) |
| 207 | { |
| 208 | TRACE_ERROR("Error in %s: %s", nested_conffiles[current_nested_level].filelist.gl_pathv[nested_conffiles[current_nested_level].current_file], strerror(errno)); |
| 209 | return LEX_ERROR; |
| 210 | } |
| 211 | |
| 212 | yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE )); |
| 213 | |
| 214 | /* In case of recursive includes */ |
| 215 | current_nested_level++; |
| 216 | } |
| 217 | |
| 218 | } |
| 219 | |
| 220 | {qstring} { |
| 221 | /* First copy the string without the quotes for use in the yacc parser */ |
| 222 | CHECK_MALLOC_DO( yylval->string = strdup(yytext+1), /* This allocates one useless tail char but... it's easier :D */ |
| 223 | return LEX_ERROR );/* on error, trig an error in yacc parser */ |
| 224 | |
| 225 | yylval->string[yyleng-2] = '\0'; |
| 226 | |
| 227 | /* the yacc parser will check the string is valid */ |
| 228 | return QSTRING; |
| 229 | } |
| 230 | |
| 231 | [[:digit:]]+ { |
| 232 | /* Convert this to an integer value */ |
| 233 | int ret = sscanf(yytext, "%i", &yylval->integer); |
| 234 | if (ret != 1) { |
| 235 | /* No matching: an error occurred */ |
| 236 | TRACE_ERROR("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno)); |
| 237 | return LEX_ERROR; /* trig an error in yacc parser */ |
| 238 | /* Maybe we could REJECT instead of failing here? */ |
| 239 | } |
| 240 | return INTEGER; |
| 241 | } |
| 242 | |
| 243 | /* Full words tokens (keywords) */ |
| 244 | (?i:"Identity") { return IDENTITY; } |
| 245 | (?i:"Realm") { return REALM; } |
| 246 | (?i:"Port") { return PORT; } |
| 247 | (?i:"SecPort") { return SECPORT; } |
| 248 | /* (?i:"SctpSec3436") { return SEC3436; } */ |
| 249 | (?i:"No_IPv6") { return NOIP6; } |
| 250 | (?i:"No_IP") { return NOIP; } |
| 251 | (?i:"No_TCP") { return NOTCP; } |
| 252 | (?i:"No_SCTP") { return NOSCTP; } |
| 253 | (?i:"Prefer_TCP") { return PREFERTCP; } |
| 254 | (?i:"TLS_old_method") { return OLDTLS; } |
| 255 | (?i:"SCTP_streams") { return SCTPSTREAMS; } |
| 256 | (?i:"AppServThreads") { return APPSERVTHREADS;} |
| 257 | (?i:"ListenOn") { return LISTENON; } |
| 258 | (?i:"ThreadsPerServer") { return THRPERSRV; } |
| 259 | (?i:"TcTimer") { return TCTIMER; } |
| 260 | (?i:"TwTimer") { return TWTIMER; } |
| 261 | (?i:"NoRelay") { return NORELAY; } |
| 262 | (?i:"LoadExtension") { return LOADEXT; } |
| 263 | (?i:"ConnectPeer") { return CONNPEER; } |
| 264 | (?i:"ConnectTo") { return CONNTO; } |
| 265 | (?i:"No_TLS") { return NOTLS; } |
| 266 | (?i:"TLS_Cred") { return TLS_CRED; } |
| 267 | (?i:"TLS_CA") { return TLS_CA; } |
| 268 | (?i:"TLS_CRL") { return TLS_CRL; } |
| 269 | (?i:"TLS_Prio") { return TLS_PRIO; } |
| 270 | (?i:"TLS_DH_bits") { return TLS_DH_BITS; } |
| 271 | (?i:"TLS_DH_file") { return TLS_DH_FILE; } |
| 272 | |
| 273 | |
| 274 | /* Valid single characters for yyparse */ |
| 275 | <*>[=,:;{}] { return yytext[0]; } |
| 276 | |
| 277 | /* Unrecognized token */ |
| 278 | <*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */ |
| 279 | /* Unrecognized character */ |
| 280 | <*>. { |
| 281 | TRACE_ERROR("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext); |
| 282 | return LEX_ERROR; |
| 283 | } |
| 284 | |
| 285 | %% |