xref: /openbsd-src/usr.sbin/dvmrpd/parse.y (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: parse.y,v 1.30 2016/06/21 21:35:24 benno 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 					YYERROR;
174 				}
175 			}
176 			if (symset($1, $3, 0) == -1)
177 				fatal("cannot store variable");
178 			free($1);
179 			free($3);
180 		}
181 		;
182 
183 conf_main	: FIBUPDATE yesno {
184 			if ($2 == 0)
185 				conf->flags |= DVMRPD_FLAG_NO_FIB_UPDATE;
186 			else
187 				conf->flags &= ~DVMRPD_FLAG_NO_FIB_UPDATE;
188 		}
189 		| defaults
190 		;
191 
192 defaults	: LASTMEMBERQUERYCNT NUMBER {
193 			if ($2 < MIN_LAST_MEMBER_QUERY_CNT ||
194 			    $2 > MAX_LAST_MEMBER_QUERY_CNT) {
195 				yyerror("last-member-query-count out of "
196 				    "range (%d-%d)",
197 				    MIN_LAST_MEMBER_QUERY_CNT,
198 				    MAX_LAST_MEMBER_QUERY_CNT);
199 				YYERROR;
200 			}
201 			defs->last_member_query_cnt = $2;
202 		}
203 		| LASTMEMBERQUERYINTERVAL NUMBER {
204 			if ($2 < MIN_LAST_MEMBER_QUERY_INTERVAL ||
205 			    $2 > MAX_LAST_MEMBER_QUERY_INTERVAL) {
206 				yyerror("last-member-query-interval out of "
207 				    "range (%d-%d)",
208 				    MIN_LAST_MEMBER_QUERY_INTERVAL,
209 				    MAX_LAST_MEMBER_QUERY_INTERVAL);
210 				YYERROR;
211 			}
212 			defs->last_member_query_interval = $2;
213 		}
214 		| METRIC NUMBER {
215 			if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
216 				yyerror("metric out of range (%d-%d)",
217 				    MIN_METRIC, MAX_METRIC);
218 				YYERROR;
219 			}
220 			defs->metric = $2;
221 		}
222 		| QUERYINTERVAL NUMBER {
223 			if ($2 < MIN_QUERY_INTERVAL ||
224 			    $2 > MAX_QUERY_INTERVAL) {
225 				yyerror("query-interval out of range (%d-%d)",
226 				    MIN_QUERY_INTERVAL, MAX_QUERY_INTERVAL);
227 				YYERROR;
228 			}
229 			defs->query_interval = $2;
230 		}
231 		| QUERYRESPINTERVAL NUMBER {
232 			if ($2 < MIN_QUERY_RESP_INTERVAL ||
233 			    $2 > MAX_QUERY_RESP_INTERVAL) {
234 				yyerror("query-response-interval out of "
235 				    "range (%d-%d)",
236 				    MIN_QUERY_RESP_INTERVAL,
237 				    MAX_QUERY_RESP_INTERVAL);
238 				YYERROR;
239 			}
240 			defs->query_resp_interval = $2;
241 		}
242 		| ROBUSTNESS NUMBER {
243 			if ($2 < MIN_ROBUSTNESS || $2 > MAX_ROBUSTNESS) {
244 				yyerror("robustness out of range (%d-%d)",
245 				    MIN_ROBUSTNESS, MAX_ROBUSTNESS);
246 				YYERROR;
247 			}
248 			defs->robustness = $2;
249 		}
250 		| STARTUPQUERYCNT NUMBER {
251 			if ($2 < MIN_STARTUP_QUERY_CNT ||
252 			    $2 > MAX_STARTUP_QUERY_CNT) {
253 				yyerror("startup-query-count out of "
254 				    "range (%d-%d)",
255 				    MIN_STARTUP_QUERY_CNT,
256 				    MAX_STARTUP_QUERY_CNT);
257 				YYERROR;
258 			}
259 			defs->startup_query_cnt = $2;
260 		}
261 		| STARTUPQUERYINTERVAL NUMBER {
262 			if ($2 < MIN_STARTUP_QUERY_INTERVAL ||
263 			    $2 > MAX_STARTUP_QUERY_INTERVAL) {
264 				yyerror("startup-query-interval out of "
265 				    "range (%d-%d)",
266 				    MIN_STARTUP_QUERY_INTERVAL,
267 				    MAX_STARTUP_QUERY_INTERVAL);
268 				YYERROR;
269 			}
270 			defs->startup_query_interval = $2;
271 		}
272 		| IGMPVERSION NUMBER {
273 			if ($2 < MIN_IGMP_VERSION ||
274 			    $2 > MAX_IGMP_VERSION) {
275 				yyerror("igmp-version out of range (%d-%d)",
276 				    MIN_IGMP_VERSION, MAX_IGMP_VERSION);
277 				YYERROR;
278 			}
279 			defs->igmp_version = $2;
280 		}
281 		;
282 
283 optnl		: '\n' optnl
284 		|
285 		;
286 
287 nl		: '\n' optnl		/* one newline or more */
288 		;
289 
290 interface	: INTERFACE STRING	{
291 			struct kif *kif;
292 
293 			if ((kif = kif_findname($2)) == NULL) {
294 				yyerror("unknown interface %s", $2);
295 				free($2);
296 				YYERROR;
297 			}
298 			free($2);
299 			iface = conf_get_if(kif);
300 			if (iface == NULL)
301 				YYERROR;
302 			LIST_INSERT_HEAD(&conf->iface_list, iface, entry);
303 
304 			memcpy(&ifacedefs, defs, sizeof(ifacedefs));
305 			defs = &ifacedefs;
306 		} interface_block {
307 			iface->probe_interval = defs->probe_interval;
308 			iface->query_interval = defs->query_interval;
309 			iface->query_resp_interval = defs->query_resp_interval;
310 			iface->startup_query_interval =
311 			    defs->startup_query_interval;
312 			iface->startup_query_cnt = defs->startup_query_cnt;
313 			iface->last_member_query_interval =
314 			    defs->last_member_query_interval;
315 			iface->last_member_query_cnt =
316 			    defs->last_member_query_cnt;
317 			iface->dead_interval = defs->dead_interval;
318 			iface->metric = defs->metric;
319 			iface->robustness = defs->robustness;
320 			iface->igmp_version = defs->igmp_version;
321 			if (grdefs)
322 				defs = grdefs;
323 			else
324 				defs = &globaldefs;
325 			iface = NULL;
326 		}
327 		;
328 
329 interface_block	: '{' optnl interfaceopts_l '}'
330 		| '{' optnl '}'
331 		|
332 		;
333 
334 interfaceopts_l	: interfaceopts_l interfaceoptsl
335 		| interfaceoptsl
336 		;
337 
338 interfaceoptsl	: PASSIVE nl		{ iface->passive = 1; }
339 		| defaults nl
340 		;
341 
342 group		: GROUP optnl '{' optnl {
343 			memcpy(&groupdefs, defs, sizeof(groupdefs));
344 			grdefs = defs = &groupdefs;
345 		}
346 		    groupopts_l '}' {
347 			grdefs = NULL;
348 			defs = &globaldefs;
349 		}
350 		;
351 
352 groupopts_l	: groupopts_l groupoptsl
353 		| groupoptsl
354 		;
355 
356 groupoptsl	: interface nl
357 		| defaults nl
358 		| error nl
359 		;
360 
361 %%
362 
363 struct keywords {
364 	const char	*k_name;
365 	int		 k_val;
366 };
367 
368 int
369 yyerror(const char *fmt, ...)
370 {
371 	va_list		 ap;
372 	char		*msg;
373 
374 	file->errors++;
375 	va_start(ap, fmt);
376 	if (vasprintf(&msg, fmt, ap) == -1)
377 		fatalx("yyerror vasprintf");
378 	va_end(ap);
379 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
380 	free(msg);
381 	return (0);
382 }
383 
384 int
385 kw_cmp(const void *k, const void *e)
386 {
387 	return (strcmp(k, ((const struct keywords *)e)->k_name));
388 }
389 
390 int
391 lookup(char *s)
392 {
393 	/* this has to be sorted always */
394 	static const struct keywords keywords[] = {
395 		{"fib-update",			FIBUPDATE},
396 		{"group",			GROUP},
397 		{"igmp-version",		IGMPVERSION},
398 		{"interface",			INTERFACE},
399 		{"last-member-query-count",	LASTMEMBERQUERYCNT},
400 		{"last-member-query-interval",	LASTMEMBERQUERYINTERVAL},
401 		{"metric",			METRIC},
402 		{"passive",			PASSIVE},
403 		{"query-interval",		QUERYINTERVAL},
404 		{"query-response-interval",	QUERYRESPINTERVAL},
405 		{"robustness",			ROBUSTNESS},
406 		{"startup-query-count",		STARTUPQUERYCNT},
407 		{"startup-query-interval",	STARTUPQUERYINTERVAL}
408 	};
409 	const struct keywords	*p;
410 
411 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
412 	    sizeof(keywords[0]), kw_cmp);
413 
414 	if (p)
415 		return (p->k_val);
416 	else
417 		return (STRING);
418 }
419 
420 #define MAXPUSHBACK	128
421 
422 u_char	*parsebuf;
423 int	 parseindex;
424 u_char	 pushback_buffer[MAXPUSHBACK];
425 int	 pushback_index = 0;
426 
427 int
428 lgetc(int quotec)
429 {
430 	int		c, next;
431 
432 	if (parsebuf) {
433 		/* Read character from the parsebuffer instead of input. */
434 		if (parseindex >= 0) {
435 			c = parsebuf[parseindex++];
436 			if (c != '\0')
437 				return (c);
438 			parsebuf = NULL;
439 		} else
440 			parseindex++;
441 	}
442 
443 	if (pushback_index)
444 		return (pushback_buffer[--pushback_index]);
445 
446 	if (quotec) {
447 		if ((c = getc(file->stream)) == EOF) {
448 			yyerror("reached end of file while parsing "
449 			    "quoted string");
450 			if (file == topfile || popfile() == EOF)
451 				return (EOF);
452 			return (quotec);
453 		}
454 		return (c);
455 	}
456 
457 	while ((c = getc(file->stream)) == '\\') {
458 		next = getc(file->stream);
459 		if (next != '\n') {
460 			c = next;
461 			break;
462 		}
463 		yylval.lineno = file->lineno;
464 		file->lineno++;
465 	}
466 
467 	while (c == EOF) {
468 		if (file == topfile || popfile() == EOF)
469 			return (EOF);
470 		c = getc(file->stream);
471 	}
472 	return (c);
473 }
474 
475 int
476 lungetc(int c)
477 {
478 	if (c == EOF)
479 		return (EOF);
480 	if (parsebuf) {
481 		parseindex--;
482 		if (parseindex >= 0)
483 			return (c);
484 	}
485 	if (pushback_index < MAXPUSHBACK-1)
486 		return (pushback_buffer[pushback_index++] = c);
487 	else
488 		return (EOF);
489 }
490 
491 int
492 findeol(void)
493 {
494 	int	c;
495 
496 	parsebuf = NULL;
497 
498 	/* skip to either EOF or the first real EOL */
499 	while (1) {
500 		if (pushback_index)
501 			c = pushback_buffer[--pushback_index];
502 		else
503 			c = lgetc(0);
504 		if (c == '\n') {
505 			file->lineno++;
506 			break;
507 		}
508 		if (c == EOF)
509 			break;
510 	}
511 	return (ERROR);
512 }
513 
514 int
515 yylex(void)
516 {
517 	u_char	 buf[8096];
518 	u_char	*p, *val;
519 	int	 quotec, next, c;
520 	int	 token;
521 
522 top:
523 	p = buf;
524 	while ((c = lgetc(0)) == ' ' || c == '\t')
525 		; /* nothing */
526 
527 	yylval.lineno = file->lineno;
528 	if (c == '#')
529 		while ((c = lgetc(0)) != '\n' && c != EOF)
530 			; /* nothing */
531 	if (c == '$' && parsebuf == NULL) {
532 		while (1) {
533 			if ((c = lgetc(0)) == EOF)
534 				return (0);
535 
536 			if (p + 1 >= buf + sizeof(buf) - 1) {
537 				yyerror("string too long");
538 				return (findeol());
539 			}
540 			if (isalnum(c) || c == '_') {
541 				*p++ = c;
542 				continue;
543 			}
544 			*p = '\0';
545 			lungetc(c);
546 			break;
547 		}
548 		val = symget(buf);
549 		if (val == NULL) {
550 			yyerror("macro '%s' not defined", buf);
551 			return (findeol());
552 		}
553 		parsebuf = val;
554 		parseindex = 0;
555 		goto top;
556 	}
557 
558 	switch (c) {
559 	case '\'':
560 	case '"':
561 		quotec = c;
562 		while (1) {
563 			if ((c = lgetc(quotec)) == EOF)
564 				return (0);
565 			if (c == '\n') {
566 				file->lineno++;
567 				continue;
568 			} else if (c == '\\') {
569 				if ((next = lgetc(quotec)) == EOF)
570 					return (0);
571 				if (next == quotec || c == ' ' || c == '\t')
572 					c = next;
573 				else if (next == '\n') {
574 					file->lineno++;
575 					continue;
576 				} else
577 					lungetc(next);
578 			} else if (c == quotec) {
579 				*p = '\0';
580 				break;
581 			} else if (c == '\0') {
582 				yyerror("syntax error");
583 				return (findeol());
584 			}
585 			if (p + 1 >= buf + sizeof(buf) - 1) {
586 				yyerror("string too long");
587 				return (findeol());
588 			}
589 			*p++ = c;
590 		}
591 		yylval.v.string = strdup(buf);
592 		if (yylval.v.string == NULL)
593 			err(1, "yylex: strdup");
594 		return (STRING);
595 	}
596 
597 #define allowed_to_end_number(x) \
598 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
599 
600 	if (c == '-' || isdigit(c)) {
601 		do {
602 			*p++ = c;
603 			if ((unsigned)(p-buf) >= sizeof(buf)) {
604 				yyerror("string too long");
605 				return (findeol());
606 			}
607 		} while ((c = lgetc(0)) != EOF && isdigit(c));
608 		lungetc(c);
609 		if (p == buf + 1 && buf[0] == '-')
610 			goto nodigits;
611 		if (c == EOF || allowed_to_end_number(c)) {
612 			const char *errstr = NULL;
613 
614 			*p = '\0';
615 			yylval.v.number = strtonum(buf, LLONG_MIN,
616 			    LLONG_MAX, &errstr);
617 			if (errstr) {
618 				yyerror("\"%s\" invalid number: %s",
619 				    buf, errstr);
620 				return (findeol());
621 			}
622 			return (NUMBER);
623 		} else {
624 nodigits:
625 			while (p > buf + 1)
626 				lungetc(*--p);
627 			c = *--p;
628 			if (c == '-')
629 				return (c);
630 		}
631 	}
632 
633 #define allowed_in_string(x) \
634 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
635 	x != '{' && x != '}' && \
636 	x != '!' && x != '=' && x != '#' && \
637 	x != ','))
638 
639 	if (isalnum(c) || c == ':' || c == '_') {
640 		do {
641 			*p++ = c;
642 			if ((unsigned)(p-buf) >= sizeof(buf)) {
643 				yyerror("string too long");
644 				return (findeol());
645 			}
646 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
647 		lungetc(c);
648 		*p = '\0';
649 		if ((token = lookup(buf)) == STRING)
650 			if ((yylval.v.string = strdup(buf)) == NULL)
651 				err(1, "yylex: strdup");
652 		return (token);
653 	}
654 	if (c == '\n') {
655 		yylval.lineno = file->lineno;
656 		file->lineno++;
657 	}
658 	if (c == EOF)
659 		return (0);
660 	return (c);
661 }
662 
663 int
664 check_file_secrecy(int fd, const char *fname)
665 {
666 	struct stat	st;
667 
668 	if (fstat(fd, &st)) {
669 		log_warn("cannot stat %s", fname);
670 		return (-1);
671 	}
672 	if (st.st_uid != 0 && st.st_uid != getuid()) {
673 		log_warnx("%s: owner not root or current user", fname);
674 		return (-1);
675 	}
676 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
677 		log_warnx("%s: group writable or world read/writable", fname);
678 		return (-1);
679 	}
680 	return (0);
681 }
682 
683 struct file *
684 pushfile(const char *name, int secret)
685 {
686 	struct file	*nfile;
687 
688 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
689 		log_warn("malloc");
690 		return (NULL);
691 	}
692 	if ((nfile->name = strdup(name)) == NULL) {
693 		log_warn("malloc");
694 		free(nfile);
695 		return (NULL);
696 	}
697 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
698 		log_warn("%s", nfile->name);
699 		free(nfile->name);
700 		free(nfile);
701 		return (NULL);
702 	} else if (secret &&
703 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
704 		fclose(nfile->stream);
705 		free(nfile->name);
706 		free(nfile);
707 		return (NULL);
708 	}
709 	nfile->lineno = 1;
710 	TAILQ_INSERT_TAIL(&files, nfile, entry);
711 	return (nfile);
712 }
713 
714 int
715 popfile(void)
716 {
717 	struct file	*prev;
718 
719 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
720 		prev->errors += file->errors;
721 
722 	TAILQ_REMOVE(&files, file, entry);
723 	fclose(file->stream);
724 	free(file->name);
725 	free(file);
726 	file = prev;
727 	return (file ? 0 : EOF);
728 }
729 
730 struct dvmrpd_conf *
731 parse_config(char *filename, int opts)
732 {
733 	int		 errors = 0;
734 	struct sym	*sym, *next;
735 	struct timeval	 now;
736 
737 	if ((conf = calloc(1, sizeof(struct dvmrpd_conf))) == NULL) {
738 		errx(1, "parse_config calloc");
739 		return (NULL);
740 	}
741 
742 	defs = &globaldefs;
743 	defs->probe_interval = DEFAULT_PROBE_INTERVAL;
744 	defs->last_member_query_cnt = DEFAULT_LAST_MEMBER_QUERY_CNT;
745 	defs->last_member_query_interval = DEFAULT_LAST_MEMBER_QUERY_INTERVAL;
746 	defs->metric = DEFAULT_METRIC;
747 	defs->query_interval = DEFAULT_QUERY_INTERVAL;
748 	defs->query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL;
749 	defs->robustness = DEFAULT_ROBUSTNESS;
750 	defs->startup_query_cnt = DEFAULT_STARTUP_QUERY_CNT;
751 	defs->startup_query_interval = DEFAULT_STARTUP_QUERY_INTERVAL;
752 	defs->igmp_version = DEFAULT_IGMP_VERSION;
753 	defs->dead_interval = NBR_TMOUT;
754 
755 	if ((file = pushfile(filename, 1)) == NULL) {
756 		free(conf);
757 		return (NULL);
758 	}
759 	topfile = file;
760 
761 	gettimeofday(&now, NULL);
762 	conf->gen_id = (u_int32_t)now.tv_sec;	/* for a while after 2038 */
763 	conf->opts = opts;
764 
765 	yyparse();
766 	errors = file->errors;
767 	popfile();
768 
769 	/* Free macros and check which have not been used. */
770 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
771 		next = TAILQ_NEXT(sym, entry);
772 		if ((conf->opts & DVMRPD_OPT_VERBOSE2) && !sym->used)
773 			fprintf(stderr, "warning: macro '%s' not "
774 			    "used\n", sym->nam);
775 		if (!sym->persist) {
776 			free(sym->nam);
777 			free(sym->val);
778 			TAILQ_REMOVE(&symhead, sym, entry);
779 			free(sym);
780 		}
781 	}
782 
783 	if (errors) {
784 		clear_config(conf);
785 		return (NULL);
786 	}
787 
788 	return (conf);
789 }
790 
791 int
792 symset(const char *nam, const char *val, int persist)
793 {
794 	struct sym	*sym;
795 
796 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
797 	    sym = TAILQ_NEXT(sym, entry))
798 		;	/* nothing */
799 
800 	if (sym != NULL) {
801 		if (sym->persist == 1)
802 			return (0);
803 		else {
804 			free(sym->nam);
805 			free(sym->val);
806 			TAILQ_REMOVE(&symhead, sym, entry);
807 			free(sym);
808 		}
809 	}
810 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
811 		return (-1);
812 
813 	sym->nam = strdup(nam);
814 	if (sym->nam == NULL) {
815 		free(sym);
816 		return (-1);
817 	}
818 	sym->val = strdup(val);
819 	if (sym->val == NULL) {
820 		free(sym->nam);
821 		free(sym);
822 		return (-1);
823 	}
824 	sym->used = 0;
825 	sym->persist = persist;
826 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
827 	return (0);
828 }
829 
830 int
831 cmdline_symset(char *s)
832 {
833 	char	*sym, *val;
834 	int	ret;
835 	size_t	len;
836 
837 	if ((val = strrchr(s, '=')) == NULL)
838 		return (-1);
839 
840 	len = strlen(s) - strlen(val) + 1;
841 	if ((sym = malloc(len)) == NULL)
842 		errx(1, "cmdline_symset: malloc");
843 
844 	strlcpy(sym, s, len);
845 
846 	ret = symset(sym, val + 1, 1);
847 	free(sym);
848 
849 	return (ret);
850 }
851 
852 char *
853 symget(const char *nam)
854 {
855 	struct sym	*sym;
856 
857 	TAILQ_FOREACH(sym, &symhead, entry)
858 		if (strcmp(nam, sym->nam) == 0) {
859 			sym->used = 1;
860 			return (sym->val);
861 		}
862 	return (NULL);
863 }
864 
865 struct iface *
866 conf_get_if(struct kif *kif)
867 {
868 	struct iface	*i;
869 
870 	if (kif->ifindex >= MAXVIFS) {
871 		yyerror("interface %s index too large", kif->ifname);
872 		return (NULL);
873 	}
874 
875 	LIST_FOREACH(i, &conf->iface_list, entry)
876 		if (i->ifindex == kif->ifindex) {
877 			yyerror("interface %s already configured",
878 			    kif->ifname);
879 			return (NULL);
880 		}
881 
882 	i = if_new(kif);
883 	i->passive = 0;
884 	i->recv_query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL;
885 
886 	return (i);
887 }
888 
889 void
890 clear_config(struct dvmrpd_conf *xconf)
891 {
892 	/* XXX clear conf */
893 		/* ... */
894 }
895