xref: /openbsd-src/usr.sbin/dvmrpd/parse.y (revision 08f6ba1906d01c4a24fa700d568400f71bcf9611)
1 /*	$OpenBSD: parse.y,v 1.40 2021/10/15 15:01:27 naddy Exp $ */
2 
3 /*
4  * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
5  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
8  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
9  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 %{
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <sys/stat.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <net/if.h>
32 #include <ctype.h>
33 #include <err.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <syslog.h>
41 
42 #include "igmp.h"
43 #include "dvmrp.h"
44 #include "dvmrpd.h"
45 #include "dvmrpe.h"
46 #include "log.h"
47 
48 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
49 static struct file {
50 	TAILQ_ENTRY(file)	 entry;
51 	FILE			*stream;
52 	char			*name;
53 	int			 lineno;
54 	int			 errors;
55 } *file, *topfile;
56 struct file	*pushfile(const char *, int);
57 int		 popfile(void);
58 int		 check_file_secrecy(int, const char *);
59 int		 yyparse(void);
60 int		 yylex(void);
61 int		 yyerror(const char *, ...)
62     __attribute__((__format__ (printf, 1, 2)))
63     __attribute__((__nonnull__ (1)));
64 int		 kw_cmp(const void *, const void *);
65 int		 lookup(char *);
66 int		 lgetc(int);
67 int		 lungetc(int);
68 int		 findeol(void);
69 
70 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
71 struct sym {
72 	TAILQ_ENTRY(sym)	 entry;
73 	int			 used;
74 	int			 persist;
75 	char			*nam;
76 	char			*val;
77 };
78 int		 symset(const char *, const char *, int);
79 char		*symget(const char *);
80 
81 static struct dvmrpd_conf	*conf;
82 char				*start_state;
83 struct iface			*iface = NULL;
84 
85 static struct {
86 	u_int32_t	 probe_interval;
87 	u_int32_t	 query_interval;
88 	u_int32_t	 query_resp_interval;
89 	u_int32_t	 startup_query_interval;
90 	u_int32_t	 startup_query_cnt;
91 	u_int32_t	 last_member_query_interval;
92 	u_int32_t	 last_member_query_cnt;
93 	u_int32_t	 dead_interval;
94 	u_int16_t	 metric;
95 	u_int8_t	 robustness;
96 	u_int8_t	 igmp_version;
97 } *defs, *grdefs, globaldefs, groupdefs, ifacedefs;
98 
99 void		 clear_config(struct dvmrpd_conf *xconf);
100 struct iface	*conf_get_if(struct kif *);
101 struct iface	*new_group(void);
102 
103 typedef struct {
104 	union {
105 		int64_t		 number;
106 		char		*string;
107 	} v;
108 	int lineno;
109 } YYSTYPE;
110 
111 %}
112 
113 %token	INTERFACE FIBUPDATE
114 %token	GROUP
115 %token	METRIC PASSIVE
116 %token	ROBUSTNESS QUERYINTERVAL QUERYRESPINTERVAL
117 %token	STARTUPQUERYINTERVAL STARTUPQUERYCNT
118 %token	LASTMEMBERQUERYINTERVAL LASTMEMBERQUERYCNT
119 %token	IGMPVERSION
120 %token	ERROR
121 %token	<v.string>	STRING
122 %token	<v.number>	NUMBER
123 %type	<v.number>	yesno
124 %type	<v.string>	string
125 
126 %%
127 
128 grammar		: /* empty */
129 		| grammar '\n'
130 		| grammar conf_main '\n'
131 		| grammar varset '\n'
132 		| grammar interface '\n'
133 		| grammar group '\n'
134 		| grammar error '\n'		{ file->errors++; }
135 		;
136 
137 string		: string STRING {
138 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
139 				free($1);
140 				free($2);
141 				yyerror("string: asprintf");
142 				YYERROR;
143 			}
144 			free($1);
145 			free($2);
146 		}
147 		| STRING
148 		;
149 
150 yesno		: STRING {
151 			if (!strcmp($1, "yes"))
152 				$$ = 1;
153 			else if (!strcmp($1, "no"))
154 				$$ = 0;
155 			else {
156 				yyerror("syntax error, "
157 				    "either yes or no expected");
158 				free($1);
159 				YYERROR;
160 			}
161 			free($1);
162 		}
163 		;
164 
165 varset		: STRING '=' string		{
166 			char *s = $1;
167 			if (conf->opts & DVMRPD_OPT_VERBOSE)
168 				printf("%s = \"%s\"\n", $1, $3);
169 			while (*s++) {
170 				if (isspace((unsigned char)*s)) {
171 					yyerror("macro name cannot contain "
172 					    "whitespace");
173 					free($1);
174 					free($3);
175 					YYERROR;
176 				}
177 			}
178 			if (symset($1, $3, 0) == -1)
179 				fatal("cannot store variable");
180 			free($1);
181 			free($3);
182 		}
183 		;
184 
185 conf_main	: FIBUPDATE yesno {
186 			if ($2 == 0)
187 				conf->flags |= DVMRPD_FLAG_NO_FIB_UPDATE;
188 			else
189 				conf->flags &= ~DVMRPD_FLAG_NO_FIB_UPDATE;
190 		}
191 		| defaults
192 		;
193 
194 defaults	: LASTMEMBERQUERYCNT NUMBER {
195 			if ($2 < MIN_LAST_MEMBER_QUERY_CNT ||
196 			    $2 > MAX_LAST_MEMBER_QUERY_CNT) {
197 				yyerror("last-member-query-count out of "
198 				    "range (%d-%d)",
199 				    MIN_LAST_MEMBER_QUERY_CNT,
200 				    MAX_LAST_MEMBER_QUERY_CNT);
201 				YYERROR;
202 			}
203 			defs->last_member_query_cnt = $2;
204 		}
205 		| LASTMEMBERQUERYINTERVAL NUMBER {
206 			if ($2 < MIN_LAST_MEMBER_QUERY_INTERVAL ||
207 			    $2 > MAX_LAST_MEMBER_QUERY_INTERVAL) {
208 				yyerror("last-member-query-interval out of "
209 				    "range (%d-%d)",
210 				    MIN_LAST_MEMBER_QUERY_INTERVAL,
211 				    MAX_LAST_MEMBER_QUERY_INTERVAL);
212 				YYERROR;
213 			}
214 			defs->last_member_query_interval = $2;
215 		}
216 		| METRIC NUMBER {
217 			if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
218 				yyerror("metric out of range (%d-%d)",
219 				    MIN_METRIC, MAX_METRIC);
220 				YYERROR;
221 			}
222 			defs->metric = $2;
223 		}
224 		| QUERYINTERVAL NUMBER {
225 			if ($2 < MIN_QUERY_INTERVAL ||
226 			    $2 > MAX_QUERY_INTERVAL) {
227 				yyerror("query-interval out of range (%d-%d)",
228 				    MIN_QUERY_INTERVAL, MAX_QUERY_INTERVAL);
229 				YYERROR;
230 			}
231 			defs->query_interval = $2;
232 		}
233 		| QUERYRESPINTERVAL NUMBER {
234 			if ($2 < MIN_QUERY_RESP_INTERVAL ||
235 			    $2 > MAX_QUERY_RESP_INTERVAL) {
236 				yyerror("query-response-interval out of "
237 				    "range (%d-%d)",
238 				    MIN_QUERY_RESP_INTERVAL,
239 				    MAX_QUERY_RESP_INTERVAL);
240 				YYERROR;
241 			}
242 			defs->query_resp_interval = $2;
243 		}
244 		| ROBUSTNESS NUMBER {
245 			if ($2 < MIN_ROBUSTNESS || $2 > MAX_ROBUSTNESS) {
246 				yyerror("robustness out of range (%d-%d)",
247 				    MIN_ROBUSTNESS, MAX_ROBUSTNESS);
248 				YYERROR;
249 			}
250 			defs->robustness = $2;
251 		}
252 		| STARTUPQUERYCNT NUMBER {
253 			if ($2 < MIN_STARTUP_QUERY_CNT ||
254 			    $2 > MAX_STARTUP_QUERY_CNT) {
255 				yyerror("startup-query-count out of "
256 				    "range (%d-%d)",
257 				    MIN_STARTUP_QUERY_CNT,
258 				    MAX_STARTUP_QUERY_CNT);
259 				YYERROR;
260 			}
261 			defs->startup_query_cnt = $2;
262 		}
263 		| STARTUPQUERYINTERVAL NUMBER {
264 			if ($2 < MIN_STARTUP_QUERY_INTERVAL ||
265 			    $2 > MAX_STARTUP_QUERY_INTERVAL) {
266 				yyerror("startup-query-interval out of "
267 				    "range (%d-%d)",
268 				    MIN_STARTUP_QUERY_INTERVAL,
269 				    MAX_STARTUP_QUERY_INTERVAL);
270 				YYERROR;
271 			}
272 			defs->startup_query_interval = $2;
273 		}
274 		| IGMPVERSION NUMBER {
275 			if ($2 < MIN_IGMP_VERSION ||
276 			    $2 > MAX_IGMP_VERSION) {
277 				yyerror("igmp-version out of range (%d-%d)",
278 				    MIN_IGMP_VERSION, MAX_IGMP_VERSION);
279 				YYERROR;
280 			}
281 			defs->igmp_version = $2;
282 		}
283 		;
284 
285 optnl		: '\n' optnl
286 		|
287 		;
288 
289 nl		: '\n' optnl		/* one newline or more */
290 		;
291 
292 interface	: INTERFACE STRING	{
293 			struct kif *kif;
294 
295 			if ((kif = kif_findname($2)) == NULL) {
296 				yyerror("unknown interface %s", $2);
297 				free($2);
298 				YYERROR;
299 			}
300 			free($2);
301 			iface = conf_get_if(kif);
302 			if (iface == NULL)
303 				YYERROR;
304 			LIST_INSERT_HEAD(&conf->iface_list, iface, entry);
305 
306 			memcpy(&ifacedefs, defs, sizeof(ifacedefs));
307 			defs = &ifacedefs;
308 		} interface_block {
309 			iface->probe_interval = defs->probe_interval;
310 			iface->query_interval = defs->query_interval;
311 			iface->query_resp_interval = defs->query_resp_interval;
312 			iface->startup_query_interval =
313 			    defs->startup_query_interval;
314 			iface->startup_query_cnt = defs->startup_query_cnt;
315 			iface->last_member_query_interval =
316 			    defs->last_member_query_interval;
317 			iface->last_member_query_cnt =
318 			    defs->last_member_query_cnt;
319 			iface->dead_interval = defs->dead_interval;
320 			iface->metric = defs->metric;
321 			iface->robustness = defs->robustness;
322 			iface->igmp_version = defs->igmp_version;
323 			if (grdefs)
324 				defs = grdefs;
325 			else
326 				defs = &globaldefs;
327 			iface = NULL;
328 		}
329 		;
330 
331 interface_block	: '{' optnl interfaceopts_l '}'
332 		| '{' optnl '}'
333 		|
334 		;
335 
336 interfaceopts_l	: interfaceopts_l interfaceoptsl
337 		| interfaceoptsl
338 		;
339 
340 interfaceoptsl	: PASSIVE nl		{ iface->passive = 1; }
341 		| defaults nl
342 		;
343 
344 group		: GROUP optnl '{' optnl {
345 			memcpy(&groupdefs, defs, sizeof(groupdefs));
346 			grdefs = defs = &groupdefs;
347 		}
348 		    groupopts_l '}' {
349 			grdefs = NULL;
350 			defs = &globaldefs;
351 		}
352 		;
353 
354 groupopts_l	: groupopts_l groupoptsl
355 		| groupoptsl
356 		;
357 
358 groupoptsl	: interface nl
359 		| defaults nl
360 		| error nl
361 		;
362 
363 %%
364 
365 struct keywords {
366 	const char	*k_name;
367 	int		 k_val;
368 };
369 
370 int
yyerror(const char * fmt,...)371 yyerror(const char *fmt, ...)
372 {
373 	va_list		 ap;
374 	char		*msg;
375 
376 	file->errors++;
377 	va_start(ap, fmt);
378 	if (vasprintf(&msg, fmt, ap) == -1)
379 		fatalx("yyerror vasprintf");
380 	va_end(ap);
381 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
382 	free(msg);
383 	return (0);
384 }
385 
386 int
kw_cmp(const void * k,const void * e)387 kw_cmp(const void *k, const void *e)
388 {
389 	return (strcmp(k, ((const struct keywords *)e)->k_name));
390 }
391 
392 int
lookup(char * s)393 lookup(char *s)
394 {
395 	/* this has to be sorted always */
396 	static const struct keywords keywords[] = {
397 		{"fib-update",			FIBUPDATE},
398 		{"group",			GROUP},
399 		{"igmp-version",		IGMPVERSION},
400 		{"interface",			INTERFACE},
401 		{"last-member-query-count",	LASTMEMBERQUERYCNT},
402 		{"last-member-query-interval",	LASTMEMBERQUERYINTERVAL},
403 		{"metric",			METRIC},
404 		{"passive",			PASSIVE},
405 		{"query-interval",		QUERYINTERVAL},
406 		{"query-response-interval",	QUERYRESPINTERVAL},
407 		{"robustness",			ROBUSTNESS},
408 		{"startup-query-count",		STARTUPQUERYCNT},
409 		{"startup-query-interval",	STARTUPQUERYINTERVAL}
410 	};
411 	const struct keywords	*p;
412 
413 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
414 	    sizeof(keywords[0]), kw_cmp);
415 
416 	if (p)
417 		return (p->k_val);
418 	else
419 		return (STRING);
420 }
421 
422 #define MAXPUSHBACK	128
423 
424 char	*parsebuf;
425 int	 parseindex;
426 char	 pushback_buffer[MAXPUSHBACK];
427 int	 pushback_index = 0;
428 
429 int
lgetc(int quotec)430 lgetc(int quotec)
431 {
432 	int		c, next;
433 
434 	if (parsebuf) {
435 		/* Read character from the parsebuffer instead of input. */
436 		if (parseindex >= 0) {
437 			c = (unsigned char)parsebuf[parseindex++];
438 			if (c != '\0')
439 				return (c);
440 			parsebuf = NULL;
441 		} else
442 			parseindex++;
443 	}
444 
445 	if (pushback_index)
446 		return ((unsigned char)pushback_buffer[--pushback_index]);
447 
448 	if (quotec) {
449 		if ((c = getc(file->stream)) == EOF) {
450 			yyerror("reached end of file while parsing "
451 			    "quoted string");
452 			if (file == topfile || popfile() == EOF)
453 				return (EOF);
454 			return (quotec);
455 		}
456 		return (c);
457 	}
458 
459 	while ((c = getc(file->stream)) == '\\') {
460 		next = getc(file->stream);
461 		if (next != '\n') {
462 			c = next;
463 			break;
464 		}
465 		yylval.lineno = file->lineno;
466 		file->lineno++;
467 	}
468 
469 	while (c == EOF) {
470 		if (file == topfile || popfile() == EOF)
471 			return (EOF);
472 		c = getc(file->stream);
473 	}
474 	return (c);
475 }
476 
477 int
lungetc(int c)478 lungetc(int c)
479 {
480 	if (c == EOF)
481 		return (EOF);
482 	if (parsebuf) {
483 		parseindex--;
484 		if (parseindex >= 0)
485 			return (c);
486 	}
487 	if (pushback_index + 1 >= MAXPUSHBACK)
488 		return (EOF);
489 	pushback_buffer[pushback_index++] = c;
490 	return (c);
491 }
492 
493 int
findeol(void)494 findeol(void)
495 {
496 	int	c;
497 
498 	parsebuf = NULL;
499 
500 	/* skip to either EOF or the first real EOL */
501 	while (1) {
502 		if (pushback_index)
503 			c = (unsigned char)pushback_buffer[--pushback_index];
504 		else
505 			c = lgetc(0);
506 		if (c == '\n') {
507 			file->lineno++;
508 			break;
509 		}
510 		if (c == EOF)
511 			break;
512 	}
513 	return (ERROR);
514 }
515 
516 int
yylex(void)517 yylex(void)
518 {
519 	char	 buf[8096];
520 	char	*p, *val;
521 	int	 quotec, next, c;
522 	int	 token;
523 
524 top:
525 	p = buf;
526 	while ((c = lgetc(0)) == ' ' || c == '\t')
527 		; /* nothing */
528 
529 	yylval.lineno = file->lineno;
530 	if (c == '#')
531 		while ((c = lgetc(0)) != '\n' && c != EOF)
532 			; /* nothing */
533 	if (c == '$' && parsebuf == NULL) {
534 		while (1) {
535 			if ((c = lgetc(0)) == EOF)
536 				return (0);
537 
538 			if (p + 1 >= buf + sizeof(buf) - 1) {
539 				yyerror("string too long");
540 				return (findeol());
541 			}
542 			if (isalnum(c) || c == '_') {
543 				*p++ = c;
544 				continue;
545 			}
546 			*p = '\0';
547 			lungetc(c);
548 			break;
549 		}
550 		val = symget(buf);
551 		if (val == NULL) {
552 			yyerror("macro '%s' not defined", buf);
553 			return (findeol());
554 		}
555 		parsebuf = val;
556 		parseindex = 0;
557 		goto top;
558 	}
559 
560 	switch (c) {
561 	case '\'':
562 	case '"':
563 		quotec = c;
564 		while (1) {
565 			if ((c = lgetc(quotec)) == EOF)
566 				return (0);
567 			if (c == '\n') {
568 				file->lineno++;
569 				continue;
570 			} else if (c == '\\') {
571 				if ((next = lgetc(quotec)) == EOF)
572 					return (0);
573 				if (next == quotec || next == ' ' ||
574 				    next == '\t')
575 					c = next;
576 				else if (next == '\n') {
577 					file->lineno++;
578 					continue;
579 				} else
580 					lungetc(next);
581 			} else if (c == quotec) {
582 				*p = '\0';
583 				break;
584 			} else if (c == '\0') {
585 				yyerror("syntax error");
586 				return (findeol());
587 			}
588 			if (p + 1 >= buf + sizeof(buf) - 1) {
589 				yyerror("string too long");
590 				return (findeol());
591 			}
592 			*p++ = c;
593 		}
594 		yylval.v.string = strdup(buf);
595 		if (yylval.v.string == NULL)
596 			err(1, "%s", __func__);
597 		return (STRING);
598 	}
599 
600 #define allowed_to_end_number(x) \
601 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
602 
603 	if (c == '-' || isdigit(c)) {
604 		do {
605 			*p++ = c;
606 			if ((size_t)(p-buf) >= sizeof(buf)) {
607 				yyerror("string too long");
608 				return (findeol());
609 			}
610 		} while ((c = lgetc(0)) != EOF && isdigit(c));
611 		lungetc(c);
612 		if (p == buf + 1 && buf[0] == '-')
613 			goto nodigits;
614 		if (c == EOF || allowed_to_end_number(c)) {
615 			const char *errstr = NULL;
616 
617 			*p = '\0';
618 			yylval.v.number = strtonum(buf, LLONG_MIN,
619 			    LLONG_MAX, &errstr);
620 			if (errstr) {
621 				yyerror("\"%s\" invalid number: %s",
622 				    buf, errstr);
623 				return (findeol());
624 			}
625 			return (NUMBER);
626 		} else {
627 nodigits:
628 			while (p > buf + 1)
629 				lungetc((unsigned char)*--p);
630 			c = (unsigned char)*--p;
631 			if (c == '-')
632 				return (c);
633 		}
634 	}
635 
636 #define allowed_in_string(x) \
637 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
638 	x != '{' && x != '}' && \
639 	x != '!' && x != '=' && x != '#' && \
640 	x != ','))
641 
642 	if (isalnum(c) || c == ':' || c == '_') {
643 		do {
644 			*p++ = c;
645 			if ((size_t)(p-buf) >= sizeof(buf)) {
646 				yyerror("string too long");
647 				return (findeol());
648 			}
649 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
650 		lungetc(c);
651 		*p = '\0';
652 		if ((token = lookup(buf)) == STRING)
653 			if ((yylval.v.string = strdup(buf)) == NULL)
654 				err(1, "%s", __func__);
655 		return (token);
656 	}
657 	if (c == '\n') {
658 		yylval.lineno = file->lineno;
659 		file->lineno++;
660 	}
661 	if (c == EOF)
662 		return (0);
663 	return (c);
664 }
665 
666 int
check_file_secrecy(int fd,const char * fname)667 check_file_secrecy(int fd, const char *fname)
668 {
669 	struct stat	st;
670 
671 	if (fstat(fd, &st)) {
672 		log_warn("cannot stat %s", fname);
673 		return (-1);
674 	}
675 	if (st.st_uid != 0 && st.st_uid != getuid()) {
676 		log_warnx("%s: owner not root or current user", fname);
677 		return (-1);
678 	}
679 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
680 		log_warnx("%s: group writable or world read/writable", fname);
681 		return (-1);
682 	}
683 	return (0);
684 }
685 
686 struct file *
pushfile(const char * name,int secret)687 pushfile(const char *name, int secret)
688 {
689 	struct file	*nfile;
690 
691 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
692 		log_warn("%s", __func__);
693 		return (NULL);
694 	}
695 	if ((nfile->name = strdup(name)) == NULL) {
696 		log_warn("%s", __func__);
697 		free(nfile);
698 		return (NULL);
699 	}
700 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
701 		log_warn("%s: %s", __func__, nfile->name);
702 		free(nfile->name);
703 		free(nfile);
704 		return (NULL);
705 	} else if (secret &&
706 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
707 		fclose(nfile->stream);
708 		free(nfile->name);
709 		free(nfile);
710 		return (NULL);
711 	}
712 	nfile->lineno = 1;
713 	TAILQ_INSERT_TAIL(&files, nfile, entry);
714 	return (nfile);
715 }
716 
717 int
popfile(void)718 popfile(void)
719 {
720 	struct file	*prev;
721 
722 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
723 		prev->errors += file->errors;
724 
725 	TAILQ_REMOVE(&files, file, entry);
726 	fclose(file->stream);
727 	free(file->name);
728 	free(file);
729 	file = prev;
730 	return (file ? 0 : EOF);
731 }
732 
733 struct dvmrpd_conf *
parse_config(char * filename,int opts)734 parse_config(char *filename, int opts)
735 {
736 	int		 errors = 0;
737 	struct sym	*sym, *next;
738 	struct timeval	 now;
739 
740 	if ((conf = calloc(1, sizeof(struct dvmrpd_conf))) == NULL) {
741 		errx(1, "parse_config calloc");
742 		return (NULL);
743 	}
744 
745 	defs = &globaldefs;
746 	defs->probe_interval = DEFAULT_PROBE_INTERVAL;
747 	defs->last_member_query_cnt = DEFAULT_LAST_MEMBER_QUERY_CNT;
748 	defs->last_member_query_interval = DEFAULT_LAST_MEMBER_QUERY_INTERVAL;
749 	defs->metric = DEFAULT_METRIC;
750 	defs->query_interval = DEFAULT_QUERY_INTERVAL;
751 	defs->query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL;
752 	defs->robustness = DEFAULT_ROBUSTNESS;
753 	defs->startup_query_cnt = DEFAULT_STARTUP_QUERY_CNT;
754 	defs->startup_query_interval = DEFAULT_STARTUP_QUERY_INTERVAL;
755 	defs->igmp_version = DEFAULT_IGMP_VERSION;
756 	defs->dead_interval = NBR_TMOUT;
757 
758 	if ((file = pushfile(filename, 1)) == NULL) {
759 		free(conf);
760 		return (NULL);
761 	}
762 	topfile = file;
763 
764 	gettimeofday(&now, NULL);
765 	conf->gen_id = (u_int32_t)now.tv_sec;	/* for a while after 2038 */
766 	conf->opts = opts;
767 
768 	yyparse();
769 	errors = file->errors;
770 	popfile();
771 
772 	/* Free macros and check which have not been used. */
773 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
774 		if ((conf->opts & DVMRPD_OPT_VERBOSE2) && !sym->used)
775 			fprintf(stderr, "warning: macro '%s' not "
776 			    "used\n", sym->nam);
777 		if (!sym->persist) {
778 			free(sym->nam);
779 			free(sym->val);
780 			TAILQ_REMOVE(&symhead, sym, entry);
781 			free(sym);
782 		}
783 	}
784 
785 	if (errors) {
786 		clear_config(conf);
787 		return (NULL);
788 	}
789 
790 	return (conf);
791 }
792 
793 int
symset(const char * nam,const char * val,int persist)794 symset(const char *nam, const char *val, int persist)
795 {
796 	struct sym	*sym;
797 
798 	TAILQ_FOREACH(sym, &symhead, entry) {
799 		if (strcmp(nam, sym->nam) == 0)
800 			break;
801 	}
802 
803 	if (sym != NULL) {
804 		if (sym->persist == 1)
805 			return (0);
806 		else {
807 			free(sym->nam);
808 			free(sym->val);
809 			TAILQ_REMOVE(&symhead, sym, entry);
810 			free(sym);
811 		}
812 	}
813 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
814 		return (-1);
815 
816 	sym->nam = strdup(nam);
817 	if (sym->nam == NULL) {
818 		free(sym);
819 		return (-1);
820 	}
821 	sym->val = strdup(val);
822 	if (sym->val == NULL) {
823 		free(sym->nam);
824 		free(sym);
825 		return (-1);
826 	}
827 	sym->used = 0;
828 	sym->persist = persist;
829 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
830 	return (0);
831 }
832 
833 int
cmdline_symset(char * s)834 cmdline_symset(char *s)
835 {
836 	char	*sym, *val;
837 	int	ret;
838 
839 	if ((val = strrchr(s, '=')) == NULL)
840 		return (-1);
841 	sym = strndup(s, val - s);
842 	if (sym == NULL)
843 		errx(1, "%s: strndup", __func__);
844 	ret = symset(sym, val + 1, 1);
845 	free(sym);
846 
847 	return (ret);
848 }
849 
850 char *
symget(const char * nam)851 symget(const char *nam)
852 {
853 	struct sym	*sym;
854 
855 	TAILQ_FOREACH(sym, &symhead, entry) {
856 		if (strcmp(nam, sym->nam) == 0) {
857 			sym->used = 1;
858 			return (sym->val);
859 		}
860 	}
861 	return (NULL);
862 }
863 
864 struct iface *
conf_get_if(struct kif * kif)865 conf_get_if(struct kif *kif)
866 {
867 	struct iface	*i;
868 
869 	if (kif->ifindex >= MAXVIFS) {
870 		yyerror("interface %s index too large", kif->ifname);
871 		return (NULL);
872 	}
873 
874 	LIST_FOREACH(i, &conf->iface_list, entry)
875 		if (i->ifindex == kif->ifindex) {
876 			yyerror("interface %s already configured",
877 			    kif->ifname);
878 			return (NULL);
879 		}
880 
881 	i = if_new(kif);
882 	i->passive = 0;
883 	i->recv_query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL;
884 
885 	return (i);
886 }
887 
888 void
clear_config(struct dvmrpd_conf * xconf)889 clear_config(struct dvmrpd_conf *xconf)
890 {
891 	/* XXX clear conf */
892 		/* ... */
893 }
894