xref: /openbsd/usr.sbin/rad/parse.y (revision 771fbea0)
1 /*	$OpenBSD: parse.y,v 1.18 2021/03/01 08:05:40 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
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/queue.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30 
31 #include <netinet/in.h>
32 #include <net/if.h>
33 
34 #include <arpa/inet.h>
35 
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <event.h>
40 #include <imsg.h>
41 #include <limits.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <syslog.h>
46 #include <unistd.h>
47 
48 #include "log.h"
49 #include "rad.h"
50 #include "frontend.h"
51 
52 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
53 static struct file {
54 	TAILQ_ENTRY(file)	 entry;
55 	FILE			*stream;
56 	char			*name;
57 	size_t	 		 ungetpos;
58 	size_t			 ungetsize;
59 	u_char			*ungetbuf;
60 	int			 eof_reached;
61 	int			 lineno;
62 	int			 errors;
63 } *file, *topfile;
64 struct file	*pushfile(const char *, int);
65 int		 popfile(void);
66 int		 check_file_secrecy(int, const char *);
67 int		 yyparse(void);
68 int		 yylex(void);
69 int		 yyerror(const char *, ...)
70     __attribute__((__format__ (printf, 1, 2)))
71     __attribute__((__nonnull__ (1)));
72 int		 kw_cmp(const void *, const void *);
73 int		 lookup(char *);
74 int		 igetc(void);
75 int		 lgetc(int);
76 void		 lungetc(int);
77 int		 findeol(void);
78 
79 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
80 struct sym {
81 	TAILQ_ENTRY(sym)	 entry;
82 	int			 used;
83 	int			 persist;
84 	char			*nam;
85 	char			*val;
86 };
87 
88 int	 symset(const char *, const char *, int);
89 char	*symget(const char *);
90 
91 void	 clear_config(struct rad_conf *xconf);
92 
93 static struct rad_conf		*conf;
94 static struct ra_options_conf	*ra_options;
95 static int			 errors;
96 
97 static struct ra_iface_conf	*ra_iface_conf;
98 static struct ra_prefix_conf	*ra_prefix_conf;
99 
100 struct ra_prefix_conf	*conf_get_ra_prefix(struct in6_addr*, int);
101 struct ra_iface_conf	*conf_get_ra_iface(char *);
102 void			 copy_dns_options(const struct ra_options_conf *,
103 			    struct ra_options_conf *);
104 
105 typedef struct {
106 	union {
107 		int64_t		 number;
108 		char		*string;
109 	} v;
110 	int lineno;
111 } YYSTYPE;
112 
113 %}
114 
115 %token	RA_IFACE YES NO INCLUDE ERROR
116 %token	DEFAULT ROUTER HOP LIMIT MANAGED ADDRESS
117 %token	CONFIGURATION OTHER LIFETIME REACHABLE TIME RETRANS TIMER
118 %token	AUTO PREFIX VALID PREFERRED LIFETIME ONLINK AUTONOMOUS
119 %token	ADDRESS_CONFIGURATION DNS NAMESERVER SEARCH MTU
120 
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 include '\n'
130 		| grammar '\n'
131 		| grammar { ra_options = &conf->ra_options; } conf_main '\n'
132 		| grammar varset '\n'
133 		| grammar ra_iface '\n'
134 		| grammar error '\n'		{ file->errors++; }
135 		;
136 
137 include		: INCLUDE STRING		{
138 			struct file	*nfile;
139 
140 			if ((nfile = pushfile($2, 0)) == NULL) {
141 				yyerror("failed to include file %s", $2);
142 				free($2);
143 				YYERROR;
144 			}
145 			free($2);
146 
147 			file = nfile;
148 			lungetc('\n');
149 		}
150 		;
151 
152 string		: string STRING	{
153 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
154 				free($1);
155 				free($2);
156 				yyerror("string: asprintf");
157 				YYERROR;
158 			}
159 			free($1);
160 			free($2);
161 		}
162 		| STRING
163 		;
164 
165 yesno		: YES	{ $$ = 1; }
166 		| NO	{ $$ = 0; }
167 		;
168 
169 varset		: STRING '=' string		{
170 			char *s = $1;
171 			if (cmd_opts & OPT_VERBOSE)
172 				printf("%s = \"%s\"\n", $1, $3);
173 			while (*s++) {
174 				if (isspace((unsigned char)*s)) {
175 					yyerror("macro name cannot contain "
176 					    "whitespace");
177 					free($1);
178 					free($3);
179 					YYERROR;
180 				}
181 			}
182 			if (symset($1, $3, 0) == -1)
183 				fatal("cannot store variable");
184 			free($1);
185 			free($3);
186 		}
187 		;
188 
189 conf_main	: ra_opt_block {
190 			ra_options = &conf->ra_options;
191 		}
192 		;
193 
194 ra_opt_block	: DEFAULT ROUTER yesno {
195 			ra_options->dfr = $3;
196 		}
197 		| HOP LIMIT NUMBER {
198 			ra_options->cur_hl = $3;
199 		}
200 		| MANAGED ADDRESS CONFIGURATION yesno {
201 			ra_options->m_flag = $4;
202 		}
203 		| OTHER CONFIGURATION yesno {
204 			ra_options->o_flag = $3;
205 		}
206 		| ROUTER LIFETIME NUMBER {
207 			ra_options->router_lifetime = $3;
208 		}
209 		| REACHABLE TIME NUMBER {
210 			ra_options->reachable_time = $3;
211 		}
212 		| RETRANS TIMER NUMBER {
213 			ra_options->retrans_timer = $3;
214 		}
215 		| MTU NUMBER {
216 			ra_options->mtu = $2;
217 		}
218 		| DNS dns_block
219 		;
220 
221 optnl		: '\n' optnl		/* zero or more newlines */
222 		| /*empty*/
223 		;
224 
225 nl		: '\n' optnl		/* one or more newlines */
226 		;
227 
228 ra_iface	: RA_IFACE STRING {
229 			ra_iface_conf = conf_get_ra_iface($2);
230 			/* set auto prefix defaults */
231 			ra_iface_conf->autoprefix = conf_get_ra_prefix(NULL, 0);
232 			ra_options = &ra_iface_conf->ra_options;
233 		} ra_iface_block {
234 			ra_iface_conf = NULL;
235 			ra_options = &conf->ra_options;
236 		}
237 		;
238 
239 ra_iface_block	: '{' optnl ra_ifaceopts_l '}'
240 		| '{' optnl '}'
241 		| /* empty */
242 		;
243 
244 ra_ifaceopts_l	: ra_ifaceopts_l ra_ifaceoptsl nl
245 		| ra_ifaceoptsl optnl
246 		;
247 
248 ra_ifaceoptsl	: NO AUTO PREFIX {
249 			free(ra_iface_conf->autoprefix);
250 			ra_iface_conf->autoprefix = NULL;
251 		}
252 		| AUTO PREFIX {
253 			if (ra_iface_conf->autoprefix == NULL)
254 				ra_iface_conf->autoprefix =
255 				    conf_get_ra_prefix(NULL, 0);
256 			ra_prefix_conf = ra_iface_conf->autoprefix;
257 		} ra_prefix_block {
258 			ra_prefix_conf = NULL;
259 		}
260 		| PREFIX STRING {
261 			struct in6_addr	 addr;
262 			int		 prefixlen;
263 			char		*p;
264 			const char	*errstr;
265 
266 			memset(&addr, 0, sizeof(addr));
267 			p = strchr($2, '/');
268 			if (p != NULL) {
269 				*p++ = '\0';
270 				prefixlen = strtonum(p, 0, 128, &errstr);
271 				if (errstr != NULL) {
272 					yyerror("error parsing prefix "
273 					    "\"%s/%s\"", $2, p);
274 					free($2);
275 					YYERROR;
276 				}
277 			} else
278 				prefixlen = 64;
279 			if(inet_pton(AF_INET6, $2, &addr) == 0) {
280 				yyerror("error parsing prefix \"%s/%d\"", $2,
281 				    prefixlen);
282 				free($2);
283 				YYERROR;
284 			}
285 			mask_prefix(&addr, prefixlen);
286 			ra_prefix_conf = conf_get_ra_prefix(&addr, prefixlen);
287 		} ra_prefix_block {
288 			ra_prefix_conf = NULL;
289 		}
290 		| ra_opt_block
291 		;
292 
293 ra_prefix_block	: '{' optnl ra_prefixopts_l '}'
294 		| '{' optnl '}'
295 		| /* empty */
296 		;
297 
298 ra_prefixopts_l	: ra_prefixopts_l ra_prefixoptsl nl
299 		| ra_prefixoptsl optnl
300 		;
301 
302 ra_prefixoptsl	: VALID LIFETIME NUMBER {
303 			ra_prefix_conf->vltime = $3;
304 		}
305 		| PREFERRED LIFETIME NUMBER {
306 			ra_prefix_conf->pltime = $3;
307 		}
308 		| ONLINK yesno {
309 			ra_prefix_conf->lflag = $2;
310 		}
311 		| AUTONOMOUS ADDRESS_CONFIGURATION yesno {
312 			ra_prefix_conf->aflag = $3;
313 		}
314 		;
315 dns_block	: '{' optnl dnsopts_l '}'
316 		| '{' optnl '}'
317 		| /* empty */
318 		;
319 
320 dnsopts_l	: dnsopts_l dnsoptsl nl
321 		| dnsoptsl optnl
322 		;
323 
324 dnsoptsl	: LIFETIME NUMBER {
325 			ra_options->rdns_lifetime = $2;
326 		}
327 		| NAMESERVER nserver_block
328 		| SEARCH search_block
329 		;
330 nserver_block	: '{' optnl nserveropts_l '}'
331 			| '{' optnl '}'
332 			| nserveroptsl
333 			| /* empty */
334 			;
335 
336 nserveropts_l	: nserveropts_l nserveroptsl optnl
337 		| nserveroptsl optnl
338 		;
339 
340 nserveroptsl	: STRING {
341 			struct ra_rdnss_conf	*ra_rdnss_conf;
342 			struct in6_addr		 addr;
343 
344 			memset(&addr, 0, sizeof(addr));
345 			if (inet_pton(AF_INET6, $1, &addr)
346 			    != 1) {
347 				yyerror("error parsing nameserver address %s",
348 				    $1);
349 				free($1);
350 				YYERROR;
351 			}
352 			if ((ra_rdnss_conf = calloc(1, sizeof(*ra_rdnss_conf)))
353 			    == NULL)
354 				err(1, "%s", __func__);
355 			memcpy(&ra_rdnss_conf->rdnss, &addr, sizeof(addr));
356 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_rdnss_list,
357 			    ra_rdnss_conf, entry);
358 			ra_options->rdnss_count++;
359 		}
360 		;
361 search_block	: '{' optnl searchopts_l '}'
362 		| '{' optnl '}'
363 		| searchoptsl
364 		| /* empty */
365 		;
366 
367 searchopts_l	: searchopts_l searchoptsl optnl
368 		| searchoptsl optnl
369 		;
370 
371 searchoptsl	: STRING {
372 			struct ra_dnssl_conf	*ra_dnssl_conf;
373 			size_t			 len;
374 
375 			if ((ra_dnssl_conf = calloc(1,
376 			    sizeof(*ra_dnssl_conf))) == NULL)
377 				err(1, "%s", __func__);
378 
379 			if ((len = strlcpy(ra_dnssl_conf->search, $1,
380 			    sizeof(ra_dnssl_conf->search))) >=
381 			    sizeof(ra_dnssl_conf->search)) {
382 				yyerror("search string too long");
383 				free($1);
384 				YYERROR;
385 			}
386 			if (ra_dnssl_conf->search[len] != '.') {
387 				if ((len = strlcat(ra_dnssl_conf->search, ".",
388 				    sizeof(ra_dnssl_conf->search))) >
389 				    sizeof(ra_dnssl_conf->search)) {
390 					yyerror("search string too long");
391 					free($1);
392 					YYERROR;
393 				}
394 			}
395 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
396 			    ra_dnssl_conf, entry);
397 			ra_options->dnssl_len += len + 1;
398 		}
399 		;
400 %%
401 
402 struct keywords {
403 	const char	*k_name;
404 	int		 k_val;
405 };
406 
407 int
408 yyerror(const char *fmt, ...)
409 {
410 	va_list		 ap;
411 	char		*msg;
412 
413 	file->errors++;
414 	va_start(ap, fmt);
415 	if (vasprintf(&msg, fmt, ap) == -1)
416 		fatalx("yyerror vasprintf");
417 	va_end(ap);
418 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
419 	free(msg);
420 	return (0);
421 }
422 
423 int
424 kw_cmp(const void *k, const void *e)
425 {
426 	return (strcmp(k, ((const struct keywords *)e)->k_name));
427 }
428 
429 int
430 lookup(char *s)
431 {
432 	/* This has to be sorted always. */
433 	static const struct keywords keywords[] = {
434 		{"address",		ADDRESS},
435 		{"address-configuration",	ADDRESS_CONFIGURATION},
436 		{"auto",		AUTO},
437 		{"autonomous",		AUTONOMOUS},
438 		{"configuration",	CONFIGURATION},
439 		{"default",		DEFAULT},
440 		{"dns",			DNS},
441 		{"hop",			HOP},
442 		{"include",		INCLUDE},
443 		{"interface",		RA_IFACE},
444 		{"lifetime",		LIFETIME},
445 		{"limit",		LIMIT},
446 		{"managed",		MANAGED},
447 		{"mtu",			MTU},
448 		{"nameserver",		NAMESERVER},
449 		{"no",			NO},
450 		{"on-link",		ONLINK},
451 		{"other",		OTHER},
452 		{"preferred",		PREFERRED},
453 		{"prefix",		PREFIX},
454 		{"reachable",		REACHABLE},
455 		{"retrans",		RETRANS},
456 		{"router",		ROUTER},
457 		{"search",		SEARCH},
458 		{"time",		TIME},
459 		{"timer",		TIMER},
460 		{"valid",		VALID},
461 		{"yes",			YES},
462 	};
463 	const struct keywords	*p;
464 
465 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
466 	    sizeof(keywords[0]), kw_cmp);
467 
468 	if (p)
469 		return (p->k_val);
470 	else
471 		return (STRING);
472 }
473 
474 #define START_EXPAND	1
475 #define DONE_EXPAND	2
476 
477 static int	expanding;
478 
479 int
480 igetc(void)
481 {
482 	int	c;
483 
484 	while (1) {
485 		if (file->ungetpos > 0)
486 			c = file->ungetbuf[--file->ungetpos];
487 		else
488 			c = getc(file->stream);
489 
490 		if (c == START_EXPAND)
491 			expanding = 1;
492 		else if (c == DONE_EXPAND)
493 			expanding = 0;
494 		else
495 			break;
496 	}
497 	return (c);
498 }
499 
500 int
501 lgetc(int quotec)
502 {
503 	int		c, next;
504 
505 	if (quotec) {
506 		if ((c = igetc()) == EOF) {
507 			yyerror("reached end of file while parsing "
508 			    "quoted string");
509 			if (file == topfile || popfile() == EOF)
510 				return (EOF);
511 			return (quotec);
512 		}
513 		return (c);
514 	}
515 
516 	while ((c = igetc()) == '\\') {
517 		next = igetc();
518 		if (next != '\n') {
519 			c = next;
520 			break;
521 		}
522 		yylval.lineno = file->lineno;
523 		file->lineno++;
524 	}
525 
526 	if (c == EOF) {
527 		/*
528 		 * Fake EOL when hit EOF for the first time. This gets line
529 		 * count right if last line in included file is syntactically
530 		 * invalid and has no newline.
531 		 */
532 		if (file->eof_reached == 0) {
533 			file->eof_reached = 1;
534 			return ('\n');
535 		}
536 		while (c == EOF) {
537 			if (file == topfile || popfile() == EOF)
538 				return (EOF);
539 			c = igetc();
540 		}
541 	}
542 	return (c);
543 }
544 
545 void
546 lungetc(int c)
547 {
548 	if (c == EOF)
549 		return;
550 
551 	if (file->ungetpos >= file->ungetsize) {
552 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
553 		if (p == NULL)
554 			err(1, "lungetc");
555 		file->ungetbuf = p;
556 		file->ungetsize *= 2;
557 	}
558 	file->ungetbuf[file->ungetpos++] = c;
559 }
560 
561 int
562 findeol(void)
563 {
564 	int	c;
565 
566 	/* Skip to either EOF or the first real EOL. */
567 	while (1) {
568 		c = lgetc(0);
569 		if (c == '\n') {
570 			file->lineno++;
571 			break;
572 		}
573 		if (c == EOF)
574 			break;
575 	}
576 	return (ERROR);
577 }
578 
579 int
580 yylex(void)
581 {
582 	unsigned char	 buf[8096];
583 	unsigned char	*p, *val;
584 	int		 quotec, next, c;
585 	int		 token;
586 
587 top:
588 	p = buf;
589 	while ((c = lgetc(0)) == ' ' || c == '\t')
590 		; /* nothing */
591 
592 	yylval.lineno = file->lineno;
593 	if (c == '#')
594 		while ((c = lgetc(0)) != '\n' && c != EOF)
595 			; /* nothing */
596 	if (c == '$' && !expanding) {
597 		while (1) {
598 			if ((c = lgetc(0)) == EOF)
599 				return (0);
600 
601 			if (p + 1 >= buf + sizeof(buf) - 1) {
602 				yyerror("string too long");
603 				return (findeol());
604 			}
605 			if (isalnum(c) || c == '_') {
606 				*p++ = c;
607 				continue;
608 			}
609 			*p = '\0';
610 			lungetc(c);
611 			break;
612 		}
613 		val = symget(buf);
614 		if (val == NULL) {
615 			yyerror("macro '%s' not defined", buf);
616 			return (findeol());
617 		}
618 		p = val + strlen(val) - 1;
619 		lungetc(DONE_EXPAND);
620 		while (p >= val) {
621 			lungetc(*p);
622 			p--;
623 		}
624 		lungetc(START_EXPAND);
625 		goto top;
626 	}
627 
628 	switch (c) {
629 	case '\'':
630 	case '"':
631 		quotec = c;
632 		while (1) {
633 			if ((c = lgetc(quotec)) == EOF)
634 				return (0);
635 			if (c == '\n') {
636 				file->lineno++;
637 				continue;
638 			} else if (c == '\\') {
639 				if ((next = lgetc(quotec)) == EOF)
640 					return (0);
641 				if (next == quotec || next == ' ' ||
642 				    next == '\t')
643 					c = next;
644 				else if (next == '\n') {
645 					file->lineno++;
646 					continue;
647 				} else
648 					lungetc(next);
649 			} else if (c == quotec) {
650 				*p = '\0';
651 				break;
652 			} else if (c == '\0') {
653 				yyerror("syntax error");
654 				return (findeol());
655 			}
656 			if (p + 1 >= buf + sizeof(buf) - 1) {
657 				yyerror("string too long");
658 				return (findeol());
659 			}
660 			*p++ = c;
661 		}
662 		yylval.v.string = strdup(buf);
663 		if (yylval.v.string == NULL)
664 			err(1, "yylex: strdup");
665 		return (STRING);
666 	}
667 
668 #define allowed_to_end_number(x) \
669 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
670 
671 	if (c == '-' || isdigit(c)) {
672 		do {
673 			*p++ = c;
674 			if ((size_t)(p-buf) >= sizeof(buf)) {
675 				yyerror("string too long");
676 				return (findeol());
677 			}
678 		} while ((c = lgetc(0)) != EOF && isdigit(c));
679 		lungetc(c);
680 		if (p == buf + 1 && buf[0] == '-')
681 			goto nodigits;
682 		if (c == EOF || allowed_to_end_number(c)) {
683 			const char *errstr = NULL;
684 
685 			*p = '\0';
686 			yylval.v.number = strtonum(buf, LLONG_MIN,
687 			    LLONG_MAX, &errstr);
688 			if (errstr) {
689 				yyerror("\"%s\" invalid number: %s",
690 				    buf, errstr);
691 				return (findeol());
692 			}
693 			return (NUMBER);
694 		} else {
695 nodigits:
696 			while (p > buf + 1)
697 				lungetc(*--p);
698 			c = *--p;
699 			if (c == '-')
700 				return (c);
701 		}
702 	}
703 
704 #define allowed_in_string(x) \
705 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
706 	x != '{' && x != '}' && \
707 	x != '!' && x != '=' && x != '#' && \
708 	x != ','))
709 
710 	if (isalnum(c) || c == ':' || c == '_') {
711 		do {
712 			*p++ = c;
713 			if ((size_t)(p-buf) >= sizeof(buf)) {
714 				yyerror("string too long");
715 				return (findeol());
716 			}
717 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
718 		lungetc(c);
719 		*p = '\0';
720 		if ((token = lookup(buf)) == STRING)
721 			if ((yylval.v.string = strdup(buf)) == NULL)
722 				err(1, "yylex: strdup");
723 		return (token);
724 	}
725 	if (c == '\n') {
726 		yylval.lineno = file->lineno;
727 		file->lineno++;
728 	}
729 	if (c == EOF)
730 		return (0);
731 	return (c);
732 }
733 
734 int
735 check_file_secrecy(int fd, const char *fname)
736 {
737 	struct stat	st;
738 
739 	if (fstat(fd, &st)) {
740 		log_warn("cannot stat %s", fname);
741 		return (-1);
742 	}
743 	if (st.st_uid != 0 && st.st_uid != getuid()) {
744 		log_warnx("%s: owner not root or current user", fname);
745 		return (-1);
746 	}
747 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
748 		log_warnx("%s: group writable or world read/writable", fname);
749 		return (-1);
750 	}
751 	return (0);
752 }
753 
754 struct file *
755 pushfile(const char *name, int secret)
756 {
757 	struct file	*nfile;
758 
759 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
760 		log_warn("calloc");
761 		return (NULL);
762 	}
763 	if ((nfile->name = strdup(name)) == NULL) {
764 		log_warn("strdup");
765 		free(nfile);
766 		return (NULL);
767 	}
768 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
769 		log_warn("%s", nfile->name);
770 		free(nfile->name);
771 		free(nfile);
772 		return (NULL);
773 	} else if (secret &&
774 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
775 		fclose(nfile->stream);
776 		free(nfile->name);
777 		free(nfile);
778 		return (NULL);
779 	}
780 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
781 	nfile->ungetsize = 16;
782 	nfile->ungetbuf = malloc(nfile->ungetsize);
783 	if (nfile->ungetbuf == NULL) {
784 		log_warn("malloc");
785 		fclose(nfile->stream);
786 		free(nfile->name);
787 		free(nfile);
788 		return (NULL);
789 	}
790 	TAILQ_INSERT_TAIL(&files, nfile, entry);
791 	return (nfile);
792 }
793 
794 int
795 popfile(void)
796 {
797 	struct file	*prev;
798 
799 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
800 		prev->errors += file->errors;
801 
802 	TAILQ_REMOVE(&files, file, entry);
803 	fclose(file->stream);
804 	free(file->name);
805 	free(file->ungetbuf);
806 	free(file);
807 	file = prev;
808 	return (file ? 0 : EOF);
809 }
810 
811 struct rad_conf *
812 parse_config(char *filename)
813 {
814 	struct sym		*sym, *next;
815 	struct ra_iface_conf	*iface;
816 
817 	conf = config_new_empty();
818 	ra_options = NULL;
819 
820 	file = pushfile(filename, 0);
821 	if (file == NULL) {
822 		free(conf);
823 		return (NULL);
824 	}
825 	topfile = file;
826 
827 	yyparse();
828 	errors = file->errors;
829 	popfile();
830 
831 	/* Free macros and check which have not been used. */
832 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
833 		if ((cmd_opts & OPT_VERBOSE2) && !sym->used)
834 			fprintf(stderr, "warning: macro '%s' not used\n",
835 			    sym->nam);
836 		if (!sym->persist) {
837 			free(sym->nam);
838 			free(sym->val);
839 			TAILQ_REMOVE(&symhead, sym, entry);
840 			free(sym);
841 		}
842 	}
843 
844 	if (errors) {
845 		clear_config(conf);
846 		return (NULL);
847 	}
848 
849 	if (!SIMPLEQ_EMPTY(&conf->ra_options.ra_rdnss_list) ||
850 	    !SIMPLEQ_EMPTY(&conf->ra_options.ra_dnssl_list)) {
851 		SIMPLEQ_FOREACH(iface, &conf->ra_iface_list, entry)
852 			copy_dns_options(&conf->ra_options,
853 			    &iface->ra_options);
854 	}
855 
856 	return (conf);
857 }
858 
859 void
860 copy_dns_options(const struct ra_options_conf *src, struct ra_options_conf *dst)
861 {
862 	struct ra_rdnss_conf	*ra_rdnss, *nra_rdnss;
863 	struct ra_dnssl_conf	*ra_dnssl, *nra_dnssl;
864 
865 	if (SIMPLEQ_EMPTY(&dst->ra_rdnss_list)) {
866 		SIMPLEQ_FOREACH(ra_rdnss, &src->ra_rdnss_list, entry) {
867 			if ((nra_rdnss = calloc(1, sizeof(*nra_rdnss))) == NULL)
868 				errx(1, "%s", __func__);
869 			memcpy(nra_rdnss, ra_rdnss, sizeof(*nra_rdnss));
870 			SIMPLEQ_INSERT_TAIL(&dst->ra_rdnss_list, nra_rdnss,
871 			    entry);
872 		}
873 		dst->rdnss_count = src->rdnss_count;
874 	}
875 	if (SIMPLEQ_EMPTY(&dst->ra_dnssl_list)) {
876 		SIMPLEQ_FOREACH(ra_dnssl, &src->ra_dnssl_list, entry) {
877 			if ((nra_dnssl = calloc(1, sizeof(*nra_dnssl))) == NULL)
878 				errx(1, "%s", __func__);
879 			memcpy(nra_dnssl, ra_dnssl, sizeof(*nra_dnssl));
880 			SIMPLEQ_INSERT_TAIL(&dst->ra_dnssl_list, nra_dnssl,
881 			    entry);
882 		}
883 		dst->dnssl_len = src->dnssl_len;
884 	}
885 }
886 
887 int
888 symset(const char *nam, const char *val, int persist)
889 {
890 	struct sym	*sym;
891 
892 	TAILQ_FOREACH(sym, &symhead, entry) {
893 		if (strcmp(nam, sym->nam) == 0)
894 			break;
895 	}
896 
897 	if (sym != NULL) {
898 		if (sym->persist == 1)
899 			return (0);
900 		else {
901 			free(sym->nam);
902 			free(sym->val);
903 			TAILQ_REMOVE(&symhead, sym, entry);
904 			free(sym);
905 		}
906 	}
907 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
908 		return (-1);
909 
910 	sym->nam = strdup(nam);
911 	if (sym->nam == NULL) {
912 		free(sym);
913 		return (-1);
914 	}
915 	sym->val = strdup(val);
916 	if (sym->val == NULL) {
917 		free(sym->nam);
918 		free(sym);
919 		return (-1);
920 	}
921 	sym->used = 0;
922 	sym->persist = persist;
923 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
924 	return (0);
925 }
926 
927 int
928 cmdline_symset(char *s)
929 {
930 	char	*sym, *val;
931 	int	ret;
932 
933 	if ((val = strrchr(s, '=')) == NULL)
934 		return (-1);
935 	sym = strndup(s, val - s);
936 	if (sym == NULL)
937 		errx(1, "%s: strndup", __func__);
938 	ret = symset(sym, val + 1, 1);
939 	free(sym);
940 
941 	return (ret);
942 }
943 
944 char *
945 symget(const char *nam)
946 {
947 	struct sym	*sym;
948 
949 	TAILQ_FOREACH(sym, &symhead, entry) {
950 		if (strcmp(nam, sym->nam) == 0) {
951 			sym->used = 1;
952 			return (sym->val);
953 		}
954 	}
955 	return (NULL);
956 }
957 
958 struct ra_prefix_conf *
959 conf_get_ra_prefix(struct in6_addr *addr, int prefixlen)
960 {
961 	struct ra_prefix_conf	*prefix;
962 
963 	if (addr == NULL) {
964 		if (ra_iface_conf->autoprefix != NULL)
965 			return (ra_iface_conf->autoprefix);
966 	} else {
967 		SIMPLEQ_FOREACH(prefix, &ra_iface_conf->ra_prefix_list, entry) {
968 			if (prefix->prefixlen == prefixlen && memcmp(addr,
969 			    &prefix->prefix, sizeof(*addr)) == 0)
970 				return (prefix);
971 		}
972 	}
973 
974 	prefix = calloc(1, sizeof(*prefix));
975 	if (prefix == NULL)
976 		errx(1, "%s: calloc", __func__);
977 	prefix->prefixlen = prefixlen;
978 	prefix->vltime = ADV_VALID_LIFETIME;
979 	prefix->pltime = ADV_PREFERRED_LIFETIME;
980 	prefix->lflag = 1;
981 	prefix->aflag = 1;
982 
983 	if (addr == NULL)
984 		ra_iface_conf->autoprefix = prefix;
985 	else {
986 		prefix->prefix = *addr;
987 		SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list, prefix,
988 		    entry);
989 	}
990 
991 	return (prefix);
992 }
993 
994 struct ra_iface_conf *
995 conf_get_ra_iface(char *name)
996 {
997 	struct ra_iface_conf	*iface;
998 	size_t			 n;
999 
1000 	SIMPLEQ_FOREACH(iface, &conf->ra_iface_list, entry) {
1001 		if (strcmp(name, iface->name) == 0)
1002 			return (iface);
1003 	}
1004 
1005 	iface = calloc(1, sizeof(*iface));
1006 	if (iface == NULL)
1007 		errx(1, "%s: calloc", __func__);
1008 	n = strlcpy(iface->name, name, sizeof(iface->name));
1009 	if (n >= sizeof(iface->name))
1010 		errx(1, "%s: name too long", __func__);
1011 
1012 	/* Inherit attributes set in global section. */
1013 	iface->ra_options = conf->ra_options;
1014 
1015 	SIMPLEQ_INIT(&iface->ra_prefix_list);
1016 	SIMPLEQ_INIT(&iface->ra_options.ra_rdnss_list);
1017 	iface->ra_options.rdnss_count = 0;
1018 	SIMPLEQ_INIT(&iface->ra_options.ra_dnssl_list);
1019 	iface->ra_options.dnssl_len = 0;
1020 
1021 	SIMPLEQ_INSERT_TAIL(&conf->ra_iface_list, iface, entry);
1022 
1023 	return (iface);
1024 }
1025 
1026 void
1027 clear_config(struct rad_conf *xconf)
1028 {
1029 	struct ra_iface_conf	*iface;
1030 
1031 	free_dns_options(&xconf->ra_options);
1032 
1033 	while((iface = SIMPLEQ_FIRST(&xconf->ra_iface_list)) != NULL) {
1034 		SIMPLEQ_REMOVE_HEAD(&xconf->ra_iface_list, entry);
1035 		free_ra_iface_conf(iface);
1036 	}
1037 
1038 	free(xconf);
1039 }
1040