xref: /openbsd/usr.sbin/nsd/configlexer.lex (revision 55cc5ba3)
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 
25 #if 0
26 #define LEXOUT(s)  printf s /* used ONLY when debugging */
27 #else
28 #define LEXOUT(s)
29 #endif
30 
31 struct inc_state {
32 	char* filename;
33 	int line;
34 	YY_BUFFER_STATE buffer;
35 	struct inc_state* next;
36 };
37 static struct inc_state* config_include_stack = NULL;
38 static int inc_depth = 0;
39 
40 static void config_start_include(const char* filename)
41 {
42 	FILE *input;
43 	struct inc_state* s;
44 	char* nm;
45 	if(inc_depth++ > 10000000) {
46 		yyerror("too many include files");
47 		return;
48 	}
49 	if(strlen(filename) == 0) {
50 		yyerror("empty include file name");
51 		return;
52 	}
53 	s = (struct inc_state*)malloc(sizeof(*s));
54 	if(!s) {
55 		yyerror("include %s: malloc failure", filename);
56 		return;
57 	}
58 	nm = strdup(filename);
59 	if(!nm) {
60 		yyerror("include %s: strdup failure", filename);
61 		free(s);
62 		return;
63 	}
64 	input = fopen(filename, "r");
65 	if(!input) {
66 		yyerror("cannot open include file '%s': %s",
67 			filename, strerror(errno));
68 		free(s);
69 		free(nm);
70 		return;
71 	}
72 	LEXOUT(("switch_to_include_file(%s) ", filename));
73 	s->filename = cfg_parser->filename;
74 	s->line = cfg_parser->line;
75 	s->buffer = YY_CURRENT_BUFFER;
76 	s->next = config_include_stack;
77 	config_include_stack = s;
78 
79 	cfg_parser->filename = nm;
80 	cfg_parser->line = 1;
81 	yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
82 }
83 
84 static void config_start_include_glob(const char* filename)
85 {
86 	/* check for wildcards */
87 #ifdef HAVE_GLOB
88 	glob_t g;
89 	int i, r, flags;
90 #endif /* HAVE_GLOB */
91 	if (cfg_parser->chroot) {
92 		int l = strlen(cfg_parser->chroot); /* chroot has trailing slash */
93 		if (strncmp(cfg_parser->chroot, filename, l) != 0) {
94 			yyerror("include file '%s' is not relative to chroot '%s'",
95 				filename, cfg_parser->chroot);
96 			return;
97 		}
98 		filename += l - 1; /* strip chroot without trailing slash */
99 	}
100 #ifdef HAVE_GLOB
101 	if(!(!strchr(filename, '*') && !strchr(filename, '?') &&
102 		 !strchr(filename, '[') && !strchr(filename, '{') &&
103 		 !strchr(filename, '~'))) {
104 		 flags = 0
105 #ifdef GLOB_ERR
106 		 	 | GLOB_ERR
107 #endif
108 			 /* do not set GLOB_NOSORT so the results are sorted
109 			    and in a predictable order. */
110 #ifdef GLOB_BRACE
111 			 | GLOB_BRACE
112 #endif
113 #ifdef GLOB_TILDE
114 			 | GLOB_TILDE
115 #endif
116 		;
117 		memset(&g, 0, sizeof(g));
118 		r = glob(filename, flags, NULL, &g);
119 		if(r) {
120 			/* some error */
121 			globfree(&g);
122 			if(r == GLOB_NOMATCH)
123 				return; /* no matches for pattern */
124 			config_start_include(filename); /* let original deal with it */
125 			return;
126 		}
127 		/* process files found, if any */
128 		for(i=(int)g.gl_pathc-1; i>=0; i--) {
129 			config_start_include(g.gl_pathv[i]);
130 		}
131 		globfree(&g);
132 		return;
133 	}
134 #endif /* HAVE_GLOB */
135 	config_start_include(filename);
136 }
137 
138 static void config_end_include(void)
139 {
140 	struct inc_state* s = config_include_stack;
141 	--inc_depth;
142 	if(!s) return;
143 	free(cfg_parser->filename);
144 	cfg_parser->filename = s->filename;
145 	cfg_parser->line = s->line;
146 	yy_delete_buffer(YY_CURRENT_BUFFER);
147 	yy_switch_to_buffer(s->buffer);
148 	config_include_stack = s->next;
149 	free(s);
150 }
151 
152 #ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
153 #define yy_set_bol(at_bol) \
154         { \
155 	        if ( ! yy_current_buffer ) \
156 	                yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
157 	        yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
158         }
159 #endif
160 
161 %}
162 %option noinput
163 %option nounput
164 %{
165 #ifndef YY_NO_UNPUT
166 #define YY_NO_UNPUT 1
167 #endif
168 #ifndef YY_NO_INPUT
169 #define YY_NO_INPUT 1
170 #endif
171 %}
172 
173 SPACE   [ \t]
174 LETTER  [a-zA-Z]
175 UNQUOTEDLETTER [^\"\n\r \t\\]|\\.
176 NEWLINE [\r\n]
177 COMMENT \#
178 COLON 	\:
179 ANY     [^\"\n\r\\]|\\.
180 
181 %x	quotedstring include include_quoted
182 
183 %%
184 {SPACE}* 		{ LEXOUT(("SP ")); /* ignore */ }
185 {SPACE}*{COMMENT}.* 	{ LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
186 server{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;}
187 name{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_NAME;}
188 ip-address{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;}
189 interface{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;}
190 ip-transparent{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_IP_TRANSPARENT;}
191 ip-freebind{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_IP_FREEBIND;}
192 send-buffer-size{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SEND_BUFFER_SIZE;}
193 receive-buffer-size{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RECEIVE_BUFFER_SIZE;}
194 debug-mode{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;}
195 use-systemd{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_USE_SYSTEMD;}
196 hide-version{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;}
197 hide-identity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_IDENTITY;}
198 drop-updates{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DROP_UPDATES; }
199 ip4-only{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;}
200 ip6-only{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_IP6_ONLY;}
201 do-ip4{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP4;}
202 do-ip6{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP6;}
203 database{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DATABASE;}
204 identity{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_IDENTITY;}
205 version{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_VERSION;}
206 nsid{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_NSID;}
207 logfile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;}
208 log-only-syslog{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_LOG_ONLY_SYSLOG;}
209 server-count{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_COUNT;}
210 tcp-count{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_COUNT;}
211 tcp-reject-overflow{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_REJECT_OVERFLOW;}
212 tcp-query-count{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_QUERY_COUNT;}
213 tcp-timeout{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_TIMEOUT;}
214 tcp-mss{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_MSS;}
215 outgoing-tcp-mss{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_TCP_MSS;}
216 ipv4-edns-size{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_IPV4_EDNS_SIZE;}
217 ipv6-edns-size{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_IPV6_EDNS_SIZE;}
218 pidfile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_PIDFILE;}
219 port{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_PORT;}
220 reuseport{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_REUSEPORT;}
221 statistics{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_STATISTICS;}
222 chroot{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;}
223 username{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;}
224 zonesdir{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONESDIR;}
225 zonelistfile{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONELISTFILE;}
226 difffile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DIFFFILE;}
227 xfrdfile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRDFILE;}
228 xfrdir{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRDIR;}
229 xfrd-reload-timeout{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_RELOAD_TIMEOUT;}
230 verbosity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_VERBOSITY;}
231 zone{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;}
232 zonefile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;}
233 zonestats{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONESTATS;}
234 allow-notify{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;}
235 size-limit-xfr{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SIZE_LIMIT_XFR;}
236 request-xfr{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;}
237 notify{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;}
238 notify-retry{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;}
239 provide-xfr{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_PROVIDE_XFR;}
240 outgoing-interface{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_INTERFACE;}
241 allow-axfr-fallback{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_AXFR_FALLBACK;}
242 key{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_KEY;}
243 algorithm{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ALGORITHM;}
244 secret{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_SECRET;}
245 pattern{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_PATTERN;}
246 include-pattern{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_INCLUDE_PATTERN;}
247 remote-control{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_REMOTE_CONTROL;}
248 control-enable{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_ENABLE;}
249 control-interface{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_INTERFACE;}
250 control-port{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_PORT;}
251 server-key-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_KEY_FILE;}
252 server-cert-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_CERT_FILE;}
253 control-key-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_KEY_FILE;}
254 control-cert-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_CERT_FILE;}
255 AXFR			{ LEXOUT(("v(%s) ", yytext)); return VAR_AXFR;}
256 UDP			{ LEXOUT(("v(%s) ", yytext)); return VAR_UDP;}
257 rrl-size{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SIZE;}
258 rrl-ratelimit{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_RATELIMIT;}
259 rrl-slip{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SLIP;}
260 rrl-ipv4-prefix-length{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV4_PREFIX_LENGTH;}
261 rrl-ipv6-prefix-length{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;}
262 rrl-whitelist-ratelimit{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;}
263 rrl-whitelist{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;}
264 zonefiles-check{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;}
265 zonefiles-write{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;}
266 dnstap{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP;}
267 dnstap-enable{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_ENABLE;}
268 dnstap-socket-path{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_SOCKET_PATH; }
269 dnstap-send-identity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_SEND_IDENTITY; }
270 dnstap-send-version{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_SEND_VERSION; }
271 dnstap-identity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_IDENTITY; }
272 dnstap-version{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_VERSION; }
273 dnstap-log-auth-query-messages{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES; }
274 dnstap-log-auth-response-messages{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES; }
275 log-time-ascii{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
276 round-robin{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
277 minimal-responses{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MINIMAL_RESPONSES;}
278 confine-to-zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONFINE_TO_ZONE;}
279 refuse-any{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_REFUSE_ANY;}
280 max-refresh-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;}
281 min-refresh-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;}
282 max-retry-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;}
283 min-retry-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;}
284 min-expire-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MIN_EXPIRE_TIME;}
285 multi-master-check{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MULTI_MASTER_CHECK;}
286 tls-service-key{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_KEY;}
287 tls-service-ocsp{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_OCSP;}
288 tls-service-pem{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_PEM;}
289 tls-port{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_TLS_PORT;}
290 {NEWLINE}		{ LEXOUT(("NL\n")); cfg_parser->line++;}
291 
292 servers={UNQUOTEDLETTER}*	{
293 	yyless(yyleng - (yyleng - 8));
294 	LEXOUT(("v(%s) ", yytext));
295 	return VAR_SERVERS;
296 }
297 bindtodevice={UNQUOTEDLETTER}*	{
298 	yyless(yyleng - (yyleng - 13));
299 	LEXOUT(("v(%s) ", yytext));
300 	return VAR_BINDTODEVICE;
301 }
302 setfib={UNQUOTEDLETTER}*	{
303 	yyless(yyleng - (yyleng - 7));
304 	LEXOUT(("v(%s) ", yytext));
305 	return VAR_SETFIB;
306 }
307 
308 cpu-affinity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CPU_AFFINITY; }
309 xfrd-cpu-affinity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_CPU_AFFINITY; }
310 server-[1-9][0-9]*-cpu-affinity{COLON}	{
311 		char *str = yytext;
312 		LEXOUT(("v(%s) ", yytext));
313 		/* Skip server- */
314 		while (*str != '\0' && (*str < '0' || *str > '9')) {
315 			str++;
316 		}
317 		yylval.llng = strtoll(str, NULL, 10);
318 		return VAR_SERVER_CPU_AFFINITY;
319 	}
320 
321 	/* Quoted strings. Strip leading and ending quotes */
322 \"			{ BEGIN(quotedstring); LEXOUT(("QS ")); }
323 <quotedstring><<EOF>>   {
324         yyerror("EOF inside quoted string");
325         BEGIN(INITIAL);
326 }
327 <quotedstring>{ANY}*    { LEXOUT(("STR(%s) ", yytext)); yymore(); }
328 <quotedstring>\n        { cfg_parser->line++; yymore(); }
329 <quotedstring>\" {
330         LEXOUT(("QE "));
331         BEGIN(INITIAL);
332         yytext[yyleng - 1] = '\0';
333 	yylval.str = region_strdup(cfg_parser->opt->region, yytext);
334         return STRING;
335 }
336 
337 	/* include: directive */
338 include{COLON}		{ LEXOUT(("v(%s) ", yytext)); BEGIN(include); }
339 <include><<EOF>>	{
340         yyerror("EOF inside include directive");
341         BEGIN(INITIAL);
342 }
343 <include>{SPACE}*	{ LEXOUT(("ISP ")); /* ignore */ }
344 <include>{NEWLINE}	{ LEXOUT(("NL\n")); cfg_parser->line++;}
345 <include>\"		{ LEXOUT(("IQS ")); BEGIN(include_quoted); }
346 <include>{UNQUOTEDLETTER}*	{
347 	LEXOUT(("Iunquotedstr(%s) ", yytext));
348 	config_start_include_glob(yytext);
349 	BEGIN(INITIAL);
350 }
351 <include_quoted><<EOF>>	{
352         yyerror("EOF inside quoted string");
353         BEGIN(INITIAL);
354 }
355 <include_quoted>{ANY}*	{ LEXOUT(("ISTR(%s) ", yytext)); yymore(); }
356 <include_quoted>{NEWLINE}	{ cfg_parser->line++; yymore(); }
357 <include_quoted>\"	{
358 	LEXOUT(("IQE "));
359 	yytext[yyleng - 1] = '\0';
360 	config_start_include_glob(yytext);
361 	BEGIN(INITIAL);
362 }
363 <INITIAL><<EOF>>	{
364 	yy_set_bol(1); /* Set beginning of line, so "^" rules match.  */
365 	if (!config_include_stack) {
366 		yyterminate();
367 	} else {
368 		fclose(yyin);
369 		config_end_include();
370 	}
371 }
372 
373 {UNQUOTEDLETTER}*	{ LEXOUT(("unquotedstr(%s) ", yytext));
374 			yylval.str = region_strdup(cfg_parser->opt->region, yytext); return STRING; }
375 
376 %%
377