xref: /openbsd/usr.sbin/nsd/configlexer.lex (revision 8529ddd3)
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 debug-mode{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;}
203 hide-version{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;}
204 ip4-only{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;}
205 ip6-only{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_IP6_ONLY;}
206 do-ip4{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP4;}
207 do-ip6{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP6;}
208 database{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DATABASE;}
209 identity{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_IDENTITY;}
210 nsid{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_NSID;}
211 logfile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;}
212 server-count{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_COUNT;}
213 tcp-count{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_COUNT;}
214 tcp-query-count{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_QUERY_COUNT;}
215 tcp-timeout{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_TCP_TIMEOUT;}
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 statistics{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_STATISTICS;}
221 chroot{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_CHROOT;}
222 username{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_USERNAME;}
223 zonesdir{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONESDIR;}
224 zonelistfile{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONELISTFILE;}
225 difffile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_DIFFFILE;}
226 xfrdfile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRDFILE;}
227 xfrdir{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRDIR;}
228 xfrd-reload-timeout{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_XFRD_RELOAD_TIMEOUT;}
229 verbosity{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_VERBOSITY;}
230 zone{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;}
231 zonefile{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;}
232 zonestats{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONESTATS;}
233 allow-notify{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;}
234 request-xfr{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;}
235 notify{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;}
236 notify-retry{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;}
237 provide-xfr{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_PROVIDE_XFR;}
238 outgoing-interface{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_OUTGOING_INTERFACE;}
239 allow-axfr-fallback{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_AXFR_FALLBACK;}
240 key{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_KEY;}
241 algorithm{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ALGORITHM;}
242 secret{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_SECRET;}
243 pattern{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_PATTERN;}
244 include-pattern{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_INCLUDEPATTERN;}
245 remote-control{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_REMOTE_CONTROL;}
246 control-enable{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_ENABLE;}
247 control-interface{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_INTERFACE;}
248 control-port{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_PORT;}
249 server-key-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_KEY_FILE;}
250 server-cert-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_CERT_FILE;}
251 control-key-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_KEY_FILE;}
252 control-cert-file{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_CERT_FILE;}
253 AXFR			{ LEXOUT(("v(%s) ", yytext)); return VAR_AXFR;}
254 UDP			{ LEXOUT(("v(%s) ", yytext)); return VAR_UDP;}
255 rrl-size{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SIZE;}
256 rrl-ratelimit{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_RATELIMIT;}
257 rrl-slip{COLON}		{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SLIP;}
258 rrl-ipv4-prefix-length{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV4_PREFIX_LENGTH;}
259 rrl-ipv6-prefix-length{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;}
260 rrl-whitelist-ratelimit{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;}
261 rrl-whitelist{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;}
262 zonefiles-check{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;}
263 zonefiles-write{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;}
264 log-time-ascii{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
265 round-robin{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
266 {NEWLINE}		{ LEXOUT(("NL\n")); cfg_parser->line++;}
267 
268 	/* Quoted strings. Strip leading and ending quotes */
269 \"			{ BEGIN(quotedstring); LEXOUT(("QS ")); }
270 <quotedstring><<EOF>>   {
271         yyerror("EOF inside quoted string");
272         BEGIN(INITIAL);
273 }
274 <quotedstring>{ANY}*    { LEXOUT(("STR(%s) ", yytext)); yymore(); }
275 <quotedstring>\n        { cfg_parser->line++; yymore(); }
276 <quotedstring>\" {
277         LEXOUT(("QE "));
278         BEGIN(INITIAL);
279         yytext[yyleng - 1] = '\0';
280 	yylval.str = region_strdup(cfg_parser->opt->region, yytext);
281         return STRING;
282 }
283 
284 	/* include: directive */
285 include{COLON}		{ LEXOUT(("v(%s) ", yytext)); BEGIN(include); }
286 <include><<EOF>>	{
287         yyerror("EOF inside include directive");
288         BEGIN(INITIAL);
289 }
290 <include>{SPACE}*	{ LEXOUT(("ISP ")); /* ignore */ }
291 <include>{NEWLINE}	{ LEXOUT(("NL\n")); cfg_parser->line++;}
292 <include>\"		{ LEXOUT(("IQS ")); BEGIN(include_quoted); }
293 <include>{UNQUOTEDLETTER}*	{
294 	LEXOUT(("Iunquotedstr(%s) ", yytext));
295 	config_start_include_glob(yytext);
296 	BEGIN(INITIAL);
297 }
298 <include_quoted><<EOF>>	{
299         yyerror("EOF inside quoted string");
300         BEGIN(INITIAL);
301 }
302 <include_quoted>{ANY}*	{ LEXOUT(("ISTR(%s) ", yytext)); yymore(); }
303 <include_quoted>{NEWLINE}	{ cfg_parser->line++; yymore(); }
304 <include_quoted>\"	{
305 	LEXOUT(("IQE "));
306 	yytext[yyleng - 1] = '\0';
307 	config_start_include_glob(yytext);
308 	BEGIN(INITIAL);
309 }
310 <INITIAL><<EOF>>	{
311 	yy_set_bol(1); /* Set beginning of line, so "^" rules match.  */
312 	if (!config_include_stack) {
313 		yyterminate();
314 	} else {
315 		fclose(yyin);
316 		config_end_include();
317 	}
318 }
319 
320 {UNQUOTEDLETTER}*	{ LEXOUT(("unquotedstr(%s) ", yytext));
321 			yylval.str = region_strdup(cfg_parser->opt->region, yytext); return STRING; }
322 
323 %%
324