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