1 %{ 2 /* 3 * configlexer.lex - lexical analyzer for unbound 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 <ctype.h> 12 #include <string.h> 13 #include <strings.h> 14 #ifdef HAVE_GLOB_H 15 # include <glob.h> 16 #endif 17 18 #include "util/config_file.h" 19 #include "util/configparser.h" 20 void ub_c_error(const char *message); 21 22 #if 0 23 #define LEXOUT(s) printf s /* used ONLY when debugging */ 24 #else 25 #define LEXOUT(s) 26 #endif 27 28 /** avoid warning in about fwrite return value */ 29 #define ECHO ub_c_error_msg("syntax error at text: %s", yytext) 30 31 /** A parser variable, this is a statement in the config file which is 32 * of the form variable: value1 value2 ... nargs is the number of values. */ 33 #define YDVAR(nargs, var) \ 34 num_args=(nargs); \ 35 LEXOUT(("v(%s%d) ", yytext, num_args)); \ 36 if(num_args > 0) { BEGIN(val); } \ 37 return (var); 38 39 struct inc_state { 40 char* filename; 41 int line; 42 YY_BUFFER_STATE buffer; 43 struct inc_state* next; 44 }; 45 static struct inc_state* config_include_stack = NULL; 46 static int inc_depth = 0; 47 static int inc_prev = 0; 48 static int num_args = 0; 49 50 void init_cfg_parse(void) 51 { 52 config_include_stack = NULL; 53 inc_depth = 0; 54 inc_prev = 0; 55 num_args = 0; 56 } 57 58 static void config_start_include(const char* filename) 59 { 60 FILE *input; 61 struct inc_state* s; 62 char* nm; 63 if(inc_depth++ > 100000) { 64 ub_c_error_msg("too many include files"); 65 return; 66 } 67 if(strlen(filename) == 0) { 68 ub_c_error_msg("empty include file name"); 69 return; 70 } 71 s = (struct inc_state*)malloc(sizeof(*s)); 72 if(!s) { 73 ub_c_error_msg("include %s: malloc failure", filename); 74 return; 75 } 76 if(cfg_parser->chroot && strncmp(filename, cfg_parser->chroot, 77 strlen(cfg_parser->chroot)) == 0) { 78 filename += strlen(cfg_parser->chroot); 79 } 80 nm = strdup(filename); 81 if(!nm) { 82 ub_c_error_msg("include %s: strdup failure", filename); 83 free(s); 84 return; 85 } 86 input = fopen(filename, "r"); 87 if(!input) { 88 ub_c_error_msg("cannot open include file '%s': %s", 89 filename, strerror(errno)); 90 free(s); 91 free(nm); 92 return; 93 } 94 LEXOUT(("switch_to_include_file(%s)\n", filename)); 95 s->filename = cfg_parser->filename; 96 s->line = cfg_parser->line; 97 s->buffer = YY_CURRENT_BUFFER; 98 s->next = config_include_stack; 99 config_include_stack = s; 100 cfg_parser->filename = nm; 101 cfg_parser->line = 1; 102 yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE)); 103 } 104 105 static void config_start_include_glob(const char* filename) 106 { 107 108 /* check for wildcards */ 109 #ifdef HAVE_GLOB 110 glob_t g; 111 size_t i; 112 int r, flags; 113 if(!(!strchr(filename, '*') && !strchr(filename, '?') && !strchr(filename, '[') && 114 !strchr(filename, '{') && !strchr(filename, '~'))) { 115 flags = 0 116 #ifdef GLOB_ERR 117 | GLOB_ERR 118 #endif 119 #ifdef GLOB_NOSORT 120 | GLOB_NOSORT 121 #endif 122 #ifdef GLOB_BRACE 123 | GLOB_BRACE 124 #endif 125 #ifdef GLOB_TILDE 126 | GLOB_TILDE 127 #endif 128 ; 129 memset(&g, 0, sizeof(g)); 130 r = glob(filename, flags, NULL, &g); 131 if(r) { 132 /* some error */ 133 globfree(&g); 134 if(r == GLOB_NOMATCH) 135 return; /* no matches for pattern */ 136 config_start_include(filename); /* let original deal with it */ 137 return; 138 } 139 /* process files found, if any */ 140 for(i=0; i<(size_t)g.gl_pathc; i++) { 141 config_start_include(g.gl_pathv[i]); 142 } 143 globfree(&g); 144 return; 145 } 146 #endif /* HAVE_GLOB */ 147 148 config_start_include(filename); 149 } 150 151 static void config_end_include(void) 152 { 153 struct inc_state* s = config_include_stack; 154 --inc_depth; 155 if(!s) return; 156 free(cfg_parser->filename); 157 cfg_parser->filename = s->filename; 158 cfg_parser->line = s->line; 159 yy_delete_buffer(YY_CURRENT_BUFFER); 160 yy_switch_to_buffer(s->buffer); 161 config_include_stack = s->next; 162 free(s); 163 } 164 165 #ifndef yy_set_bol /* compat definition, for flex 2.4.6 */ 166 #define yy_set_bol(at_bol) \ 167 { \ 168 if ( ! yy_current_buffer ) \ 169 yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ 170 yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \ 171 } 172 #endif 173 174 %} 175 %option noinput 176 %option nounput 177 %{ 178 #ifndef YY_NO_UNPUT 179 #define YY_NO_UNPUT 1 180 #endif 181 #ifndef YY_NO_INPUT 182 #define YY_NO_INPUT 1 183 #endif 184 %} 185 186 SPACE [ \t] 187 LETTER [a-zA-Z] 188 UNQUOTEDLETTER [^\'\"\n\r \t\\]|\\. 189 UNQUOTEDLETTER_NOCOLON [^\:\'\"\n\r \t\\]|\\. 190 NEWLINE [\r\n] 191 COMMENT \# 192 COLON \: 193 DQANY [^\"\n\r\\]|\\. 194 SQANY [^\'\n\r\\]|\\. 195 196 %x quotedstring singlequotedstr include include_quoted val 197 198 %% 199 <INITIAL,val>{SPACE}* { 200 LEXOUT(("SP ")); /* ignore */ } 201 <INITIAL,val>{SPACE}*{COMMENT}.* { 202 /* note that flex makes the longest match and '.' is any but not nl */ 203 LEXOUT(("comment(%s) ", yytext)); /* ignore */ } 204 server{COLON} { YDVAR(0, VAR_SERVER) } 205 num-threads{COLON} { YDVAR(1, VAR_NUM_THREADS) } 206 verbosity{COLON} { YDVAR(1, VAR_VERBOSITY) } 207 port{COLON} { YDVAR(1, VAR_PORT) } 208 outgoing-range{COLON} { YDVAR(1, VAR_OUTGOING_RANGE) } 209 outgoing-port-permit{COLON} { YDVAR(1, VAR_OUTGOING_PORT_PERMIT) } 210 outgoing-port-avoid{COLON} { YDVAR(1, VAR_OUTGOING_PORT_AVOID) } 211 outgoing-num-tcp{COLON} { YDVAR(1, VAR_OUTGOING_NUM_TCP) } 212 incoming-num-tcp{COLON} { YDVAR(1, VAR_INCOMING_NUM_TCP) } 213 do-ip4{COLON} { YDVAR(1, VAR_DO_IP4) } 214 do-ip6{COLON} { YDVAR(1, VAR_DO_IP6) } 215 do-udp{COLON} { YDVAR(1, VAR_DO_UDP) } 216 do-tcp{COLON} { YDVAR(1, VAR_DO_TCP) } 217 tcp-upstream{COLON} { YDVAR(1, VAR_TCP_UPSTREAM) } 218 ssl-upstream{COLON} { YDVAR(1, VAR_SSL_UPSTREAM) } 219 ssl-service-key{COLON} { YDVAR(1, VAR_SSL_SERVICE_KEY) } 220 ssl-service-pem{COLON} { YDVAR(1, VAR_SSL_SERVICE_PEM) } 221 ssl-port{COLON} { YDVAR(1, VAR_SSL_PORT) } 222 do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) } 223 interface{COLON} { YDVAR(1, VAR_INTERFACE) } 224 ip-address{COLON} { YDVAR(1, VAR_INTERFACE) } 225 outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) } 226 interface-automatic{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC) } 227 so-rcvbuf{COLON} { YDVAR(1, VAR_SO_RCVBUF) } 228 so-sndbuf{COLON} { YDVAR(1, VAR_SO_SNDBUF) } 229 so-reuseport{COLON} { YDVAR(1, VAR_SO_REUSEPORT) } 230 chroot{COLON} { YDVAR(1, VAR_CHROOT) } 231 username{COLON} { YDVAR(1, VAR_USERNAME) } 232 directory{COLON} { YDVAR(1, VAR_DIRECTORY) } 233 logfile{COLON} { YDVAR(1, VAR_LOGFILE) } 234 pidfile{COLON} { YDVAR(1, VAR_PIDFILE) } 235 root-hints{COLON} { YDVAR(1, VAR_ROOT_HINTS) } 236 edns-buffer-size{COLON} { YDVAR(1, VAR_EDNS_BUFFER_SIZE) } 237 msg-buffer-size{COLON} { YDVAR(1, VAR_MSG_BUFFER_SIZE) } 238 msg-cache-size{COLON} { YDVAR(1, VAR_MSG_CACHE_SIZE) } 239 msg-cache-slabs{COLON} { YDVAR(1, VAR_MSG_CACHE_SLABS) } 240 rrset-cache-size{COLON} { YDVAR(1, VAR_RRSET_CACHE_SIZE) } 241 rrset-cache-slabs{COLON} { YDVAR(1, VAR_RRSET_CACHE_SLABS) } 242 cache-max-ttl{COLON} { YDVAR(1, VAR_CACHE_MAX_TTL) } 243 cache-min-ttl{COLON} { YDVAR(1, VAR_CACHE_MIN_TTL) } 244 infra-host-ttl{COLON} { YDVAR(1, VAR_INFRA_HOST_TTL) } 245 infra-lame-ttl{COLON} { YDVAR(1, VAR_INFRA_LAME_TTL) } 246 infra-cache-slabs{COLON} { YDVAR(1, VAR_INFRA_CACHE_SLABS) } 247 infra-cache-numhosts{COLON} { YDVAR(1, VAR_INFRA_CACHE_NUMHOSTS) } 248 infra-cache-lame-size{COLON} { YDVAR(1, VAR_INFRA_CACHE_LAME_SIZE) } 249 num-queries-per-thread{COLON} { YDVAR(1, VAR_NUM_QUERIES_PER_THREAD) } 250 jostle-timeout{COLON} { YDVAR(1, VAR_JOSTLE_TIMEOUT) } 251 delay-close{COLON} { YDVAR(1, VAR_DELAY_CLOSE) } 252 target-fetch-policy{COLON} { YDVAR(1, VAR_TARGET_FETCH_POLICY) } 253 harden-short-bufsize{COLON} { YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) } 254 harden-large-queries{COLON} { YDVAR(1, VAR_HARDEN_LARGE_QUERIES) } 255 harden-glue{COLON} { YDVAR(1, VAR_HARDEN_GLUE) } 256 harden-dnssec-stripped{COLON} { YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) } 257 harden-below-nxdomain{COLON} { YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) } 258 harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) } 259 use-caps-for-id{COLON} { YDVAR(1, VAR_USE_CAPS_FOR_ID) } 260 unwanted-reply-threshold{COLON} { YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) } 261 private-address{COLON} { YDVAR(1, VAR_PRIVATE_ADDRESS) } 262 private-domain{COLON} { YDVAR(1, VAR_PRIVATE_DOMAIN) } 263 prefetch-key{COLON} { YDVAR(1, VAR_PREFETCH_KEY) } 264 prefetch{COLON} { YDVAR(1, VAR_PREFETCH) } 265 stub-zone{COLON} { YDVAR(0, VAR_STUB_ZONE) } 266 name{COLON} { YDVAR(1, VAR_NAME) } 267 stub-addr{COLON} { YDVAR(1, VAR_STUB_ADDR) } 268 stub-host{COLON} { YDVAR(1, VAR_STUB_HOST) } 269 stub-prime{COLON} { YDVAR(1, VAR_STUB_PRIME) } 270 stub-first{COLON} { YDVAR(1, VAR_STUB_FIRST) } 271 forward-zone{COLON} { YDVAR(0, VAR_FORWARD_ZONE) } 272 forward-addr{COLON} { YDVAR(1, VAR_FORWARD_ADDR) } 273 forward-host{COLON} { YDVAR(1, VAR_FORWARD_HOST) } 274 forward-first{COLON} { YDVAR(1, VAR_FORWARD_FIRST) } 275 do-not-query-address{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) } 276 do-not-query-localhost{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_LOCALHOST) } 277 access-control{COLON} { YDVAR(2, VAR_ACCESS_CONTROL) } 278 hide-identity{COLON} { YDVAR(1, VAR_HIDE_IDENTITY) } 279 hide-version{COLON} { YDVAR(1, VAR_HIDE_VERSION) } 280 identity{COLON} { YDVAR(1, VAR_IDENTITY) } 281 version{COLON} { YDVAR(1, VAR_VERSION) } 282 module-config{COLON} { YDVAR(1, VAR_MODULE_CONF) } 283 dlv-anchor{COLON} { YDVAR(1, VAR_DLV_ANCHOR) } 284 dlv-anchor-file{COLON} { YDVAR(1, VAR_DLV_ANCHOR_FILE) } 285 trust-anchor-file{COLON} { YDVAR(1, VAR_TRUST_ANCHOR_FILE) } 286 auto-trust-anchor-file{COLON} { YDVAR(1, VAR_AUTO_TRUST_ANCHOR_FILE) } 287 trusted-keys-file{COLON} { YDVAR(1, VAR_TRUSTED_KEYS_FILE) } 288 trust-anchor{COLON} { YDVAR(1, VAR_TRUST_ANCHOR) } 289 val-override-date{COLON} { YDVAR(1, VAR_VAL_OVERRIDE_DATE) } 290 val-sig-skew-min{COLON} { YDVAR(1, VAR_VAL_SIG_SKEW_MIN) } 291 val-sig-skew-max{COLON} { YDVAR(1, VAR_VAL_SIG_SKEW_MAX) } 292 val-bogus-ttl{COLON} { YDVAR(1, VAR_BOGUS_TTL) } 293 val-clean-additional{COLON} { YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) } 294 val-permissive-mode{COLON} { YDVAR(1, VAR_VAL_PERMISSIVE_MODE) } 295 ignore-cd-flag{COLON} { YDVAR(1, VAR_IGNORE_CD_FLAG) } 296 val-log-level{COLON} { YDVAR(1, VAR_VAL_LOG_LEVEL) } 297 key-cache-size{COLON} { YDVAR(1, VAR_KEY_CACHE_SIZE) } 298 key-cache-slabs{COLON} { YDVAR(1, VAR_KEY_CACHE_SLABS) } 299 neg-cache-size{COLON} { YDVAR(1, VAR_NEG_CACHE_SIZE) } 300 val-nsec3-keysize-iterations{COLON} { 301 YDVAR(1, VAR_VAL_NSEC3_KEYSIZE_ITERATIONS) } 302 add-holddown{COLON} { YDVAR(1, VAR_ADD_HOLDDOWN) } 303 del-holddown{COLON} { YDVAR(1, VAR_DEL_HOLDDOWN) } 304 keep-missing{COLON} { YDVAR(1, VAR_KEEP_MISSING) } 305 use-syslog{COLON} { YDVAR(1, VAR_USE_SYSLOG) } 306 log-time-ascii{COLON} { YDVAR(1, VAR_LOG_TIME_ASCII) } 307 log-queries{COLON} { YDVAR(1, VAR_LOG_QUERIES) } 308 local-zone{COLON} { YDVAR(2, VAR_LOCAL_ZONE) } 309 local-data{COLON} { YDVAR(1, VAR_LOCAL_DATA) } 310 local-data-ptr{COLON} { YDVAR(1, VAR_LOCAL_DATA_PTR) } 311 statistics-interval{COLON} { YDVAR(1, VAR_STATISTICS_INTERVAL) } 312 statistics-cumulative{COLON} { YDVAR(1, VAR_STATISTICS_CUMULATIVE) } 313 extended-statistics{COLON} { YDVAR(1, VAR_EXTENDED_STATISTICS) } 314 remote-control{COLON} { YDVAR(0, VAR_REMOTE_CONTROL) } 315 control-enable{COLON} { YDVAR(1, VAR_CONTROL_ENABLE) } 316 control-interface{COLON} { YDVAR(1, VAR_CONTROL_INTERFACE) } 317 control-port{COLON} { YDVAR(1, VAR_CONTROL_PORT) } 318 server-key-file{COLON} { YDVAR(1, VAR_SERVER_KEY_FILE) } 319 server-cert-file{COLON} { YDVAR(1, VAR_SERVER_CERT_FILE) } 320 control-key-file{COLON} { YDVAR(1, VAR_CONTROL_KEY_FILE) } 321 control-cert-file{COLON} { YDVAR(1, VAR_CONTROL_CERT_FILE) } 322 python-script{COLON} { YDVAR(1, VAR_PYTHON_SCRIPT) } 323 python{COLON} { YDVAR(0, VAR_PYTHON) } 324 domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) } 325 minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) } 326 rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) } 327 max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) } 328 <INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } 329 330 /* Quoted strings. Strip leading and ending quotes */ 331 <val>\" { BEGIN(quotedstring); LEXOUT(("QS ")); } 332 <quotedstring><<EOF>> { 333 yyerror("EOF inside quoted string"); 334 if(--num_args == 0) { BEGIN(INITIAL); } 335 else { BEGIN(val); } 336 } 337 <quotedstring>{DQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } 338 <quotedstring>{NEWLINE} { yyerror("newline inside quoted string, no end \""); 339 cfg_parser->line++; BEGIN(INITIAL); } 340 <quotedstring>\" { 341 LEXOUT(("QE ")); 342 if(--num_args == 0) { BEGIN(INITIAL); } 343 else { BEGIN(val); } 344 yytext[yyleng - 1] = '\0'; 345 yylval.str = strdup(yytext); 346 if(!yylval.str) 347 yyerror("out of memory"); 348 return STRING_ARG; 349 } 350 351 /* Single Quoted strings. Strip leading and ending quotes */ 352 <val>\' { BEGIN(singlequotedstr); LEXOUT(("SQS ")); } 353 <singlequotedstr><<EOF>> { 354 yyerror("EOF inside quoted string"); 355 if(--num_args == 0) { BEGIN(INITIAL); } 356 else { BEGIN(val); } 357 } 358 <singlequotedstr>{SQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } 359 <singlequotedstr>{NEWLINE} { yyerror("newline inside quoted string, no end '"); 360 cfg_parser->line++; BEGIN(INITIAL); } 361 <singlequotedstr>\' { 362 LEXOUT(("SQE ")); 363 if(--num_args == 0) { BEGIN(INITIAL); } 364 else { BEGIN(val); } 365 yytext[yyleng - 1] = '\0'; 366 yylval.str = strdup(yytext); 367 if(!yylval.str) 368 yyerror("out of memory"); 369 return STRING_ARG; 370 } 371 372 /* include: directive */ 373 <INITIAL,val>include{COLON} { 374 LEXOUT(("v(%s) ", yytext)); inc_prev = YYSTATE; BEGIN(include); } 375 <include><<EOF>> { 376 yyerror("EOF inside include directive"); 377 BEGIN(inc_prev); 378 } 379 <include>{SPACE}* { LEXOUT(("ISP ")); /* ignore */ } 380 <include>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} 381 <include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); } 382 <include>{UNQUOTEDLETTER}* { 383 LEXOUT(("Iunquotedstr(%s) ", yytext)); 384 config_start_include_glob(yytext); 385 BEGIN(inc_prev); 386 } 387 <include_quoted><<EOF>> { 388 yyerror("EOF inside quoted string"); 389 BEGIN(inc_prev); 390 } 391 <include_quoted>{DQANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); } 392 <include_quoted>{NEWLINE} { yyerror("newline before \" in include name"); 393 cfg_parser->line++; BEGIN(inc_prev); } 394 <include_quoted>\" { 395 LEXOUT(("IQE ")); 396 yytext[yyleng - 1] = '\0'; 397 config_start_include_glob(yytext); 398 BEGIN(inc_prev); 399 } 400 <INITIAL,val><<EOF>> { 401 LEXOUT(("LEXEOF ")); 402 yy_set_bol(1); /* Set beginning of line, so "^" rules match. */ 403 if (!config_include_stack) { 404 yyterminate(); 405 } else { 406 fclose(yyin); 407 config_end_include(); 408 } 409 } 410 411 <val>{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext)); 412 if(--num_args == 0) { BEGIN(INITIAL); } 413 yylval.str = strdup(yytext); return STRING_ARG; } 414 415 {UNQUOTEDLETTER_NOCOLON}* { 416 ub_c_error_msg("unknown keyword '%s'", yytext); 417 } 418 419 <*>. { 420 ub_c_error_msg("stray '%s'", yytext); 421 } 422 423 %% 424