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