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