xref: /openbsd/usr.sbin/lpd/parse.y (revision 952f302f)
1 /*	$OpenBSD: parse.y,v 1.8 2021/10/15 15:01:28 naddy Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@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/socket.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <sys/un.h>
30 
31 #include <net/if.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <ifaddrs.h>
39 #include <inttypes.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 
45 #include "lpd.h"
46 
47 #include "log.h"
48 
49 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
50 static struct file {
51 	TAILQ_ENTRY(file)	 entry;
52 	FILE			*stream;
53 	char			*name;
54 	int			 lineno;
55 	int			 errors;
56 } *file, *topfile;
57 struct file	*pushfile(const char *, int);
58 int		 popfile(void);
59 int		 check_file_secrecy(int, const char *);
60 int		 yyparse(void);
61 int		 yylex(void);
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 int		 yyerror(const char *, ...)
68     __attribute__((__format__ (printf, 1, 2)))
69     __attribute__((__nonnull__ (1)));
70 
71 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
72 struct sym {
73 	TAILQ_ENTRY(sym)	 entry;
74 	int			 used;
75 	int			 persist;
76 	char			*nam;
77 	char			*val;
78 };
79 int		 symset(const char *, const char *, int);
80 char		*symget(const char *);
81 
82 static int		 errors = 0;
83 
84 struct lpd_conf		*conf = NULL;
85 
86 enum listen_options {
87 	LO_FAMILY	= 0x000001,
88 	LO_PORT		= 0x000002,
89 };
90 
91 static struct listen_opts {
92 	char	       *ifx;
93 	int		family;
94 	int		proto;
95 	in_port_t	port;
96 	uint32_t       	options;
97 } listen_opts;
98 
99 static void	config_free(struct lpd_conf *);
100 static void	create_listeners(struct listen_opts *);
101 static void	config_listener(struct listener *, struct listen_opts *);
102 static int	local(struct listen_opts *);
103 static int	host_v4(struct listen_opts *);
104 static int	host_v6(struct listen_opts *);
105 static int	host_dns(struct listen_opts *);
106 static int	interface(struct listen_opts *);
107 static int	is_if_in_group(const char *, const char *);
108 
109 typedef struct {
110 	union {
111 		int64_t		 number;
112 		char		*string;
113 		struct host	*host;
114 	} v;
115 	int lineno;
116 } YYSTYPE;
117 
118 %}
119 
120 %token	ERROR ARROW INCLUDE
121 %token	LISTEN ON PORT INET4 INET6 LOCAL SOCKET
122 %token	<v.string>	STRING
123 %token  <v.number>	NUMBER
124 %type	<v.number>	family_inet portno
125 
126 %%
127 
128 grammar		: /* empty */
129 		| grammar '\n'
130 		| grammar include '\n'
131 		| grammar varset '\n'
132 		| grammar main '\n'
133 		| grammar error '\n'		{ file->errors++; }
134 		;
135 
136 include		: INCLUDE STRING		{
137 			struct file	*nfile;
138 
139 			if ((nfile = pushfile($2, 0)) == NULL) {
140 				yyerror("failed to include file %s", $2);
141 				free($2);
142 				YYERROR;
143 			}
144 			free($2);
145 
146 			file = nfile;
147 			lungetc('\n');
148 		}
149 		;
150 
151 varset		: STRING '=' STRING		{
152 			char *s = $1;
153 			while (*s++) {
154 				if (isspace((unsigned char)*s)) {
155 					yyerror("macro name cannot contain "
156 					    "whitespace");
157 					free($1);
158 					free($3);
159 					YYERROR;
160 				}
161 			}
162 			if (symset($1, $3, 0) == -1)
163 				fatal("cannot store variable");
164 			free($1);
165 			free($3);
166 		}
167 		;
168 
169 portno		: STRING {
170 			struct servent	*servent;
171 			servent = getservbyname($1, "tcp");
172 			if (servent == NULL) {
173 				yyerror("invalid port: %s", $1);
174 				free($1);
175 				YYERROR;
176 			}
177 			free($1);
178 			$$ = ntohs(servent->s_port);
179 		}
180 		| NUMBER {
181 			if ($1 <= 0 || $1 > (int)USHRT_MAX) {
182 				yyerror("invalid port: %" PRId64, $1);
183 				YYERROR;
184 			}
185 			$$ = $1;
186 		}
187 		;
188 
189 family_inet	: INET4 { $$ = AF_INET; }
190 		| INET6 { $$ = AF_INET6; }
191 		;
192 
193 opt_listen	: family_inet {
194 			if (listen_opts.options & LO_FAMILY) {
195 				yyerror("address family already specified");
196 				YYERROR;
197 			}
198 			listen_opts.options |= LO_FAMILY;
199 			listen_opts.family = $1;
200 		}
201 		| PORT portno {
202 			if (listen_opts.options & LO_PORT) {
203 				yyerror("port already specified");
204 				YYERROR;
205 			}
206 			listen_opts.options |= LO_PORT;
207 			listen_opts.port = htons($2);
208 		}
209 		;
210 
211 listener	: opt_listen listener
212 		| /* empty */ {
213 			create_listeners(&listen_opts);
214 		}
215 		;
216 
217 main		: LISTEN ON STRING {
218 			memset(&listen_opts, 0, sizeof listen_opts);
219 			listen_opts.ifx = $3;
220 			listen_opts.family = AF_UNSPEC;
221 			listen_opts.proto = PROTO_LPR;
222 			listen_opts.port = htons(PORT_LPR);
223 		} listener
224 		;
225 %%
226 
227 struct keywords {
228 	const char	*k_name;
229 	int		 k_val;
230 };
231 
232 int
233 yyerror(const char *fmt, ...)
234 {
235 	va_list		 ap;
236 	char		*msg;
237 
238 	file->errors++;
239 	va_start(ap, fmt);
240 	if (vasprintf(&msg, fmt, ap) == -1)
241 		fatalx("yyerror vasprintf");
242 	va_end(ap);
243 	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
244 	free(msg);
245 	return (0);
246 }
247 
248 int
249 kw_cmp(const void *k, const void *e)
250 {
251 	return (strcmp(k, ((const struct keywords *)e)->k_name));
252 }
253 
254 int
255 lookup(char *s)
256 {
257 	/* this has to be sorted always */
258 	static const struct keywords keywords[] = {
259 		{ "include",		INCLUDE },
260 		{ "inet4",		INET4 },
261 		{ "inet6",		INET6 },
262 		{ "listen",		LISTEN },
263 		{ "on",			ON },
264 		{ "port",		PORT },
265 		{ "socket",		SOCKET },
266 	};
267 	const struct keywords	*p;
268 
269 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
270 	    sizeof(keywords[0]), kw_cmp);
271 
272 	if (p)
273 		return (p->k_val);
274 	else
275 		return (STRING);
276 }
277 
278 #define MAXPUSHBACK	128
279 
280 char	*parsebuf;
281 int	 parseindex;
282 char	 pushback_buffer[MAXPUSHBACK];
283 int	 pushback_index = 0;
284 
285 int
286 lgetc(int quotec)
287 {
288 	int		c, next;
289 
290 	if (parsebuf) {
291 		/* Read character from the parsebuffer instead of input. */
292 		if (parseindex >= 0) {
293 			c = (unsigned char)parsebuf[parseindex++];
294 			if (c != '\0')
295 				return (c);
296 			parsebuf = NULL;
297 		} else
298 			parseindex++;
299 	}
300 
301 	if (pushback_index)
302 		return ((unsigned char)pushback_buffer[--pushback_index]);
303 
304 	if (quotec) {
305 		if ((c = getc(file->stream)) == EOF) {
306 			yyerror("reached end of file while parsing "
307 			    "quoted string");
308 			if (file == topfile || popfile() == EOF)
309 				return (EOF);
310 			return (quotec);
311 		}
312 		return (c);
313 	}
314 
315 	while ((c = getc(file->stream)) == '\\') {
316 		next = getc(file->stream);
317 		if (next != '\n') {
318 			c = next;
319 			break;
320 		}
321 		yylval.lineno = file->lineno;
322 		file->lineno++;
323 	}
324 
325 	while (c == EOF) {
326 		if (file == topfile || popfile() == EOF)
327 			return (EOF);
328 		c = getc(file->stream);
329 	}
330 	return (c);
331 }
332 
333 int
334 lungetc(int c)
335 {
336 	if (c == EOF)
337 		return (EOF);
338 	if (parsebuf) {
339 		parseindex--;
340 		if (parseindex >= 0)
341 			return (c);
342 	}
343 	if (pushback_index + 1 >= MAXPUSHBACK)
344 		return (EOF);
345 	pushback_buffer[pushback_index++] = c;
346 	return (c);
347 }
348 
349 int
350 findeol(void)
351 {
352 	int	c;
353 
354 	parsebuf = NULL;
355 	pushback_index = 0;
356 
357 	/* skip to either EOF or the first real EOL */
358 	while (1) {
359 		c = lgetc(0);
360 		if (c == '\n') {
361 			file->lineno++;
362 			break;
363 		}
364 		if (c == EOF)
365 			break;
366 	}
367 	return (ERROR);
368 }
369 
370 int
371 yylex(void)
372 {
373 	char	 buf[8096];
374 	char	*p, *val;
375 	int	 quotec, next, c;
376 	int	 token;
377 
378 top:
379 	p = buf;
380 	while ((c = lgetc(0)) == ' ' || c == '\t')
381 		; /* nothing */
382 
383 	yylval.lineno = file->lineno;
384 	if (c == '#')
385 		while ((c = lgetc(0)) != '\n' && c != EOF)
386 			; /* nothing */
387 	if (c == '$' && parsebuf == NULL) {
388 		while (1) {
389 			if ((c = lgetc(0)) == EOF)
390 				return (0);
391 
392 			if (p + 1 >= buf + sizeof(buf) - 1) {
393 				yyerror("string too long");
394 				return (findeol());
395 			}
396 			if (isalnum(c) || c == '_') {
397 				*p++ = c;
398 				continue;
399 			}
400 			*p = '\0';
401 			lungetc(c);
402 			break;
403 		}
404 		val = symget(buf);
405 		if (val == NULL) {
406 			yyerror("macro '%s' not defined", buf);
407 			return (findeol());
408 		}
409 		parsebuf = val;
410 		parseindex = 0;
411 		goto top;
412 	}
413 
414 	switch (c) {
415 	case '\'':
416 	case '"':
417 		quotec = c;
418 		while (1) {
419 			if ((c = lgetc(quotec)) == EOF)
420 				return (0);
421 			if (c == '\n') {
422 				file->lineno++;
423 				continue;
424 			} else if (c == '\\') {
425 				if ((next = lgetc(quotec)) == EOF)
426 					return (0);
427 				if (next == quotec || next == ' ' ||
428 				    next == '\t')
429 					c = next;
430 				else if (next == '\n') {
431 					file->lineno++;
432 					continue;
433 				} else
434 					lungetc(next);
435 			} else if (c == quotec) {
436 				*p = '\0';
437 				break;
438 			} else if (c == '\0') {
439 				yyerror("syntax error");
440 				return (findeol());
441 			}
442 			if (p + 1 >= buf + sizeof(buf) - 1) {
443 				yyerror("string too long");
444 				return (findeol());
445 			}
446 			*p++ = c;
447 		}
448 		yylval.v.string = strdup(buf);
449 		if (yylval.v.string == NULL)
450 			err(1, "%s", __func__);
451 		return (STRING);
452 	}
453 
454 #define allowed_to_end_number(x) \
455 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
456 
457 	if (c == '-' || isdigit(c)) {
458 		do {
459 			*p++ = c;
460 			if ((size_t)(p-buf) >= sizeof(buf)) {
461 				yyerror("string too long");
462 				return (findeol());
463 			}
464 		} while ((c = lgetc(0)) != EOF && isdigit(c));
465 		lungetc(c);
466 		if (p == buf + 1 && buf[0] == '-')
467 			goto nodigits;
468 		if (c == EOF || allowed_to_end_number(c)) {
469 			const char *errstr = NULL;
470 
471 			*p = '\0';
472 			yylval.v.number = strtonum(buf, LLONG_MIN,
473 			    LLONG_MAX, &errstr);
474 			if (errstr) {
475 				yyerror("\"%s\" invalid number: %s",
476 				    buf, errstr);
477 				return (findeol());
478 			}
479 			return (NUMBER);
480 		} else {
481 nodigits:
482 			while (p > buf + 1)
483 				lungetc((unsigned char)*--p);
484 			c = (unsigned char)*--p;
485 			if (c == '-')
486 				return (c);
487 		}
488 	}
489 
490 	if (c == '=') {
491 		if ((c = lgetc(0)) != EOF && c == '>')
492 			return (ARROW);
493 		lungetc(c);
494 		c = '=';
495 	}
496 
497 #define allowed_in_string(x) \
498 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
499 	x != '{' && x != '}' && x != '<' && x != '>' && \
500 	x != '!' && x != '=' && x != '#' && \
501 	x != ','))
502 
503 	if (isalnum(c) || c == ':' || c == '_') {
504 		do {
505 			*p++ = c;
506 			if ((size_t)(p-buf) >= sizeof(buf)) {
507 				yyerror("string too long");
508 				return (findeol());
509 			}
510 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
511 		lungetc(c);
512 		*p = '\0';
513 		if ((token = lookup(buf)) == STRING)
514 			if ((yylval.v.string = strdup(buf)) == NULL)
515 				err(1, "%s", __func__);
516 		return (token);
517 	}
518 	if (c == '\n') {
519 		yylval.lineno = file->lineno;
520 		file->lineno++;
521 	}
522 	if (c == EOF)
523 		return (0);
524 	return (c);
525 }
526 
527 int
528 check_file_secrecy(int fd, const char *fname)
529 {
530 	struct stat	st;
531 
532 	if (fstat(fd, &st)) {
533 		log_warn("warn: cannot stat %s", fname);
534 		return (-1);
535 	}
536 	if (st.st_uid != 0 && st.st_uid != getuid()) {
537 		log_warnx("warn: %s: owner not root or current user", fname);
538 		return (-1);
539 	}
540 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
541 		log_warnx("warn: %s: group/world readable/writeable", fname);
542 		return (-1);
543 	}
544 	return (0);
545 }
546 
547 struct file *
548 pushfile(const char *name, int secret)
549 {
550 	struct file	*nfile;
551 
552 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
553 		log_warn("%s", __func__);
554 		return (NULL);
555 	}
556 	if ((nfile->name = strdup(name)) == NULL) {
557 		log_warn("%s", __func__);
558 		free(nfile);
559 		return (NULL);
560 	}
561 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
562 		log_warn("%s: %s", __func__, nfile->name);
563 		free(nfile->name);
564 		free(nfile);
565 		return (NULL);
566 	} else if (secret &&
567 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
568 		fclose(nfile->stream);
569 		free(nfile->name);
570 		free(nfile);
571 		return (NULL);
572 	}
573 	nfile->lineno = 1;
574 	TAILQ_INSERT_TAIL(&files, nfile, entry);
575 	return (nfile);
576 }
577 
578 int
579 popfile(void)
580 {
581 	struct file	*prev;
582 
583 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
584 		prev->errors += file->errors;
585 
586 	TAILQ_REMOVE(&files, file, entry);
587 	fclose(file->stream);
588 	free(file->name);
589 	free(file);
590 	file = prev;
591 	return (file ? 0 : EOF);
592 }
593 
594 struct lpd_conf *
595 parse_config(const char *filename, int verbose)
596 {
597 	struct sym     *sym, *next;
598 
599 	conf = calloc(1, sizeof(*conf));
600 	if (conf == NULL)
601 		return NULL;
602 	TAILQ_INIT(&conf->listeners);
603 
604 	errors = 0;
605 
606 	if ((file = pushfile(filename, 0)) == NULL) {
607 		config_free(conf);
608 		return NULL;
609 	}
610 	topfile = file;
611 
612 	/*
613 	 * parse configuration
614 	 */
615 	setservent(1);
616 	yyparse();
617 	errors = file->errors;
618 	popfile();
619 	endservent();
620 
621 	/* Free macros and check which have not been used. */
622 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
623 		if ((verbose) && !sym->used)
624 			log_warnx("warning: macro '%s' not used\n", sym->nam);
625 		if (!sym->persist) {
626 			free(sym->nam);
627 			free(sym->val);
628 			TAILQ_REMOVE(&symhead, sym, entry);
629 			free(sym);
630 		}
631 	}
632 
633 	if (errors) {
634 		config_free(conf);
635 		return NULL;
636 	}
637 
638 	return conf;
639 }
640 
641 int
642 symset(const char *nam, const char *val, int persist)
643 {
644 	struct sym	*sym;
645 
646 	TAILQ_FOREACH(sym, &symhead, entry) {
647 		if (strcmp(nam, sym->nam) == 0)
648 			break;
649 	}
650 
651 	if (sym != NULL) {
652 		if (sym->persist == 1)
653 			return (0);
654 		else {
655 			free(sym->nam);
656 			free(sym->val);
657 			TAILQ_REMOVE(&symhead, sym, entry);
658 			free(sym);
659 		}
660 	}
661 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
662 		return (-1);
663 
664 	sym->nam = strdup(nam);
665 	if (sym->nam == NULL) {
666 		free(sym);
667 		return (-1);
668 	}
669 	sym->val = strdup(val);
670 	if (sym->val == NULL) {
671 		free(sym->nam);
672 		free(sym);
673 		return (-1);
674 	}
675 	sym->used = 0;
676 	sym->persist = persist;
677 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
678 	return (0);
679 }
680 
681 int
682 cmdline_symset(char *s)
683 {
684 	char	*sym, *val;
685 	int	ret;
686 
687 	if ((val = strrchr(s, '=')) == NULL)
688 		return (-1);
689 	sym = strndup(s, val - s);
690 	if (sym == NULL)
691 		errx(1, "%s: strndup", __func__);
692 	ret = symset(sym, val + 1, 1);
693 	free(sym);
694 
695 	return (ret);
696 }
697 
698 char *
699 symget(const char *nam)
700 {
701 	struct sym	*sym;
702 
703 	TAILQ_FOREACH(sym, &symhead, entry) {
704 		if (strcmp(nam, sym->nam) == 0) {
705 			sym->used = 1;
706 			return (sym->val);
707 		}
708 	}
709 	return (NULL);
710 }
711 
712 static void
713 config_free(struct lpd_conf *c)
714 {
715 	struct listener *l;
716 
717 	while ((l = TAILQ_FIRST(&c->listeners))) {
718 		TAILQ_REMOVE(&c->listeners, l, entry);
719 		free(l);
720 	}
721 	free(c);
722 }
723 
724 static void
725 create_listeners(struct listen_opts *lo)
726 {
727 	if (local(lo))
728 		return;
729 	if (interface(lo))
730 		return;
731 	if (host_v4(lo))
732 		return;
733 	if (host_v6(lo))
734 		return;
735 	if (host_dns(lo))
736 		return;
737 
738 	errx(1, "invalid virtual ip or interface: %s", lo->ifx);
739 }
740 
741 static void
742 config_listener(struct listener *l,  struct listen_opts *lo)
743 {
744 	l->sock = -1;
745 	l->proto = lo->proto;
746 
747 	TAILQ_INSERT_TAIL(&conf->listeners, l, entry);
748 }
749 
750 static int
751 local(struct listen_opts *lo)
752 {
753 	struct sockaddr_un	*sun;
754 	struct listener		*h;
755 
756 	if (lo->family != AF_UNSPEC && lo->family != AF_LOCAL)
757 		return 0;
758 
759 	if (lo->ifx[0] != '/')
760 		return 0;
761 
762 	h = calloc(1, sizeof(*h));
763 	sun = (struct sockaddr_un *)&h->ss;
764 	sun->sun_len = sizeof(*sun);
765 	sun->sun_family = AF_LOCAL;
766 	if (strlcpy(sun->sun_path, lo->ifx, sizeof(sun->sun_path))
767 	    >= sizeof(sun->sun_path))
768 		fatalx("path too long");
769 
770 	config_listener(h,  lo);
771 
772 	return (1);
773 }
774 
775 static int
776 host_v4(struct listen_opts *lo)
777 {
778 	struct in_addr		 ina;
779 	struct sockaddr_in	*sain;
780 	struct listener		*h;
781 
782 	if (lo->family != AF_UNSPEC && lo->family != AF_INET)
783 		return (0);
784 
785 	memset(&ina, 0, sizeof(ina));
786 	if (inet_pton(AF_INET, lo->ifx, &ina) != 1)
787 		return (0);
788 
789 	h = calloc(1, sizeof(*h));
790 	sain = (struct sockaddr_in *)&h->ss;
791 	sain->sin_len = sizeof(struct sockaddr_in);
792 	sain->sin_family = AF_INET;
793 	sain->sin_addr.s_addr = ina.s_addr;
794 	sain->sin_port = lo->port;
795 
796 	config_listener(h,  lo);
797 
798 	return (1);
799 }
800 
801 static int
802 host_v6(struct listen_opts *lo)
803 {
804 	struct in6_addr		 ina6;
805 	struct sockaddr_in6	*sin6;
806 	struct listener		*h;
807 
808 	if (lo->family != AF_UNSPEC && lo->family != AF_INET6)
809 		return (0);
810 
811 	memset(&ina6, 0, sizeof(ina6));
812 	if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1)
813 		return (0);
814 
815 	h = calloc(1, sizeof(*h));
816 	sin6 = (struct sockaddr_in6 *)&h->ss;
817 	sin6->sin6_len = sizeof(struct sockaddr_in6);
818 	sin6->sin6_family = AF_INET6;
819 	sin6->sin6_port = lo->port;
820 	memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
821 
822 	config_listener(h,  lo);
823 
824 	return (1);
825 }
826 
827 static int
828 host_dns(struct listen_opts *lo)
829 {
830 	struct addrinfo		 hints, *res0, *res;
831 	int			 error, cnt = 0;
832 	struct sockaddr_in	*sain;
833 	struct sockaddr_in6	*sin6;
834 	struct listener		*h;
835 
836 	memset(&hints, 0, sizeof(hints));
837 	hints.ai_family = lo->family;
838 	hints.ai_socktype = SOCK_STREAM;
839 	hints.ai_flags = AI_ADDRCONFIG;
840 	error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
841 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
842 		return (0);
843 	if (error) {
844 		log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
845 		    gai_strerror(error));
846 		return (-1);
847 	}
848 
849 	for (res = res0; res; res = res->ai_next) {
850 		if (res->ai_family != AF_INET &&
851 		    res->ai_family != AF_INET6)
852 			continue;
853 		h = calloc(1, sizeof(*h));
854 		h->ss.ss_family = res->ai_family;
855 		if (res->ai_family == AF_INET) {
856 			sain = (struct sockaddr_in *)&h->ss;
857 			sain->sin_len = sizeof(struct sockaddr_in);
858 			sain->sin_addr.s_addr = ((struct sockaddr_in *)
859 			    res->ai_addr)->sin_addr.s_addr;
860 			sain->sin_port = lo->port;
861 		} else {
862 			sin6 = (struct sockaddr_in6 *)&h->ss;
863 			sin6->sin6_len = sizeof(struct sockaddr_in6);
864 			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
865 			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
866 			sin6->sin6_port = lo->port;
867 		}
868 
869 		config_listener(h, lo);
870 
871 		cnt++;
872 	}
873 
874 	freeaddrinfo(res0);
875 	return (cnt);
876 }
877 
878 static int
879 interface(struct listen_opts *lo)
880 {
881 	struct ifaddrs *ifap, *p;
882 	struct sockaddr_in	*sain;
883 	struct sockaddr_in6	*sin6;
884 	struct listener		*h;
885 	int			ret = 0;
886 
887 	if (getifaddrs(&ifap) == -1)
888 		fatal("getifaddrs");
889 
890 	for (p = ifap; p != NULL; p = p->ifa_next) {
891 		if (p->ifa_addr == NULL)
892 			continue;
893 		if (strcmp(p->ifa_name, lo->ifx) != 0 &&
894 		    !is_if_in_group(p->ifa_name, lo->ifx))
895 			continue;
896 		if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
897 			continue;
898 
899 		h = calloc(1, sizeof(*h));
900 
901 		switch (p->ifa_addr->sa_family) {
902 		case AF_INET:
903 			sain = (struct sockaddr_in *)&h->ss;
904 			*sain = *(struct sockaddr_in *)p->ifa_addr;
905 			sain->sin_len = sizeof(struct sockaddr_in);
906 			sain->sin_port = lo->port;
907 			break;
908 
909 		case AF_INET6:
910 			sin6 = (struct sockaddr_in6 *)&h->ss;
911 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
912 			sin6->sin6_len = sizeof(struct sockaddr_in6);
913 			sin6->sin6_port = lo->port;
914 			break;
915 
916 		default:
917 			free(h);
918 			continue;
919 		}
920 
921 		config_listener(h, lo);
922 		ret = 1;
923 	}
924 
925 	freeifaddrs(ifap);
926 
927 	return ret;
928 }
929 
930 static int
931 is_if_in_group(const char *ifname, const char *groupname)
932 {
933         unsigned int		 len;
934         struct ifgroupreq        ifgr;
935         struct ifg_req          *ifg;
936 	int			 s;
937 	int			 ret = 0;
938 
939 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
940 		err(1, "socket");
941 
942         memset(&ifgr, 0, sizeof(ifgr));
943         if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ)
944 		errx(1, "interface name too large");
945 
946         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
947                 if (errno == EINVAL || errno == ENOTTY)
948 			goto end;
949 		err(1, "SIOCGIFGROUP");
950         }
951 
952         len = ifgr.ifgr_len;
953         ifgr.ifgr_groups = calloc(len/sizeof(struct ifg_req),
954 		sizeof(struct ifg_req));
955         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
956                 err(1, "SIOCGIFGROUP");
957 
958         ifg = ifgr.ifgr_groups;
959         for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
960                 len -= sizeof(struct ifg_req);
961 		if (strcmp(ifg->ifgrq_group, groupname) == 0) {
962 			ret = 1;
963 			break;
964 		}
965         }
966         free(ifgr.ifgr_groups);
967 
968 end:
969 	close(s);
970 	return ret;
971 }
972