xref: /openbsd/usr.sbin/ntpd/parse.y (revision 9b7c3dbb)
1 /*	$OpenBSD: parse.y,v 1.65 2015/10/31 19:32:18 naddy 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 CONSTRAINT CONSTRAINTS FROM
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 url
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 ((h = $3->a) == NULL &&
110 			    (host_dns($3->name, &h) == -1 || !h)) {
111 				yyerror("could not resolve \"%s\"", $3->name);
112 				free($3->name);
113 				free($3);
114 				YYERROR;
115 			}
116 
117 			for (; h != NULL; h = next) {
118 				next = h->next;
119 				la = calloc(1, sizeof(struct listen_addr));
120 				if (la == NULL)
121 					fatal("listen on calloc");
122 				la->fd = -1;
123 				la->rtable = $4.rtable;
124 				memcpy(&la->sa, &h->ss,
125 				    sizeof(struct sockaddr_storage));
126 				TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
127 				    entry);
128 				free(h);
129 			}
130 			free($3->name);
131 			free($3);
132 		}
133 		| SERVERS address server_opts	{
134 			struct ntp_peer		*p;
135 			struct ntp_addr		*h, *next;
136 
137 			h = $2->a;
138 			do {
139 				if (h != NULL) {
140 					next = h->next;
141 					if (h->ss.ss_family != AF_INET &&
142 					    h->ss.ss_family != AF_INET6) {
143 						yyerror("IPv4 or IPv6 address "
144 						    "or hostname expected");
145 						free(h);
146 						free($2->name);
147 						free($2);
148 						YYERROR;
149 					}
150 					h->next = NULL;
151 				} else
152 					next = NULL;
153 
154 				p = new_peer();
155 				p->weight = $3.weight;
156 				p->addr = h;
157 				p->addr_head.a = h;
158 				p->addr_head.pool = 1;
159 				p->addr_head.name = strdup($2->name);
160 				if (p->addr_head.name == NULL)
161 					fatal(NULL);
162 				if (p->addr != NULL)
163 					p->state = STATE_DNS_DONE;
164 				TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
165 				h = next;
166 			} while (h != NULL);
167 
168 			free($2->name);
169 			free($2);
170 		}
171 		| SERVER address server_opts {
172 			struct ntp_peer		*p;
173 			struct ntp_addr		*h, *next;
174 
175 			p = new_peer();
176 			for (h = $2->a; h != NULL; h = next) {
177 				next = h->next;
178 				if (h->ss.ss_family != AF_INET &&
179 				    h->ss.ss_family != AF_INET6) {
180 					yyerror("IPv4 or IPv6 address "
181 					    "or hostname expected");
182 					free(h);
183 					free(p);
184 					free($2->name);
185 					free($2);
186 					YYERROR;
187 				}
188 				h->next = p->addr;
189 				p->addr = h;
190 			}
191 
192 			p->weight = $3.weight;
193 			p->addr_head.a = p->addr;
194 			p->addr_head.pool = 0;
195 			p->addr_head.name = strdup($2->name);
196 			if (p->addr_head.name == NULL)
197 				fatal(NULL);
198 			if (p->addr != NULL)
199 				p->state = STATE_DNS_DONE;
200 			TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
201 			free($2->name);
202 			free($2);
203 		}
204 		| CONSTRAINTS FROM url		{
205 			struct constraint	*p;
206 			struct ntp_addr		*h, *next;
207 
208 			h = $3->a;
209 			do {
210 				if (h != NULL) {
211 					next = h->next;
212 					if (h->ss.ss_family != AF_INET &&
213 					    h->ss.ss_family != AF_INET6) {
214 						yyerror("IPv4 or IPv6 address "
215 						    "or hostname expected");
216 						free(h);
217 						free($3->name);
218 						free($3->path);
219 						free($3);
220 						YYERROR;
221 					}
222 					h->next = NULL;
223 				} else
224 					next = NULL;
225 
226 				p = new_constraint();
227 				p->addr = h;
228 				p->addr_head.a = h;
229 				p->addr_head.pool = 1;
230 				p->addr_head.name = strdup($3->name);
231 				p->addr_head.path = strdup($3->path);
232 				if (p->addr_head.name == NULL ||
233 				    p->addr_head.path == NULL)
234 					fatal(NULL);
235 				if (p->addr != NULL)
236 					p->state = STATE_DNS_DONE;
237 				constraint_add(p);
238 				h = next;
239 			} while (h != NULL);
240 
241 			free($3->name);
242 			free($3);
243 		}
244 		| CONSTRAINT FROM url		{
245 			struct constraint	*p;
246 			struct ntp_addr		*h, *next;
247 
248 			p = new_constraint();
249 			for (h = $3->a; h != NULL; h = next) {
250 				next = h->next;
251 				if (h->ss.ss_family != AF_INET &&
252 				    h->ss.ss_family != AF_INET6) {
253 					yyerror("IPv4 or IPv6 address "
254 					    "or hostname expected");
255 					free(h);
256 					free(p);
257 					free($3->name);
258 					free($3->path);
259 					free($3);
260 					YYERROR;
261 				}
262 				h->next = p->addr;
263 				p->addr = h;
264 			}
265 
266 			p->addr_head.a = p->addr;
267 			p->addr_head.pool = 0;
268 			p->addr_head.name = strdup($3->name);
269 			p->addr_head.path = strdup($3->path);
270 			if (p->addr_head.name == NULL ||
271 			    p->addr_head.path == NULL)
272 				fatal(NULL);
273 			if (p->addr != NULL)
274 				p->state = STATE_DNS_DONE;
275 			constraint_add(p);
276 			free($3->name);
277 			free($3);
278 		}
279 		| SENSOR STRING	sensor_opts {
280 			struct ntp_conf_sensor	*s;
281 
282 			s = new_sensor($2);
283 			s->weight = $3.weight;
284 			s->correction = $3.correction;
285 			s->refstr = $3.refstr;
286 			s->stratum = $3.stratum;
287 			free($2);
288 			TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry);
289 		}
290 		;
291 
292 address		: STRING		{
293 			if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
294 			    NULL)
295 				fatal(NULL);
296 			host($1, &$$->a);
297 			$$->name = $1;
298 		}
299 		;
300 
301 url		: STRING		{
302 			char	*hname, *path;
303 
304 			if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
305 			    NULL)
306 				fatal("calloc");
307 
308 			if (strncmp("https://", $1,
309 			    strlen("https://")) != 0) {
310 				host($1, &$$->a);
311 				$$->name = $1;
312 			} else {
313 				hname = $1 + strlen("https://");
314 
315 				path = hname + strcspn(hname, "/\\");
316 				if (*path != '\0') {
317 					if (($$->path = strdup(path)) == NULL)
318 						fatal("strdup");
319 					*path = '\0';
320 				}
321 				host(hname, &$$->a);
322 				if (($$->name = strdup(hname)) == NULL)
323 					fatal("strdup");
324 			}
325 			if ($$->path == NULL &&
326 			    ($$->path = strdup("/")) == NULL)
327 				fatal("strdup");
328 		}
329 		;
330 
331 listen_opts	:	{ opts_default(); }
332 		  listen_opts_l
333 			{ $$ = opts; }
334 		|	{ opts_default(); $$ = opts; }
335 		;
336 listen_opts_l	: listen_opts_l listen_opt
337 		| listen_opt
338 		;
339 listen_opt	: rtable
340 		;
341 
342 server_opts	:	{ opts_default(); }
343 		  server_opts_l
344 			{ $$ = opts; }
345 		|	{ opts_default(); $$ = opts; }
346 		;
347 server_opts_l	: server_opts_l server_opt
348 		| server_opt
349 		;
350 server_opt	: weight
351 		;
352 
353 sensor_opts	:	{ opts_default(); }
354 		  sensor_opts_l
355 			{ $$ = opts; }
356 		|	{ opts_default(); $$ = opts; }
357 		;
358 sensor_opts_l	: sensor_opts_l sensor_opt
359 		| sensor_opt
360 		;
361 sensor_opt	: correction
362 		| refid
363 		| stratum
364 		| weight
365 		;
366 
367 correction	: CORRECTION NUMBER {
368 			if ($2 < -127000000 || $2 > 127000000) {
369 				yyerror("correction must be between "
370 				    "-127000000 and 127000000 microseconds");
371 				YYERROR;
372 			}
373 			opts.correction = $2;
374 		}
375 		;
376 
377 refid		: REFID STRING {
378 			size_t l = strlen($2);
379 
380 			if (l < 1 || l > 4) {
381 				yyerror("refid must be 1 to 4 characters");
382 				free($2);
383 				YYERROR;
384 			}
385 			opts.refstr = $2;
386 		}
387 		;
388 
389 stratum		: STRATUM NUMBER {
390 			if ($2 < 1 || $2 > 15) {
391 				yyerror("stratum must be between "
392 				    "1 and 15");
393 				YYERROR;
394 			}
395 			opts.stratum = $2;
396 		}
397 		;
398 
399 weight		: WEIGHT NUMBER	{
400 			if ($2 < 1 || $2 > 10) {
401 				yyerror("weight must be between 1 and 10");
402 				YYERROR;
403 			}
404 			opts.weight = $2;
405 		}
406 rtable		: RTABLE NUMBER {
407 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
408 				yyerror("rtable must be between 1"
409 				    " and RT_TABLEID_MAX");
410 				YYERROR;
411 			}
412 			opts.rtable = $2;
413 		}
414 		;
415 
416 %%
417 
418 void
419 opts_default(void)
420 {
421 	memset(&opts, 0, sizeof opts);
422 	opts.weight = 1;
423 	opts.stratum = 1;
424 }
425 
426 struct keywords {
427 	const char	*k_name;
428 	int		 k_val;
429 };
430 
431 int
432 yyerror(const char *fmt, ...)
433 {
434 	va_list		 ap;
435 	char		*msg;
436 
437 	file->errors++;
438 	va_start(ap, fmt);
439 	if (vasprintf(&msg, fmt, ap) == -1)
440 		fatalx("yyerror vasprintf");
441 	va_end(ap);
442 	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
443 	free(msg);
444 	return (0);
445 }
446 
447 int
448 kw_cmp(const void *k, const void *e)
449 {
450 	return (strcmp(k, ((const struct keywords *)e)->k_name));
451 }
452 
453 int
454 lookup(char *s)
455 {
456 	/* this has to be sorted always */
457 	static const struct keywords keywords[] = {
458 		{ "constraint",		CONSTRAINT},
459 		{ "constraints",	CONSTRAINTS},
460 		{ "correction",		CORRECTION},
461 		{ "from",		FROM},
462 		{ "listen",		LISTEN},
463 		{ "on",			ON},
464 		{ "refid",		REFID},
465 		{ "rtable",		RTABLE},
466 		{ "sensor",		SENSOR},
467 		{ "server",		SERVER},
468 		{ "servers",		SERVERS},
469 		{ "stratum",		STRATUM},
470 		{ "weight",		WEIGHT}
471 	};
472 	const struct keywords	*p;
473 
474 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
475 	    sizeof(keywords[0]), kw_cmp);
476 
477 	if (p)
478 		return (p->k_val);
479 	else
480 		return (STRING);
481 }
482 
483 #define MAXPUSHBACK	128
484 
485 u_char	*parsebuf;
486 int	 parseindex;
487 u_char	 pushback_buffer[MAXPUSHBACK];
488 int	 pushback_index = 0;
489 
490 int
491 lgetc(int quotec)
492 {
493 	int		c, next;
494 
495 	if (parsebuf) {
496 		/* Read character from the parsebuffer instead of input. */
497 		if (parseindex >= 0) {
498 			c = parsebuf[parseindex++];
499 			if (c != '\0')
500 				return (c);
501 			parsebuf = NULL;
502 		} else
503 			parseindex++;
504 	}
505 
506 	if (pushback_index)
507 		return (pushback_buffer[--pushback_index]);
508 
509 	if (quotec) {
510 		if ((c = getc(file->stream)) == EOF) {
511 			yyerror("reached end of file while parsing "
512 			    "quoted string");
513 			if (file == topfile || popfile() == EOF)
514 				return (EOF);
515 			return (quotec);
516 		}
517 		return (c);
518 	}
519 
520 	while ((c = getc(file->stream)) == '\\') {
521 		next = getc(file->stream);
522 		if (next != '\n') {
523 			c = next;
524 			break;
525 		}
526 		yylval.lineno = file->lineno;
527 		file->lineno++;
528 	}
529 
530 	while (c == EOF) {
531 		if (file == topfile || popfile() == EOF)
532 			return (EOF);
533 		c = getc(file->stream);
534 	}
535 	return (c);
536 }
537 
538 int
539 lungetc(int c)
540 {
541 	if (c == EOF)
542 		return (EOF);
543 	if (parsebuf) {
544 		parseindex--;
545 		if (parseindex >= 0)
546 			return (c);
547 	}
548 	if (pushback_index < MAXPUSHBACK-1)
549 		return (pushback_buffer[pushback_index++] = c);
550 	else
551 		return (EOF);
552 }
553 
554 int
555 findeol(void)
556 {
557 	int	c;
558 
559 	parsebuf = NULL;
560 
561 	/* skip to either EOF or the first real EOL */
562 	while (1) {
563 		if (pushback_index)
564 			c = pushback_buffer[--pushback_index];
565 		else
566 			c = lgetc(0);
567 		if (c == '\n') {
568 			file->lineno++;
569 			break;
570 		}
571 		if (c == EOF)
572 			break;
573 	}
574 	return (ERROR);
575 }
576 
577 int
578 yylex(void)
579 {
580 	u_char	 buf[8096];
581 	u_char	*p;
582 	int	 quotec, next, c;
583 	int	 token;
584 
585 	p = buf;
586 	while ((c = lgetc(0)) == ' ' || c == '\t')
587 		; /* nothing */
588 
589 	yylval.lineno = file->lineno;
590 	if (c == '#')
591 		while ((c = lgetc(0)) != '\n' && c != EOF)
592 			; /* nothing */
593 
594 	switch (c) {
595 	case '\'':
596 	case '"':
597 		quotec = c;
598 		while (1) {
599 			if ((c = lgetc(quotec)) == EOF)
600 				return (0);
601 			if (c == '\n') {
602 				file->lineno++;
603 				continue;
604 			} else if (c == '\\') {
605 				if ((next = lgetc(quotec)) == EOF)
606 					return (0);
607 				if (next == quotec || c == ' ' || c == '\t')
608 					c = next;
609 				else if (next == '\n') {
610 					file->lineno++;
611 					continue;
612 				} else
613 					lungetc(next);
614 			} else if (c == quotec) {
615 				*p = '\0';
616 				break;
617 			} else if (c == '\0') {
618 				yyerror("syntax error");
619 				return (findeol());
620 			}
621 			if (p + 1 >= buf + sizeof(buf) - 1) {
622 				yyerror("string too long");
623 				return (findeol());
624 			}
625 			*p++ = c;
626 		}
627 		yylval.v.string = strdup(buf);
628 		if (yylval.v.string == NULL)
629 			fatal("yylex: strdup");
630 		return (STRING);
631 	}
632 
633 #define allowed_to_end_number(x) \
634 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
635 
636 	if (c == '-' || isdigit(c)) {
637 		do {
638 			*p++ = c;
639 			if ((unsigned)(p-buf) >= sizeof(buf)) {
640 				yyerror("string too long");
641 				return (findeol());
642 			}
643 		} while ((c = lgetc(0)) != EOF && isdigit(c));
644 		lungetc(c);
645 		if (p == buf + 1 && buf[0] == '-')
646 			goto nodigits;
647 		if (c == EOF || allowed_to_end_number(c)) {
648 			const char *errstr = NULL;
649 
650 			*p = '\0';
651 			yylval.v.number = strtonum(buf, LLONG_MIN,
652 			    LLONG_MAX, &errstr);
653 			if (errstr) {
654 				yyerror("\"%s\" invalid number: %s",
655 				    buf, errstr);
656 				return (findeol());
657 			}
658 			return (NUMBER);
659 		} else {
660 nodigits:
661 			while (p > buf + 1)
662 				lungetc(*--p);
663 			c = *--p;
664 			if (c == '-')
665 				return (c);
666 		}
667 	}
668 
669 #define allowed_in_string(x) \
670 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
671 	x != '{' && x != '}' && x != '<' && x != '>' && \
672 	x != '!' && x != '=' && x != '/' && x != '#' && \
673 	x != ','))
674 
675 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
676 		do {
677 			*p++ = c;
678 			if ((unsigned)(p-buf) >= sizeof(buf)) {
679 				yyerror("string too long");
680 				return (findeol());
681 			}
682 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
683 		lungetc(c);
684 		*p = '\0';
685 		if ((token = lookup(buf)) == STRING)
686 			if ((yylval.v.string = strdup(buf)) == NULL)
687 				fatal("yylex: strdup");
688 		return (token);
689 	}
690 	if (c == '\n') {
691 		yylval.lineno = file->lineno;
692 		file->lineno++;
693 	}
694 	if (c == EOF)
695 		return (0);
696 	return (c);
697 }
698 
699 struct file *
700 pushfile(const char *name)
701 {
702 	struct file	*nfile;
703 
704 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
705 		log_warn("malloc");
706 		return (NULL);
707 	}
708 	if ((nfile->name = strdup(name)) == NULL) {
709 		log_warn("malloc");
710 		free(nfile);
711 		return (NULL);
712 	}
713 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
714 		log_warn("%s", nfile->name);
715 		free(nfile->name);
716 		free(nfile);
717 		return (NULL);
718 	}
719 	nfile->lineno = 1;
720 	TAILQ_INSERT_TAIL(&files, nfile, entry);
721 	return (nfile);
722 }
723 
724 int
725 popfile(void)
726 {
727 	struct file	*prev;
728 
729 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
730 		prev->errors += file->errors;
731 
732 	TAILQ_REMOVE(&files, file, entry);
733 	fclose(file->stream);
734 	free(file->name);
735 	free(file);
736 	file = prev;
737 	return (file ? 0 : EOF);
738 }
739 
740 int
741 parse_config(const char *filename, struct ntpd_conf *xconf)
742 {
743 	int		 errors = 0;
744 
745 	conf = xconf;
746 	TAILQ_INIT(&conf->listen_addrs);
747 	TAILQ_INIT(&conf->ntp_peers);
748 	TAILQ_INIT(&conf->ntp_conf_sensors);
749 	TAILQ_INIT(&conf->constraints);
750 
751 	if ((file = pushfile(filename)) == NULL) {
752 		return (-1);
753 	}
754 	topfile = file;
755 
756 	yyparse();
757 	errors = file->errors;
758 	popfile();
759 
760 	return (errors ? -1 : 0);
761 }
762