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