xref: /openbsd/usr.sbin/ntpd/parse.y (revision a6445c1d)
1 /*	$OpenBSD: parse.y,v 1.55 2014/11/20 05:51:20 jsg Exp $ */
2 
3 /*
4  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
6  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
7  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 %{
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 
28 #include <ctype.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <syslog.h>
36 
37 #include "ntpd.h"
38 
39 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
40 static struct file {
41 	TAILQ_ENTRY(file)	 entry;
42 	FILE			*stream;
43 	char			*name;
44 	int			 lineno;
45 	int			 errors;
46 } *file, *topfile;
47 struct file	*pushfile(const char *);
48 int		 popfile(void);
49 int		 yyparse(void);
50 int		 yylex(void);
51 int		 yyerror(const char *, ...)
52     __attribute__((__format__ (printf, 1, 2)))
53     __attribute__((__nonnull__ (1)));
54 int		 kw_cmp(const void *, const void *);
55 int		 lookup(char *);
56 int		 lgetc(int);
57 int		 lungetc(int);
58 int		 findeol(void);
59 
60 struct ntpd_conf		*conf;
61 
62 struct opts {
63 	int		weight;
64 	int		correction;
65 	int		stratum;
66 	int		rtable;
67 	char		*refstr;
68 } opts;
69 void		opts_default(void);
70 
71 typedef struct {
72 	union {
73 		int64_t			 number;
74 		char			*string;
75 		struct ntp_addr_wrap	*addr;
76 		struct opts		 opts;
77 	} v;
78 	int lineno;
79 } YYSTYPE;
80 
81 %}
82 
83 %token	LISTEN ON
84 %token	SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT
85 %token	ERROR
86 %token	<v.string>		STRING
87 %token	<v.number>		NUMBER
88 %type	<v.addr>		address
89 %type	<v.opts>		listen_opts listen_opts_l listen_opt
90 %type	<v.opts>		server_opts server_opts_l server_opt
91 %type	<v.opts>		sensor_opts sensor_opts_l sensor_opt
92 %type	<v.opts>		correction
93 %type	<v.opts>		rtable
94 %type	<v.opts>		refid
95 %type	<v.opts>		stratum
96 %type	<v.opts>		weight
97 %%
98 
99 grammar		: /* empty */
100 		| grammar '\n'
101 		| grammar main '\n'
102 		| grammar error '\n'		{ file->errors++; }
103 		;
104 
105 main		: LISTEN ON address listen_opts	{
106 			struct listen_addr	*la;
107 			struct ntp_addr		*h, *next;
108 
109 			if ($3->a)
110 				$3->a->rtable = $4.rtable;
111 			if ((h = $3->a) == NULL &&
112 			    (host_dns($3->name, &h) == -1 || !h)) {
113 				yyerror("could not resolve \"%s\"", $3->name);
114 				free($3->name);
115 				free($3);
116 				YYERROR;
117 			}
118 
119 			for (; h != NULL; h = next) {
120 				next = h->next;
121 				la = calloc(1, sizeof(struct listen_addr));
122 				if (la == NULL)
123 					fatal("listen on calloc");
124 				la->fd = -1;
125 				la->rtable = $4.rtable;
126 				memcpy(&la->sa, &h->ss,
127 				    sizeof(struct sockaddr_storage));
128 				TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
129 				    entry);
130 				free(h);
131 			}
132 			free($3->name);
133 			free($3);
134 		}
135 		| SERVERS address server_opts	{
136 			struct ntp_peer		*p;
137 			struct ntp_addr		*h, *next;
138 
139 			h = $2->a;
140 			do {
141 				if (h != NULL) {
142 					next = h->next;
143 					if (h->ss.ss_family != AF_INET &&
144 					    h->ss.ss_family != AF_INET6) {
145 						yyerror("IPv4 or IPv6 address "
146 						    "or hostname expected");
147 						free(h);
148 						free($2->name);
149 						free($2);
150 						YYERROR;
151 					}
152 					h->next = NULL;
153 				} else
154 					next = NULL;
155 
156 				p = new_peer();
157 				p->weight = $3.weight;
158 				p->rtable = $3.rtable;
159 				p->addr = h;
160 				p->addr_head.a = h;
161 				p->addr_head.pool = 1;
162 				p->addr_head.name = strdup($2->name);
163 				if (p->addr_head.name == NULL)
164 					fatal(NULL);
165 				if (p->addr != NULL)
166 					p->state = STATE_DNS_DONE;
167 				if (!(p->rtable > 0 && p->addr))
168 					TAILQ_INSERT_TAIL(&conf->ntp_peers,
169 					    p, entry);
170 				h = next;
171 			} while (h != NULL);
172 
173 			free($2->name);
174 			free($2);
175 		}
176 		| SERVER address server_opts {
177 			struct ntp_peer		*p;
178 			struct ntp_addr		*h, *next;
179 
180 			p = new_peer();
181 			for (h = $2->a; h != NULL; h = next) {
182 				next = h->next;
183 				if (h->ss.ss_family != AF_INET &&
184 				    h->ss.ss_family != AF_INET6) {
185 					yyerror("IPv4 or IPv6 address "
186 					    "or hostname expected");
187 					free(h);
188 					free(p);
189 					free($2->name);
190 					free($2);
191 					YYERROR;
192 				}
193 				h->next = p->addr;
194 				p->addr = h;
195 			}
196 
197 			p->weight = $3.weight;
198 			p->rtable = $3.rtable;
199 			p->addr_head.a = p->addr;
200 			p->addr_head.pool = 0;
201 			p->addr_head.name = strdup($2->name);
202 			if (p->addr_head.name == NULL)
203 				fatal(NULL);
204 			if (p->addr != NULL)
205 				p->state = STATE_DNS_DONE;
206 			if (!(p->rtable > 0 && p->addr))
207 				TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
208 			free($2->name);
209 			free($2);
210 		}
211 		| SENSOR STRING	sensor_opts {
212 			struct ntp_conf_sensor	*s;
213 
214 			s = new_sensor($2);
215 			s->weight = $3.weight;
216 			s->correction = $3.correction;
217 			s->refstr = $3.refstr;
218 			s->stratum = $3.stratum;
219 			free($2);
220 			TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry);
221 		}
222 		;
223 
224 address		: STRING		{
225 			if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
226 			    NULL)
227 				fatal(NULL);
228 			if (host($1, &$$->a) == -1) {
229 				yyerror("could not parse address spec \"%s\"",
230 				    $1);
231 				free($1);
232 				free($$);
233 				YYERROR;
234 			}
235 			$$->name = $1;
236 		}
237 		;
238 
239 listen_opts	:	{ opts_default(); }
240 		  listen_opts_l
241 			{ $$ = opts; }
242 		|	{ opts_default(); $$ = opts; }
243 		;
244 listen_opts_l	: listen_opts_l listen_opt
245 		| listen_opt
246 		;
247 listen_opt	: rtable
248 		;
249 
250 server_opts	:	{ opts_default(); }
251 		  server_opts_l
252 			{ $$ = opts; }
253 		|	{ opts_default(); $$ = opts; }
254 		;
255 server_opts_l	: server_opts_l server_opt
256 		| server_opt
257 		;
258 server_opt	: weight
259 		| rtable
260 		;
261 
262 sensor_opts	:	{ opts_default(); }
263 		  sensor_opts_l
264 			{ $$ = opts; }
265 		|	{ opts_default(); $$ = opts; }
266 		;
267 sensor_opts_l	: sensor_opts_l sensor_opt
268 		| sensor_opt
269 		;
270 sensor_opt	: correction
271 		| refid
272 		| stratum
273 		| weight
274 		;
275 
276 correction	: CORRECTION NUMBER {
277 			if ($2 < -127000000 || $2 > 127000000) {
278 				yyerror("correction must be between "
279 				    "-127000000 and 127000000 microseconds");
280 				YYERROR;
281 			}
282 			opts.correction = $2;
283 		}
284 		;
285 
286 refid		: REFID STRING {
287 			size_t l = strlen($2);
288 
289 			if (l < 1 || l > 4) {
290 				yyerror("refid must be 1 to 4 characters");
291 				free($2);
292 				YYERROR;
293 			}
294 			opts.refstr = $2;
295 		}
296 		;
297 
298 stratum		: STRATUM NUMBER {
299 			if ($2 < 1 || $2 > 15) {
300 				yyerror("stratum must be between "
301 				    "1 and 15");
302 				YYERROR;
303 			}
304 			opts.stratum = $2;
305 		}
306 		;
307 
308 weight		: WEIGHT NUMBER	{
309 			if ($2 < 1 || $2 > 10) {
310 				yyerror("weight must be between 1 and 10");
311 				YYERROR;
312 			}
313 			opts.weight = $2;
314 		}
315 rtable		: RTABLE NUMBER {
316 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
317 				yyerror("rtable must be between 1 and RT_TABLEID_MAX");
318 				YYERROR;
319 			}
320 			opts.rtable = $2;
321 		}
322 		;
323 
324 %%
325 
326 void
327 opts_default(void)
328 {
329 	bzero(&opts, sizeof opts);
330 	opts.weight = 1;
331 	opts.rtable = -1;
332 	opts.stratum = 1;
333 }
334 
335 struct keywords {
336 	const char	*k_name;
337 	int		 k_val;
338 };
339 
340 int
341 yyerror(const char *fmt, ...)
342 {
343 	va_list		 ap;
344 	char		*msg;
345 
346 	file->errors++;
347 	va_start(ap, fmt);
348 	if (vasprintf(&msg, fmt, ap) == -1)
349 		fatalx("yyerror vasprintf");
350 	va_end(ap);
351 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
352 	free(msg);
353 	return (0);
354 }
355 
356 int
357 kw_cmp(const void *k, const void *e)
358 {
359 	return (strcmp(k, ((const struct keywords *)e)->k_name));
360 }
361 
362 int
363 lookup(char *s)
364 {
365 	/* this has to be sorted always */
366 	static const struct keywords keywords[] = {
367 		{ "correction",		CORRECTION},
368 		{ "listen",		LISTEN},
369 		{ "on",			ON},
370 		{ "refid",		REFID},
371 		{ "rtable",		RTABLE},
372 		{ "sensor",		SENSOR},
373 		{ "server",		SERVER},
374 		{ "servers",		SERVERS},
375 		{ "stratum",		STRATUM},
376 		{ "weight",		WEIGHT}
377 	};
378 	const struct keywords	*p;
379 
380 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
381 	    sizeof(keywords[0]), kw_cmp);
382 
383 	if (p)
384 		return (p->k_val);
385 	else
386 		return (STRING);
387 }
388 
389 #define MAXPUSHBACK	128
390 
391 u_char	*parsebuf;
392 int	 parseindex;
393 u_char	 pushback_buffer[MAXPUSHBACK];
394 int	 pushback_index = 0;
395 
396 int
397 lgetc(int quotec)
398 {
399 	int		c, next;
400 
401 	if (parsebuf) {
402 		/* Read character from the parsebuffer instead of input. */
403 		if (parseindex >= 0) {
404 			c = parsebuf[parseindex++];
405 			if (c != '\0')
406 				return (c);
407 			parsebuf = NULL;
408 		} else
409 			parseindex++;
410 	}
411 
412 	if (pushback_index)
413 		return (pushback_buffer[--pushback_index]);
414 
415 	if (quotec) {
416 		if ((c = getc(file->stream)) == EOF) {
417 			yyerror("reached end of file while parsing "
418 			    "quoted string");
419 			if (file == topfile || popfile() == EOF)
420 				return (EOF);
421 			return (quotec);
422 		}
423 		return (c);
424 	}
425 
426 	while ((c = getc(file->stream)) == '\\') {
427 		next = getc(file->stream);
428 		if (next != '\n') {
429 			c = next;
430 			break;
431 		}
432 		yylval.lineno = file->lineno;
433 		file->lineno++;
434 	}
435 
436 	while (c == EOF) {
437 		if (file == topfile || popfile() == EOF)
438 			return (EOF);
439 		c = getc(file->stream);
440 	}
441 	return (c);
442 }
443 
444 int
445 lungetc(int c)
446 {
447 	if (c == EOF)
448 		return (EOF);
449 	if (parsebuf) {
450 		parseindex--;
451 		if (parseindex >= 0)
452 			return (c);
453 	}
454 	if (pushback_index < MAXPUSHBACK-1)
455 		return (pushback_buffer[pushback_index++] = c);
456 	else
457 		return (EOF);
458 }
459 
460 int
461 findeol(void)
462 {
463 	int	c;
464 
465 	parsebuf = NULL;
466 
467 	/* skip to either EOF or the first real EOL */
468 	while (1) {
469 		if (pushback_index)
470 			c = pushback_buffer[--pushback_index];
471 		else
472 			c = lgetc(0);
473 		if (c == '\n') {
474 			file->lineno++;
475 			break;
476 		}
477 		if (c == EOF)
478 			break;
479 	}
480 	return (ERROR);
481 }
482 
483 int
484 yylex(void)
485 {
486 	u_char	 buf[8096];
487 	u_char	*p;
488 	int	 quotec, next, c;
489 	int	 token;
490 
491 	p = buf;
492 	while ((c = lgetc(0)) == ' ' || c == '\t')
493 		; /* nothing */
494 
495 	yylval.lineno = file->lineno;
496 	if (c == '#')
497 		while ((c = lgetc(0)) != '\n' && c != EOF)
498 			; /* nothing */
499 
500 	switch (c) {
501 	case '\'':
502 	case '"':
503 		quotec = c;
504 		while (1) {
505 			if ((c = lgetc(quotec)) == EOF)
506 				return (0);
507 			if (c == '\n') {
508 				file->lineno++;
509 				continue;
510 			} else if (c == '\\') {
511 				if ((next = lgetc(quotec)) == EOF)
512 					return (0);
513 				if (next == quotec || c == ' ' || c == '\t')
514 					c = next;
515 				else if (next == '\n') {
516 					file->lineno++;
517 					continue;
518 				} else
519 					lungetc(next);
520 			} else if (c == quotec) {
521 				*p = '\0';
522 				break;
523 			} else if (c == '\0') {
524 				yyerror("syntax error");
525 				return (findeol());
526 			}
527 			if (p + 1 >= buf + sizeof(buf) - 1) {
528 				yyerror("string too long");
529 				return (findeol());
530 			}
531 			*p++ = c;
532 		}
533 		yylval.v.string = strdup(buf);
534 		if (yylval.v.string == NULL)
535 			fatal("yylex: strdup");
536 		return (STRING);
537 	}
538 
539 #define allowed_to_end_number(x) \
540 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
541 
542 	if (c == '-' || isdigit(c)) {
543 		do {
544 			*p++ = c;
545 			if ((unsigned)(p-buf) >= sizeof(buf)) {
546 				yyerror("string too long");
547 				return (findeol());
548 			}
549 		} while ((c = lgetc(0)) != EOF && isdigit(c));
550 		lungetc(c);
551 		if (p == buf + 1 && buf[0] == '-')
552 			goto nodigits;
553 		if (c == EOF || allowed_to_end_number(c)) {
554 			const char *errstr = NULL;
555 
556 			*p = '\0';
557 			yylval.v.number = strtonum(buf, LLONG_MIN,
558 			    LLONG_MAX, &errstr);
559 			if (errstr) {
560 				yyerror("\"%s\" invalid number: %s",
561 				    buf, errstr);
562 				return (findeol());
563 			}
564 			return (NUMBER);
565 		} else {
566 nodigits:
567 			while (p > buf + 1)
568 				lungetc(*--p);
569 			c = *--p;
570 			if (c == '-')
571 				return (c);
572 		}
573 	}
574 
575 #define allowed_in_string(x) \
576 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
577 	x != '{' && x != '}' && x != '<' && x != '>' && \
578 	x != '!' && x != '=' && x != '/' && x != '#' && \
579 	x != ','))
580 
581 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
582 		do {
583 			*p++ = c;
584 			if ((unsigned)(p-buf) >= sizeof(buf)) {
585 				yyerror("string too long");
586 				return (findeol());
587 			}
588 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
589 		lungetc(c);
590 		*p = '\0';
591 		if ((token = lookup(buf)) == STRING)
592 			if ((yylval.v.string = strdup(buf)) == NULL)
593 				fatal("yylex: strdup");
594 		return (token);
595 	}
596 	if (c == '\n') {
597 		yylval.lineno = file->lineno;
598 		file->lineno++;
599 	}
600 	if (c == EOF)
601 		return (0);
602 	return (c);
603 }
604 
605 struct file *
606 pushfile(const char *name)
607 {
608 	struct file	*nfile;
609 
610 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
611 		log_warn("malloc");
612 		return (NULL);
613 	}
614 	if ((nfile->name = strdup(name)) == NULL) {
615 		log_warn("malloc");
616 		free(nfile);
617 		return (NULL);
618 	}
619 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
620 		log_warn("%s", nfile->name);
621 		free(nfile->name);
622 		free(nfile);
623 		return (NULL);
624 	}
625 	nfile->lineno = 1;
626 	TAILQ_INSERT_TAIL(&files, nfile, entry);
627 	return (nfile);
628 }
629 
630 int
631 popfile(void)
632 {
633 	struct file	*prev;
634 
635 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
636 		prev->errors += file->errors;
637 
638 	TAILQ_REMOVE(&files, file, entry);
639 	fclose(file->stream);
640 	free(file->name);
641 	free(file);
642 	file = prev;
643 	return (file ? 0 : EOF);
644 }
645 
646 int
647 parse_config(const char *filename, struct ntpd_conf *xconf)
648 {
649 	int		 errors = 0;
650 
651 	conf = xconf;
652 	TAILQ_INIT(&conf->listen_addrs);
653 	TAILQ_INIT(&conf->ntp_peers);
654 	TAILQ_INIT(&conf->ntp_conf_sensors);
655 
656 	if ((file = pushfile(filename)) == NULL) {
657 		return (-1);
658 	}
659 	topfile = file;
660 
661 	yyparse();
662 	errors = file->errors;
663 	popfile();
664 
665 	return (errors ? -1 : 0);
666 }
667