1 %{ 2 /* 3 * configlexer.lex - lexical analyzer for NSD config file 4 * 5 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved 6 * 7 * See LICENSE for the license. 8 * 9 */ 10 11 #include "config.h" 12 13 #include <ctype.h> 14 #include <errno.h> 15 #include <string.h> 16 #include <strings.h> 17 #ifdef HAVE_GLOB_H 18 # include <glob.h> 19 #endif 20 21 #include "options.h" 22 #include "configyyrename.h" 23 #include "configparser.h" 24 void c_error(const char *message); 25 26 #if 0 27 #define LEXOUT(s) printf s /* used ONLY when debugging */ 28 #else 29 #define LEXOUT(s) 30 #endif 31 32 struct inc_state { 33 char* filename; 34 int line; 35 YY_BUFFER_STATE buffer; 36 struct inc_state* next; 37 }; 38 static struct inc_state* config_include_stack = NULL; 39 static int inc_depth = 0; 40 static int inc_prev = 0; 41 static int num_args = 0; 42 43 void init_cfg_parse(void) 44 { 45 config_include_stack = NULL; 46 inc_depth = 0; 47 inc_prev = 0; 48 num_args = 0; 49 } 50 51 static void config_start_include(const char* filename) 52 { 53 FILE *input; 54 struct inc_state* s; 55 char* nm; 56 if(inc_depth++ > 10000000) { 57 c_error_msg("too many include files"); 58 return; 59 } 60 if(strlen(filename) == 0) { 61 c_error_msg("empty include file name"); 62 return; 63 } 64 s = (struct inc_state*)malloc(sizeof(*s)); 65 if(!s) { 66 c_error_msg("include %s: malloc failure", filename); 67 return; 68 } 69 if (cfg_parser->chroot) { 70 int l = strlen(cfg_parser->chroot); /* chroot has trailing slash */ 71 if (strncmp(cfg_parser->chroot, filename, l) != 0) { 72 c_error_msg("include file '%s' is not relative to chroot '%s'", 73 filename, cfg_parser->chroot); 74 return; 75 } 76 filename += l - 1; /* strip chroot without trailing slash */ 77 } 78 nm = strdup(filename); 79 if(!nm) { 80 c_error_msg("include %s: strdup failure", filename); 81 free(s); 82 return; 83 } 84 input = fopen(filename, "r"); 85 if(!input) { 86 c_error_msg("cannot open include file '%s': %s", 87 filename, strerror(errno)); 88 free(s); 89 free(nm); 90 return; 91 } 92 LEXOUT(("switch_to_include_file(%s) ", filename)); 93 s->filename = cfg_parser->filename; 94 s->line = cfg_parser->line; 95 s->buffer = YY_CURRENT_BUFFER; 96 s->next = config_include_stack; 97 config_include_stack = s; 98 99 cfg_parser->filename = nm; 100 cfg_parser->line = 1; 101 yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE)); 102 } 103 104 static void config_start_include_glob(const char* filename) 105 { 106 /* check for wildcards */ 107 #ifdef HAVE_GLOB 108 glob_t g; 109 size_t i; 110 int r, flags; 111 if(!(!strchr(filename, '*') && !strchr(filename, '?') && 112 !strchr(filename, '[') && !strchr(filename, '{') && 113 !strchr(filename, '~'))) { 114 flags = 0 115 #ifdef GLOB_ERR 116 | GLOB_ERR 117 #endif 118 #ifdef GLOB_NOSORT 119 | GLOB_NOSORT 120 #endif 121 #ifdef GLOB_BRACE 122 | GLOB_BRACE 123 #endif 124 #ifdef GLOB_TILDE 125 | GLOB_TILDE 126 #endif 127 ; 128 memset(&g, 0, sizeof(g)); 129 r = glob(filename, flags, NULL, &g); 130 if(r) { 131 /* some error */ 132 globfree(&g); 133 if(r == GLOB_NOMATCH) 134 return; /* no matches for pattern */ 135 config_start_include(filename); /* let original deal with it */ 136 return; 137 } 138 /* process files found, if any */ 139 for(i=0; i<(size_t)g.gl_pathc; i++) { 140 config_start_include(g.gl_pathv[i]); 141 } 142 globfree(&g); 143 return; 144 } 145 #endif /* HAVE_GLOB */ 146 config_start_include(filename); 147 } 148 149 static void config_end_include(void) 150 { 151 struct inc_state* s = config_include_stack; 152 --inc_depth; 153 if(!s) return; 154 free(cfg_parser->filename); 155 cfg_parser->filename = s->filename; 156 cfg_parser->line = s->line; 157 yy_delete_buffer(YY_CURRENT_BUFFER); 158 yy_switch_to_buffer(s->buffer); 159 config_include_stack = s->next; 160 free(s); 161 } 162 163 #ifndef yy_set_bol /* compat definition, for flex 2.4.6 */ 164 #define yy_set_bol(at_bol) \ 165 { \ 166 if ( ! yy_current_buffer ) \ 167 yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ 168 yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \ 169 } 170 #endif 171 172 %} 173 %option noinput 174 %option nounput 175 %{ 176 #ifndef YY_NO_UNPUT 177 #define YY_NO_UNPUT 1 178 #endif 179 #ifndef YY_NO_INPUT 180 #define YY_NO_INPUT 1 181 #endif 182 %} 183 184 SPACE [ \t] 185 LETTER [a-zA-Z] 186 UNQUOTEDLETTER [^\"\n\r \t\\]|\\. 187 NEWLINE [\r\n] 188 COMMENT \# 189 COLON \: 190 ANY [^\"\n\r\\]|\\. 191 192 %x quotedstring include include_quoted 193 194 %% 195 {SPACE}* { LEXOUT(("SP ")); /* ignore */ } 196 {SPACE}*{COMMENT}.* { LEXOUT(("comment(%s) ", yytext)); /* ignore */ } 197 server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;} 198 name{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NAME;} 199 ip-address{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} 200 interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} 201 ip-transparent{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_TRANSPARENT;} 202 ip-freebind{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_FREEBIND;} 203 debug-mode{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;} 204 hide-version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;} 205 ip4-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;} 206 ip6-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP6_ONLY;} 207 do-ip4{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP4;} 208 do-ip6{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP6;} 209 database{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DATABASE;} 210 identity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IDENTITY;} 211 version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERSION;} 212 nsid{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NSID;} 213 logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;} 214 server-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_COUNT;} 215 tcp-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_COUNT;} 216 tcp-query-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_QUERY_COUNT;} 217 tcp-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_TIMEOUT;} 218 tcp-mss{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_MSS;} 219 outgoing-tcp-mss{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_TCP_MSS;} 220 ipv4-edns-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IPV4_EDNS_SIZE;} 221 ipv6-edns-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IPV6_EDNS_SIZE;} 222 pidfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;} 223 port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PORT;} 224 reuseport{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REUSEPORT;} 225 statistics{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_STATISTICS;} 226 chroot{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;} 227 username{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;} 228 zonesdir{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONESDIR;} 229 zonelistfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONELISTFILE;} 230 difffile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DIFFFILE;} 231 xfrdfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRDFILE;} 232 xfrdir{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRDIR;} 233 xfrd-reload-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_RELOAD_TIMEOUT;} 234 verbosity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERBOSITY;} 235 zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;} 236 zonefile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;} 237 zonestats{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONESTATS;} 238 allow-notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;} 239 size-limit-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SIZE_LIMIT_XFR;} 240 request-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;} 241 notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;} 242 notify-retry{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;} 243 provide-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PROVIDE_XFR;} 244 outgoing-interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_INTERFACE;} 245 allow-axfr-fallback{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_AXFR_FALLBACK;} 246 key{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_KEY;} 247 algorithm{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALGORITHM;} 248 secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SECRET;} 249 pattern{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PATTERN;} 250 include-pattern{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_INCLUDEPATTERN;} 251 remote-control{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REMOTE_CONTROL;} 252 control-enable{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_ENABLE;} 253 control-interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_INTERFACE;} 254 control-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_PORT;} 255 server-key-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_KEY_FILE;} 256 server-cert-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_CERT_FILE;} 257 control-key-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_KEY_FILE;} 258 control-cert-file{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_CERT_FILE;} 259 AXFR { LEXOUT(("v(%s) ", yytext)); return VAR_AXFR;} 260 UDP { LEXOUT(("v(%s) ", yytext)); return VAR_UDP;} 261 rrl-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SIZE;} 262 rrl-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_RATELIMIT;} 263 rrl-slip{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SLIP;} 264 rrl-ipv4-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV4_PREFIX_LENGTH;} 265 rrl-ipv6-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;} 266 rrl-whitelist-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;} 267 rrl-whitelist{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;} 268 zonefiles-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;} 269 zonefiles-write{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;} 270 log-time-ascii{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;} 271 round-robin{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;} 272 max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;} 273 min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;} 274 max-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;} 275 min-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;} 276 {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} 277 278 /* Quoted strings. Strip leading and ending quotes */ 279 \" { BEGIN(quotedstring); LEXOUT(("QS ")); } 280 <quotedstring><<EOF>> { 281 yyerror("EOF inside quoted string"); 282 BEGIN(INITIAL); 283 } 284 <quotedstring>{ANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } 285 <quotedstring>\n { cfg_parser->line++; yymore(); } 286 <quotedstring>\" { 287 LEXOUT(("QE ")); 288 BEGIN(INITIAL); 289 yytext[yyleng - 1] = '\0'; 290 yylval.str = region_strdup(cfg_parser->opt->region, yytext); 291 return STRING; 292 } 293 294 /* include: directive */ 295 include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); } 296 <include><<EOF>> { 297 yyerror("EOF inside include directive"); 298 BEGIN(INITIAL); 299 } 300 <include>{SPACE}* { LEXOUT(("ISP ")); /* ignore */ } 301 <include>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} 302 <include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); } 303 <include>{UNQUOTEDLETTER}* { 304 LEXOUT(("Iunquotedstr(%s) ", yytext)); 305 config_start_include_glob(yytext); 306 BEGIN(INITIAL); 307 } 308 <include_quoted><<EOF>> { 309 yyerror("EOF inside quoted string"); 310 BEGIN(INITIAL); 311 } 312 <include_quoted>{ANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); } 313 <include_quoted>{NEWLINE} { cfg_parser->line++; yymore(); } 314 <include_quoted>\" { 315 LEXOUT(("IQE ")); 316 yytext[yyleng - 1] = '\0'; 317 config_start_include_glob(yytext); 318 BEGIN(INITIAL); 319 } 320 <INITIAL><<EOF>> { 321 yy_set_bol(1); /* Set beginning of line, so "^" rules match. */ 322 if (!config_include_stack) { 323 yyterminate(); 324 } else { 325 fclose(yyin); 326 config_end_include(); 327 } 328 } 329 330 {UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext)); 331 yylval.str = region_strdup(cfg_parser->opt->region, yytext); return STRING; } 332 333 %% 334