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