1 %{
2 /*
3  * zlexer.lex - lexical analyzer for (DNS) zone files
4  *
5  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved
6  *
7  * See LICENSE for the license.
8  *
9  */
10 /* because flex keeps having sign-unsigned compare problems that are unfixed*/
11 #if defined(__clang__)||(defined(__GNUC__)&&((__GNUC__ >4)||(defined(__GNUC_MINOR__)&&(__GNUC__ ==4)&&(__GNUC_MINOR__ >=2))))
12 #pragma GCC diagnostic ignored "-Wsign-compare"
13 #endif
14 /* ignore fallthrough warnings in the generated parse code case statements */
15 #if defined(__clang__)||(defined(__GNUC__)&&(__GNUC__ >=7))
16 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
17 #endif
18 
19 #include "config.h"
20 
21 #include <ctype.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <strings.h>
25 
26 #include "zonec.h"
27 #include "dname.h"
28 #include "zparser.h"
29 
30 #if 0
31 #define LEXOUT(s)  printf s /* used ONLY when debugging */
32 #else
33 #define LEXOUT(s)
34 #endif
35 
36 enum lexer_state {
37 	EXPECT_OWNER,
38 	PARSING_OWNER,
39 	PARSING_TTL_CLASS_TYPE,
40 	PARSING_RDATA
41 };
42 
43 static int parse_token(int token, char *yytext, enum lexer_state *lexer_state);
44 
45 static YY_BUFFER_STATE include_stack[MAXINCLUDES];
46 static zparser_type zparser_stack[MAXINCLUDES];
47 static int include_stack_ptr = 0;
48 
49 /*
50  * Saves the file specific variables on the include stack.
51  */
52 static void
push_parser_state(FILE * input)53 push_parser_state(FILE *input)
54 {
55 	zparser_stack[include_stack_ptr].filename = parser->filename;
56 	zparser_stack[include_stack_ptr].line = parser->line;
57 	zparser_stack[include_stack_ptr].origin = parser->origin;
58 	include_stack[include_stack_ptr] = YY_CURRENT_BUFFER;
59 	yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
60 	++include_stack_ptr;
61 }
62 
63 /*
64  * Restores the file specific variables from the include stack.
65  */
66 static void
pop_parser_state(void)67 pop_parser_state(void)
68 {
69 	if (parser->filename)
70 		region_recycle(parser->region, (void *)parser->filename,
71 			strlen(parser->filename)+1);
72 
73 	--include_stack_ptr;
74 	parser->filename = zparser_stack[include_stack_ptr].filename;
75 	parser->line = zparser_stack[include_stack_ptr].line;
76 	parser->origin = zparser_stack[include_stack_ptr].origin;
77 	yy_delete_buffer(YY_CURRENT_BUFFER);
78 	yy_switch_to_buffer(include_stack[include_stack_ptr]);
79 }
80 
81 static YY_BUFFER_STATE oldstate;
82 /* Start string scan */
83 void
parser_push_stringbuf(char * str)84 parser_push_stringbuf(char* str)
85 {
86 	oldstate = YY_CURRENT_BUFFER;
87 	yy_switch_to_buffer(yy_scan_string(str));
88 }
89 
90 void
parser_pop_stringbuf(void)91 parser_pop_stringbuf(void)
92 {
93 	yy_delete_buffer(YY_CURRENT_BUFFER);
94 	yy_switch_to_buffer(oldstate);
95 	oldstate = NULL;
96 }
97 
98 	static int paren_open = 0;
99 	static enum lexer_state lexer_state = EXPECT_OWNER;
100 void
parser_flush(void)101 parser_flush(void)
102 {
103 	YY_FLUSH_BUFFER;
104 	paren_open = 0;
105 	lexer_state = EXPECT_OWNER;
106 }
107 
108 #ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
109 #define yy_set_bol(at_bol) \
110 	{ \
111 		if ( ! yy_current_buffer ) \
112 			yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
113 		yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
114 	}
115 #endif
116 
117 %}
118 %option noinput
119 %option nounput
120 %{
121 #ifndef YY_NO_UNPUT
122 #define YY_NO_UNPUT 1
123 #endif
124 #ifndef YY_NO_INPUT
125 #define YY_NO_INPUT 1
126 #endif
127 %}
128 
129 SPACE   [ \t]
130 LETTER  [a-zA-Z]
131 NEWLINE [\n\r]
132 ZONESTR [^ \t\n\r();.\"\$]|\\.|\\\n
133 CHARSTR [^ \t\n\r();.\"]|\\.|\\\n
134 QUOTE   \"
135 DOLLAR  \$
136 COMMENT ;
137 DOT     \.
138 BIT	[^\]\n]|\\.
139 ANY     [^\"\n\\]|\\.
140 
141 %x	incl bitlabel quotedstring
142 
143 %%
144 {SPACE}*{COMMENT}.*	/* ignore */
145 ^{DOLLAR}TTL            { lexer_state = PARSING_RDATA; return DOLLAR_TTL; }
146 ^{DOLLAR}ORIGIN         { lexer_state = PARSING_RDATA; return DOLLAR_ORIGIN; }
147 
148 	/*
149 	 * Handle $INCLUDE directives.  See
150 	 * http://dinosaur.compilertools.net/flex/flex_12.html#SEC12.
151 	 */
152 ^{DOLLAR}INCLUDE        {
153 	BEGIN(incl);
154 	/* ignore case statement fallthrough on incl<EOF> flex rule */
155 }
156 <incl>\n		|
157 <incl><<EOF>>		{
158 	int error_occurred = parser->error_occurred;
159 	BEGIN(INITIAL);
160 	zc_error("missing file name in $INCLUDE directive");
161 	yy_set_bol(1); /* Set beginning of line, so "^" rules match.  */
162 	++parser->line;
163 	parser->error_occurred = error_occurred;
164 }
165 <incl>.+ 		{
166 	char *tmp;
167 	domain_type *origin = parser->origin;
168 	int error_occurred = parser->error_occurred;
169 
170 	BEGIN(INITIAL);
171 	if (include_stack_ptr >= MAXINCLUDES ) {
172 		zc_error("includes nested too deeply, skipped (>%d)",
173 			 MAXINCLUDES);
174 	} else {
175 		FILE *input;
176 
177 		/* Remove trailing comment.  */
178 		tmp = strrchr(yytext, ';');
179 		if (tmp) {
180 			*tmp = '\0';
181 		}
182 		strip_string(yytext);
183 
184 		/* Parse origin for include file.  */
185 		tmp = strrchr(yytext, ' ');
186 		if (!tmp) {
187 			tmp = strrchr(yytext, '\t');
188 		}
189 		if (tmp) {
190 			const dname_type *dname;
191 
192 			/* split the original yytext */
193 			*tmp = '\0';
194 			strip_string(yytext);
195 
196 			dname = dname_parse(parser->region, tmp + 1);
197 			if (!dname) {
198 				zc_error("incorrect include origin '%s'",
199 					 tmp + 1);
200 			} else if (*(tmp + strlen(tmp + 1)) != '.') {
201 				zc_error("$INCLUDE directive requires absolute domain name");
202 			} else {
203 				origin = domain_table_insert(
204 					parser->db->domains, dname);
205 			}
206 		}
207 
208 		if (strlen(yytext) == 0) {
209 			zc_error("missing file name in $INCLUDE directive");
210 		} else if (!(input = fopen(yytext, "r"))) {
211 			zc_error("cannot open include file '%s': %s",
212 				 yytext, strerror(errno));
213 		} else {
214 			/* Initialize parser for include file.  */
215 			char *filename = region_strdup(parser->region, yytext);
216 			push_parser_state(input); /* Destroys yytext.  */
217 			parser->filename = filename;
218 			parser->line = 1;
219 			parser->origin = origin;
220 			lexer_state = EXPECT_OWNER;
221 		}
222 	}
223 
224 	parser->error_occurred = error_occurred;
225 }
226 <INITIAL><<EOF>>	{
227 	yy_set_bol(1); /* Set beginning of line, so "^" rules match.  */
228 	if (include_stack_ptr == 0) {
229 		yyterminate();
230 	} else {
231 		fclose(yyin);
232 		pop_parser_state();
233 	}
234 }
235 ^{DOLLAR}{LETTER}+	{ zc_warning("Unknown directive: %s", yytext); }
236 {DOT}	{
237 	LEXOUT((". "));
238 	return parse_token('.', yytext, &lexer_state);
239 }
240 @	{
241 	LEXOUT(("@ "));
242 	return parse_token('@', yytext, &lexer_state);
243 }
244 \\#	{
245 	LEXOUT(("\\# "));
246 	return parse_token(URR, yytext, &lexer_state);
247 }
248 {NEWLINE}	{
249 	++parser->line;
250 	if (!paren_open) {
251 		lexer_state = EXPECT_OWNER;
252 		LEXOUT(("NL\n"));
253 		return NL;
254 	} else {
255 		LEXOUT(("SP "));
256 		return SP;
257 	}
258 }
259 \(	{
260 	if (paren_open) {
261 		zc_error("nested parentheses");
262 		yyterminate();
263 	}
264 	LEXOUT(("( "));
265 	paren_open = 1;
266 	return SP;
267 }
268 \)	{
269 	if (!paren_open) {
270 		zc_error("closing parentheses without opening parentheses");
271 		yyterminate();
272 	}
273 	LEXOUT((") "));
274 	paren_open = 0;
275 	return SP;
276 }
277 {SPACE}+	{
278 	if (!paren_open && lexer_state == EXPECT_OWNER) {
279 		lexer_state = PARSING_TTL_CLASS_TYPE;
280 		LEXOUT(("PREV "));
281 		return PREV;
282 	}
283 	if (lexer_state == PARSING_OWNER) {
284 		lexer_state = PARSING_TTL_CLASS_TYPE;
285 	}
286 	LEXOUT(("SP "));
287 	return SP;
288 }
289 
290 	/* Bitlabels.  Strip leading and ending brackets.  */
291 \\\[			{ BEGIN(bitlabel); }
292 <bitlabel><<EOF>>	{
293 	zc_error("EOF inside bitlabel");
294 	BEGIN(INITIAL);
295 	yyrestart(yyin); /* this is so that lex does not give an internal err */
296 	yyterminate();
297 }
298 <bitlabel>{BIT}*	{ yymore(); }
299 <bitlabel>\n		{ ++parser->line; yymore(); }
300 <bitlabel>\]		{
301 	BEGIN(INITIAL);
302 	yytext[yyleng - 1] = '\0';
303 	return parse_token(BITLAB, yytext, &lexer_state);
304 }
305 
306 	/* Quoted strings.  Strip leading and ending quotes.  */
307 {QUOTE}			{ BEGIN(quotedstring); LEXOUT(("\" ")); }
308 <quotedstring><<EOF>> 	{
309 	zc_error("EOF inside quoted string");
310 	BEGIN(INITIAL);
311 	yyrestart(yyin); /* this is so that lex does not give an internal err */
312 	yyterminate();
313 }
314 <quotedstring>{ANY}*	{ LEXOUT(("QSTR ")); yymore(); }
315 <quotedstring>\n 	{ ++parser->line; yymore(); }
316 <quotedstring>{QUOTE} {
317 	LEXOUT(("\" "));
318 	BEGIN(INITIAL);
319 	yytext[yyleng - 1] = '\0';
320 	return parse_token(QSTR, yytext, &lexer_state);
321 }
322 
323 {ZONESTR}({CHARSTR})* {
324 	/* Any allowed word.  */
325 	return parse_token(STR, yytext, &lexer_state);
326 }
327 . {
328 	zc_error("unknown character '%c' (\\%03d) seen - is this a zonefile?",
329 		 (int) yytext[0], (int) yytext[0]);
330 }
331 %%
332 
333 /*
334  * Analyze "word" to see if it matches an RR type, possibly by using
335  * the "TYPExxx" notation.  If it matches, the corresponding token is
336  * returned and the TYPE parameter is set to the RR type value.
337  */
338 static int
339 rrtype_to_token(const char *word, uint16_t *type)
340 {
341 	uint16_t t = rrtype_from_string(word);
342 	if (t != 0) {
343 		rrtype_descriptor_type *entry = rrtype_descriptor_by_type(t);
344 		*type = t;
345 		return entry->token;
346 	}
347 
348 	return 0;
349 }
350 
351 
352 /*
353  * Remove \DDD constructs from the input. See RFC 1035, section 5.1.
354  */
355 static size_t
356 zoctet(char *text)
357 {
358 	/*
359 	 * s follows the string, p lags behind and rebuilds the new
360 	 * string
361 	 */
362 	char *s;
363 	char *p;
364 
365 	for (s = p = text; *s; ++s, ++p) {
366 		assert(p <= s);
367 		if (s[0] != '\\') {
368 			/* Ordinary character.  */
369 			*p = *s;
370 		} else if (isdigit((unsigned char)s[1]) && isdigit((unsigned char)s[2]) && isdigit((unsigned char)s[3])) {
371 			/* \DDD escape.  */
372 			int val = (hexdigit_to_int(s[1]) * 100 +
373 				   hexdigit_to_int(s[2]) * 10 +
374 				   hexdigit_to_int(s[3]));
375 			if (0 <= val && val <= 255) {
376 				s += 3;
377 				*p = val;
378 			} else {
379 				zc_warning("text escape \\DDD overflow");
380 				*p = *++s;
381 			}
382 		} else if (s[1] != '\0') {
383 			/* \X where X is any character, keep X.  */
384 			*p = *++s;
385 		} else {
386 			/* Trailing backslash, ignore it.  */
387 			zc_warning("trailing backslash ignored");
388 			--p;
389 		}
390 	}
391 	*p = '\0';
392 	return p - text;
393 }
394 
395 static int
396 parse_token(int token, char *yytext, enum lexer_state *lexer_state)
397 {
398 	size_t len;
399 	char *str;
400 
401 	if (*lexer_state == EXPECT_OWNER) {
402 		*lexer_state = PARSING_OWNER;
403 	} else if (*lexer_state == PARSING_TTL_CLASS_TYPE) {
404 		const char *t;
405 		int token;
406 		uint16_t rrclass;
407 
408 		/* type */
409 		token = rrtype_to_token(yytext, &yylval.type);
410 		if (token != 0) {
411 			*lexer_state = PARSING_RDATA;
412 			LEXOUT(("%d[%s] ", token, yytext));
413 			return token;
414 		}
415 
416 		/* class */
417 		rrclass = rrclass_from_string(yytext);
418 		if (rrclass != 0) {
419 			yylval.klass = rrclass;
420 			LEXOUT(("CLASS "));
421 			return T_RRCLASS;
422 		}
423 
424 		/* ttl */
425 		yylval.ttl = strtottl(yytext, &t);
426 		if (*t == '\0') {
427 			LEXOUT(("TTL "));
428 			return T_TTL;
429 		}
430 	}
431 
432 	str = region_strdup(parser->rr_region, yytext);
433 	len = zoctet(str);
434 
435 	yylval.data.str = str;
436 	yylval.data.len = len;
437 
438 	LEXOUT(("%d[%s] ", token, yytext));
439 	return token;
440 }
441