xref: /openbsd-src/usr.sbin/lpd/parse.y (revision 08f6ba1906d01c4a24fa700d568400f71bcf9611)
1*08f6ba19Snaddy /*	$OpenBSD: parse.y,v 1.8 2021/10/15 15:01:28 naddy Exp $	*/
23b188dabSeric 
33b188dabSeric /*
43b188dabSeric  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53b188dabSeric  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
63b188dabSeric  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
73b188dabSeric  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
83b188dabSeric  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
93b188dabSeric  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
103b188dabSeric  *
113b188dabSeric  * Permission to use, copy, modify, and distribute this software for any
123b188dabSeric  * purpose with or without fee is hereby granted, provided that the above
133b188dabSeric  * copyright notice and this permission notice appear in all copies.
143b188dabSeric  *
153b188dabSeric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
163b188dabSeric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
173b188dabSeric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
183b188dabSeric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
193b188dabSeric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
203b188dabSeric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
213b188dabSeric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
223b188dabSeric  */
233b188dabSeric 
243b188dabSeric %{
253b188dabSeric #include <sys/types.h>
263b188dabSeric #include <sys/socket.h>
273b188dabSeric #include <sys/stat.h>
283b188dabSeric #include <sys/ioctl.h>
293b188dabSeric #include <sys/un.h>
303b188dabSeric 
313b188dabSeric #include <net/if.h>
323b188dabSeric #include <netinet/in.h>
333b188dabSeric #include <arpa/inet.h>
343b188dabSeric 
353b188dabSeric #include <ctype.h>
363b188dabSeric #include <err.h>
373b188dabSeric #include <errno.h>
383b188dabSeric #include <ifaddrs.h>
393b188dabSeric #include <inttypes.h>
403b188dabSeric #include <limits.h>
413b188dabSeric #include <stdio.h>
423b188dabSeric #include <stdlib.h>
433b188dabSeric #include <unistd.h>
443b188dabSeric 
453b188dabSeric #include "lpd.h"
463b188dabSeric 
473b188dabSeric #include "log.h"
483b188dabSeric 
493b188dabSeric TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
503b188dabSeric static struct file {
513b188dabSeric 	TAILQ_ENTRY(file)	 entry;
523b188dabSeric 	FILE			*stream;
533b188dabSeric 	char			*name;
543b188dabSeric 	int			 lineno;
553b188dabSeric 	int			 errors;
563b188dabSeric } *file, *topfile;
573b188dabSeric struct file	*pushfile(const char *, int);
583b188dabSeric int		 popfile(void);
593b188dabSeric int		 check_file_secrecy(int, const char *);
603b188dabSeric int		 yyparse(void);
613b188dabSeric int		 yylex(void);
623b188dabSeric int		 kw_cmp(const void *, const void *);
633b188dabSeric int		 lookup(char *);
643b188dabSeric int		 lgetc(int);
653b188dabSeric int		 lungetc(int);
663b188dabSeric int		 findeol(void);
673b188dabSeric int		 yyerror(const char *, ...)
683b188dabSeric     __attribute__((__format__ (printf, 1, 2)))
693b188dabSeric     __attribute__((__nonnull__ (1)));
703b188dabSeric 
713b188dabSeric TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
723b188dabSeric struct sym {
733b188dabSeric 	TAILQ_ENTRY(sym)	 entry;
743b188dabSeric 	int			 used;
753b188dabSeric 	int			 persist;
763b188dabSeric 	char			*nam;
773b188dabSeric 	char			*val;
783b188dabSeric };
793b188dabSeric int		 symset(const char *, const char *, int);
803b188dabSeric char		*symget(const char *);
813b188dabSeric 
823b188dabSeric static int		 errors = 0;
833b188dabSeric 
843b188dabSeric struct lpd_conf		*conf = NULL;
853b188dabSeric 
863b188dabSeric enum listen_options {
873b188dabSeric 	LO_FAMILY	= 0x000001,
883b188dabSeric 	LO_PORT		= 0x000002,
893b188dabSeric };
903b188dabSeric 
913b188dabSeric static struct listen_opts {
923b188dabSeric 	char	       *ifx;
933b188dabSeric 	int		family;
943b188dabSeric 	int		proto;
953b188dabSeric 	in_port_t	port;
963b188dabSeric 	uint32_t       	options;
973b188dabSeric } listen_opts;
983b188dabSeric 
993b188dabSeric static void	config_free(struct lpd_conf *);
1003b188dabSeric static void	create_listeners(struct listen_opts *);
1013b188dabSeric static void	config_listener(struct listener *, struct listen_opts *);
1023b188dabSeric static int	local(struct listen_opts *);
1033b188dabSeric static int	host_v4(struct listen_opts *);
1043b188dabSeric static int	host_v6(struct listen_opts *);
1053b188dabSeric static int	host_dns(struct listen_opts *);
1063b188dabSeric static int	interface(struct listen_opts *);
1073b188dabSeric static int	is_if_in_group(const char *, const char *);
1083b188dabSeric 
1093b188dabSeric typedef struct {
1103b188dabSeric 	union {
1113b188dabSeric 		int64_t		 number;
1123b188dabSeric 		char		*string;
1133b188dabSeric 		struct host	*host;
1143b188dabSeric 	} v;
1153b188dabSeric 	int lineno;
1163b188dabSeric } YYSTYPE;
1173b188dabSeric 
1183b188dabSeric %}
1193b188dabSeric 
1203b188dabSeric %token	ERROR ARROW INCLUDE
1213b188dabSeric %token	LISTEN ON PORT INET4 INET6 LOCAL SOCKET
1223b188dabSeric %token	<v.string>	STRING
1233b188dabSeric %token  <v.number>	NUMBER
1243b188dabSeric %type	<v.number>	family_inet portno
1253b188dabSeric 
1263b188dabSeric %%
1273b188dabSeric 
1283b188dabSeric grammar		: /* empty */
1293b188dabSeric 		| grammar '\n'
1303b188dabSeric 		| grammar include '\n'
1313b188dabSeric 		| grammar varset '\n'
1323b188dabSeric 		| grammar main '\n'
1333b188dabSeric 		| grammar error '\n'		{ file->errors++; }
1343b188dabSeric 		;
1353b188dabSeric 
1363b188dabSeric include		: INCLUDE STRING		{
1373b188dabSeric 			struct file	*nfile;
1383b188dabSeric 
1393b188dabSeric 			if ((nfile = pushfile($2, 0)) == NULL) {
1403b188dabSeric 				yyerror("failed to include file %s", $2);
1413b188dabSeric 				free($2);
1423b188dabSeric 				YYERROR;
1433b188dabSeric 			}
1443b188dabSeric 			free($2);
1453b188dabSeric 
1463b188dabSeric 			file = nfile;
1473b188dabSeric 			lungetc('\n');
1483b188dabSeric 		}
1493b188dabSeric 		;
1503b188dabSeric 
1513b188dabSeric varset		: STRING '=' STRING		{
1523b188dabSeric 			char *s = $1;
1533b188dabSeric 			while (*s++) {
1543b188dabSeric 				if (isspace((unsigned char)*s)) {
1553b188dabSeric 					yyerror("macro name cannot contain "
1563b188dabSeric 					    "whitespace");
1573b188dabSeric 					free($1);
1583b188dabSeric 					free($3);
1593b188dabSeric 					YYERROR;
1603b188dabSeric 				}
1613b188dabSeric 			}
1623b188dabSeric 			if (symset($1, $3, 0) == -1)
1633b188dabSeric 				fatal("cannot store variable");
1643b188dabSeric 			free($1);
1653b188dabSeric 			free($3);
1663b188dabSeric 		}
1673b188dabSeric 		;
1683b188dabSeric 
1693b188dabSeric portno		: STRING {
1703b188dabSeric 			struct servent	*servent;
1713b188dabSeric 			servent = getservbyname($1, "tcp");
1723b188dabSeric 			if (servent == NULL) {
1733b188dabSeric 				yyerror("invalid port: %s", $1);
1743b188dabSeric 				free($1);
1753b188dabSeric 				YYERROR;
1763b188dabSeric 			}
1773b188dabSeric 			free($1);
1783b188dabSeric 			$$ = ntohs(servent->s_port);
1793b188dabSeric 		}
1803b188dabSeric 		| NUMBER {
1813b188dabSeric 			if ($1 <= 0 || $1 > (int)USHRT_MAX) {
1823b188dabSeric 				yyerror("invalid port: %" PRId64, $1);
1833b188dabSeric 				YYERROR;
1843b188dabSeric 			}
1853b188dabSeric 			$$ = $1;
1863b188dabSeric 		}
1873b188dabSeric 		;
1883b188dabSeric 
1893b188dabSeric family_inet	: INET4 { $$ = AF_INET; }
1903b188dabSeric 		| INET6 { $$ = AF_INET6; }
1913b188dabSeric 		;
1923b188dabSeric 
1933b188dabSeric opt_listen	: family_inet {
1943b188dabSeric 			if (listen_opts.options & LO_FAMILY) {
1953b188dabSeric 				yyerror("address family already specified");
1963b188dabSeric 				YYERROR;
1973b188dabSeric 			}
1983b188dabSeric 			listen_opts.options |= LO_FAMILY;
1993b188dabSeric 			listen_opts.family = $1;
2003b188dabSeric 		}
2013b188dabSeric 		| PORT portno {
2023b188dabSeric 			if (listen_opts.options & LO_PORT) {
2033b188dabSeric 				yyerror("port already specified");
2043b188dabSeric 				YYERROR;
2053b188dabSeric 			}
2063b188dabSeric 			listen_opts.options |= LO_PORT;
2073b188dabSeric 			listen_opts.port = htons($2);
2083b188dabSeric 		}
2093b188dabSeric 		;
2103b188dabSeric 
2113b188dabSeric listener	: opt_listen listener
2123b188dabSeric 		| /* empty */ {
2133b188dabSeric 			create_listeners(&listen_opts);
2143b188dabSeric 		}
2153b188dabSeric 		;
2163b188dabSeric 
2173b188dabSeric main		: LISTEN ON STRING {
2183b188dabSeric 			memset(&listen_opts, 0, sizeof listen_opts);
2193b188dabSeric 			listen_opts.ifx = $3;
2203b188dabSeric 			listen_opts.family = AF_UNSPEC;
2213b188dabSeric 			listen_opts.proto = PROTO_LPR;
2223b188dabSeric 			listen_opts.port = htons(PORT_LPR);
2233b188dabSeric 		} listener
2243b188dabSeric 		;
2253b188dabSeric %%
2263b188dabSeric 
2273b188dabSeric struct keywords {
2283b188dabSeric 	const char	*k_name;
2293b188dabSeric 	int		 k_val;
2303b188dabSeric };
2313b188dabSeric 
2323b188dabSeric int
yyerror(const char * fmt,...)2333b188dabSeric yyerror(const char *fmt, ...)
2343b188dabSeric {
2353b188dabSeric 	va_list		 ap;
2363b188dabSeric 	char		*msg;
2373b188dabSeric 
2383b188dabSeric 	file->errors++;
2393b188dabSeric 	va_start(ap, fmt);
2403b188dabSeric 	if (vasprintf(&msg, fmt, ap) == -1)
2413b188dabSeric 		fatalx("yyerror vasprintf");
2423b188dabSeric 	va_end(ap);
2433b188dabSeric 	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
2443b188dabSeric 	free(msg);
2453b188dabSeric 	return (0);
2463b188dabSeric }
2473b188dabSeric 
2483b188dabSeric int
kw_cmp(const void * k,const void * e)2493b188dabSeric kw_cmp(const void *k, const void *e)
2503b188dabSeric {
2513b188dabSeric 	return (strcmp(k, ((const struct keywords *)e)->k_name));
2523b188dabSeric }
2533b188dabSeric 
2543b188dabSeric int
lookup(char * s)2553b188dabSeric lookup(char *s)
2563b188dabSeric {
2573b188dabSeric 	/* this has to be sorted always */
2583b188dabSeric 	static const struct keywords keywords[] = {
2593b188dabSeric 		{ "include",		INCLUDE },
2603b188dabSeric 		{ "inet4",		INET4 },
2613b188dabSeric 		{ "inet6",		INET6 },
2623b188dabSeric 		{ "listen",		LISTEN },
2633b188dabSeric 		{ "on",			ON },
2643b188dabSeric 		{ "port",		PORT },
2653b188dabSeric 		{ "socket",		SOCKET },
2663b188dabSeric 	};
2673b188dabSeric 	const struct keywords	*p;
2683b188dabSeric 
2693b188dabSeric 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
2703b188dabSeric 	    sizeof(keywords[0]), kw_cmp);
2713b188dabSeric 
2723b188dabSeric 	if (p)
2733b188dabSeric 		return (p->k_val);
2743b188dabSeric 	else
2753b188dabSeric 		return (STRING);
2763b188dabSeric }
2773b188dabSeric 
2783b188dabSeric #define MAXPUSHBACK	128
2793b188dabSeric 
280*08f6ba19Snaddy char	*parsebuf;
2813b188dabSeric int	 parseindex;
282*08f6ba19Snaddy char	 pushback_buffer[MAXPUSHBACK];
2833b188dabSeric int	 pushback_index = 0;
2843b188dabSeric 
2853b188dabSeric int
lgetc(int quotec)2863b188dabSeric lgetc(int quotec)
2873b188dabSeric {
2883b188dabSeric 	int		c, next;
2893b188dabSeric 
2903b188dabSeric 	if (parsebuf) {
2913b188dabSeric 		/* Read character from the parsebuffer instead of input. */
2923b188dabSeric 		if (parseindex >= 0) {
293*08f6ba19Snaddy 			c = (unsigned char)parsebuf[parseindex++];
2943b188dabSeric 			if (c != '\0')
2953b188dabSeric 				return (c);
2963b188dabSeric 			parsebuf = NULL;
2973b188dabSeric 		} else
2983b188dabSeric 			parseindex++;
2993b188dabSeric 	}
3003b188dabSeric 
3013b188dabSeric 	if (pushback_index)
302*08f6ba19Snaddy 		return ((unsigned char)pushback_buffer[--pushback_index]);
3033b188dabSeric 
3043b188dabSeric 	if (quotec) {
3053b188dabSeric 		if ((c = getc(file->stream)) == EOF) {
3063b188dabSeric 			yyerror("reached end of file while parsing "
3073b188dabSeric 			    "quoted string");
3083b188dabSeric 			if (file == topfile || popfile() == EOF)
3093b188dabSeric 				return (EOF);
3103b188dabSeric 			return (quotec);
3113b188dabSeric 		}
3123b188dabSeric 		return (c);
3133b188dabSeric 	}
3143b188dabSeric 
3153b188dabSeric 	while ((c = getc(file->stream)) == '\\') {
3163b188dabSeric 		next = getc(file->stream);
3173b188dabSeric 		if (next != '\n') {
3183b188dabSeric 			c = next;
3193b188dabSeric 			break;
3203b188dabSeric 		}
3213b188dabSeric 		yylval.lineno = file->lineno;
3223b188dabSeric 		file->lineno++;
3233b188dabSeric 	}
3243b188dabSeric 
3253b188dabSeric 	while (c == EOF) {
3263b188dabSeric 		if (file == topfile || popfile() == EOF)
3273b188dabSeric 			return (EOF);
3283b188dabSeric 		c = getc(file->stream);
3293b188dabSeric 	}
3303b188dabSeric 	return (c);
3313b188dabSeric }
3323b188dabSeric 
3333b188dabSeric int
lungetc(int c)3343b188dabSeric lungetc(int c)
3353b188dabSeric {
3363b188dabSeric 	if (c == EOF)
3373b188dabSeric 		return (EOF);
3383b188dabSeric 	if (parsebuf) {
3393b188dabSeric 		parseindex--;
3403b188dabSeric 		if (parseindex >= 0)
3413b188dabSeric 			return (c);
3423b188dabSeric 	}
343*08f6ba19Snaddy 	if (pushback_index + 1 >= MAXPUSHBACK)
3443b188dabSeric 		return (EOF);
345*08f6ba19Snaddy 	pushback_buffer[pushback_index++] = c;
346*08f6ba19Snaddy 	return (c);
3473b188dabSeric }
3483b188dabSeric 
3493b188dabSeric int
findeol(void)3503b188dabSeric findeol(void)
3513b188dabSeric {
3523b188dabSeric 	int	c;
3533b188dabSeric 
3543b188dabSeric 	parsebuf = NULL;
3553b188dabSeric 	pushback_index = 0;
3563b188dabSeric 
3573b188dabSeric 	/* skip to either EOF or the first real EOL */
3583b188dabSeric 	while (1) {
3593b188dabSeric 		c = lgetc(0);
3603b188dabSeric 		if (c == '\n') {
3613b188dabSeric 			file->lineno++;
3623b188dabSeric 			break;
3633b188dabSeric 		}
3643b188dabSeric 		if (c == EOF)
3653b188dabSeric 			break;
3663b188dabSeric 	}
3673b188dabSeric 	return (ERROR);
3683b188dabSeric }
3693b188dabSeric 
3703b188dabSeric int
yylex(void)3713b188dabSeric yylex(void)
3723b188dabSeric {
373*08f6ba19Snaddy 	char	 buf[8096];
374*08f6ba19Snaddy 	char	*p, *val;
3753b188dabSeric 	int	 quotec, next, c;
3763b188dabSeric 	int	 token;
3773b188dabSeric 
3783b188dabSeric top:
3793b188dabSeric 	p = buf;
3803b188dabSeric 	while ((c = lgetc(0)) == ' ' || c == '\t')
3813b188dabSeric 		; /* nothing */
3823b188dabSeric 
3833b188dabSeric 	yylval.lineno = file->lineno;
3843b188dabSeric 	if (c == '#')
3853b188dabSeric 		while ((c = lgetc(0)) != '\n' && c != EOF)
3863b188dabSeric 			; /* nothing */
3873b188dabSeric 	if (c == '$' && parsebuf == NULL) {
3883b188dabSeric 		while (1) {
3893b188dabSeric 			if ((c = lgetc(0)) == EOF)
3903b188dabSeric 				return (0);
3913b188dabSeric 
3923b188dabSeric 			if (p + 1 >= buf + sizeof(buf) - 1) {
3933b188dabSeric 				yyerror("string too long");
3943b188dabSeric 				return (findeol());
3953b188dabSeric 			}
3963b188dabSeric 			if (isalnum(c) || c == '_') {
3973b188dabSeric 				*p++ = c;
3983b188dabSeric 				continue;
3993b188dabSeric 			}
4003b188dabSeric 			*p = '\0';
4013b188dabSeric 			lungetc(c);
4023b188dabSeric 			break;
4033b188dabSeric 		}
4043b188dabSeric 		val = symget(buf);
4053b188dabSeric 		if (val == NULL) {
4063b188dabSeric 			yyerror("macro '%s' not defined", buf);
4073b188dabSeric 			return (findeol());
4083b188dabSeric 		}
4093b188dabSeric 		parsebuf = val;
4103b188dabSeric 		parseindex = 0;
4113b188dabSeric 		goto top;
4123b188dabSeric 	}
4133b188dabSeric 
4143b188dabSeric 	switch (c) {
4153b188dabSeric 	case '\'':
4163b188dabSeric 	case '"':
4173b188dabSeric 		quotec = c;
4183b188dabSeric 		while (1) {
4193b188dabSeric 			if ((c = lgetc(quotec)) == EOF)
4203b188dabSeric 				return (0);
4213b188dabSeric 			if (c == '\n') {
4223b188dabSeric 				file->lineno++;
4233b188dabSeric 				continue;
4243b188dabSeric 			} else if (c == '\\') {
4253b188dabSeric 				if ((next = lgetc(quotec)) == EOF)
4263b188dabSeric 					return (0);
427a1533359Ssashan 				if (next == quotec || next == ' ' ||
428a1533359Ssashan 				    next == '\t')
4293b188dabSeric 					c = next;
4303b188dabSeric 				else if (next == '\n') {
4313b188dabSeric 					file->lineno++;
4323b188dabSeric 					continue;
4333b188dabSeric 				} else
4343b188dabSeric 					lungetc(next);
4353b188dabSeric 			} else if (c == quotec) {
4363b188dabSeric 				*p = '\0';
4373b188dabSeric 				break;
4383b188dabSeric 			} else if (c == '\0') {
4393b188dabSeric 				yyerror("syntax error");
4403b188dabSeric 				return (findeol());
4413b188dabSeric 			}
4423b188dabSeric 			if (p + 1 >= buf + sizeof(buf) - 1) {
4433b188dabSeric 				yyerror("string too long");
4443b188dabSeric 				return (findeol());
4453b188dabSeric 			}
4463b188dabSeric 			*p++ = c;
4473b188dabSeric 		}
4483b188dabSeric 		yylval.v.string = strdup(buf);
4493b188dabSeric 		if (yylval.v.string == NULL)
450a062aa9dSkrw 			err(1, "%s", __func__);
4513b188dabSeric 		return (STRING);
4523b188dabSeric 	}
4533b188dabSeric 
4543b188dabSeric #define allowed_to_end_number(x) \
4553b188dabSeric 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
4563b188dabSeric 
4573b188dabSeric 	if (c == '-' || isdigit(c)) {
4583b188dabSeric 		do {
4593b188dabSeric 			*p++ = c;
460915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
4613b188dabSeric 				yyerror("string too long");
4623b188dabSeric 				return (findeol());
4633b188dabSeric 			}
4643b188dabSeric 		} while ((c = lgetc(0)) != EOF && isdigit(c));
4653b188dabSeric 		lungetc(c);
4663b188dabSeric 		if (p == buf + 1 && buf[0] == '-')
4673b188dabSeric 			goto nodigits;
4683b188dabSeric 		if (c == EOF || allowed_to_end_number(c)) {
4693b188dabSeric 			const char *errstr = NULL;
4703b188dabSeric 
4713b188dabSeric 			*p = '\0';
4723b188dabSeric 			yylval.v.number = strtonum(buf, LLONG_MIN,
4733b188dabSeric 			    LLONG_MAX, &errstr);
4743b188dabSeric 			if (errstr) {
4753b188dabSeric 				yyerror("\"%s\" invalid number: %s",
4763b188dabSeric 				    buf, errstr);
4773b188dabSeric 				return (findeol());
4783b188dabSeric 			}
4793b188dabSeric 			return (NUMBER);
4803b188dabSeric 		} else {
4813b188dabSeric nodigits:
4823b188dabSeric 			while (p > buf + 1)
483*08f6ba19Snaddy 				lungetc((unsigned char)*--p);
484*08f6ba19Snaddy 			c = (unsigned char)*--p;
4853b188dabSeric 			if (c == '-')
4863b188dabSeric 				return (c);
4873b188dabSeric 		}
4883b188dabSeric 	}
4893b188dabSeric 
4903b188dabSeric 	if (c == '=') {
4913b188dabSeric 		if ((c = lgetc(0)) != EOF && c == '>')
4923b188dabSeric 			return (ARROW);
4933b188dabSeric 		lungetc(c);
4943b188dabSeric 		c = '=';
4953b188dabSeric 	}
4963b188dabSeric 
4973b188dabSeric #define allowed_in_string(x) \
4983b188dabSeric 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
4993b188dabSeric 	x != '{' && x != '}' && x != '<' && x != '>' && \
5003b188dabSeric 	x != '!' && x != '=' && x != '#' && \
5013b188dabSeric 	x != ','))
5023b188dabSeric 
5033b188dabSeric 	if (isalnum(c) || c == ':' || c == '_') {
5043b188dabSeric 		do {
5053b188dabSeric 			*p++ = c;
506915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
5073b188dabSeric 				yyerror("string too long");
5083b188dabSeric 				return (findeol());
5093b188dabSeric 			}
5103b188dabSeric 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
5113b188dabSeric 		lungetc(c);
5123b188dabSeric 		*p = '\0';
5133b188dabSeric 		if ((token = lookup(buf)) == STRING)
5143b188dabSeric 			if ((yylval.v.string = strdup(buf)) == NULL)
515a062aa9dSkrw 				err(1, "%s", __func__);
5163b188dabSeric 		return (token);
5173b188dabSeric 	}
5183b188dabSeric 	if (c == '\n') {
5193b188dabSeric 		yylval.lineno = file->lineno;
5203b188dabSeric 		file->lineno++;
5213b188dabSeric 	}
5223b188dabSeric 	if (c == EOF)
5233b188dabSeric 		return (0);
5243b188dabSeric 	return (c);
5253b188dabSeric }
5263b188dabSeric 
5273b188dabSeric int
check_file_secrecy(int fd,const char * fname)5283b188dabSeric check_file_secrecy(int fd, const char *fname)
5293b188dabSeric {
5303b188dabSeric 	struct stat	st;
5313b188dabSeric 
5323b188dabSeric 	if (fstat(fd, &st)) {
5333b188dabSeric 		log_warn("warn: cannot stat %s", fname);
5343b188dabSeric 		return (-1);
5353b188dabSeric 	}
5363b188dabSeric 	if (st.st_uid != 0 && st.st_uid != getuid()) {
5373b188dabSeric 		log_warnx("warn: %s: owner not root or current user", fname);
5383b188dabSeric 		return (-1);
5393b188dabSeric 	}
5403b188dabSeric 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
5413b188dabSeric 		log_warnx("warn: %s: group/world readable/writeable", fname);
5423b188dabSeric 		return (-1);
5433b188dabSeric 	}
5443b188dabSeric 	return (0);
5453b188dabSeric }
5463b188dabSeric 
5473b188dabSeric struct file *
pushfile(const char * name,int secret)5483b188dabSeric pushfile(const char *name, int secret)
5493b188dabSeric {
5503b188dabSeric 	struct file	*nfile;
5513b188dabSeric 
5523b188dabSeric 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
5536a3d55f9Skrw 		log_warn("%s", __func__);
5543b188dabSeric 		return (NULL);
5553b188dabSeric 	}
5563b188dabSeric 	if ((nfile->name = strdup(name)) == NULL) {
5576a3d55f9Skrw 		log_warn("%s", __func__);
5583b188dabSeric 		free(nfile);
5593b188dabSeric 		return (NULL);
5603b188dabSeric 	}
5613b188dabSeric 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
5626a3d55f9Skrw 		log_warn("%s: %s", __func__, nfile->name);
5633b188dabSeric 		free(nfile->name);
5643b188dabSeric 		free(nfile);
5653b188dabSeric 		return (NULL);
5663b188dabSeric 	} else if (secret &&
5673b188dabSeric 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
5683b188dabSeric 		fclose(nfile->stream);
5693b188dabSeric 		free(nfile->name);
5703b188dabSeric 		free(nfile);
5713b188dabSeric 		return (NULL);
5723b188dabSeric 	}
5733b188dabSeric 	nfile->lineno = 1;
5743b188dabSeric 	TAILQ_INSERT_TAIL(&files, nfile, entry);
5753b188dabSeric 	return (nfile);
5763b188dabSeric }
5773b188dabSeric 
5783b188dabSeric int
popfile(void)5793b188dabSeric popfile(void)
5803b188dabSeric {
5813b188dabSeric 	struct file	*prev;
5823b188dabSeric 
5833b188dabSeric 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
5843b188dabSeric 		prev->errors += file->errors;
5853b188dabSeric 
5863b188dabSeric 	TAILQ_REMOVE(&files, file, entry);
5873b188dabSeric 	fclose(file->stream);
5883b188dabSeric 	free(file->name);
5893b188dabSeric 	free(file);
5903b188dabSeric 	file = prev;
5913b188dabSeric 	return (file ? 0 : EOF);
5923b188dabSeric }
5933b188dabSeric 
5943b188dabSeric struct lpd_conf *
parse_config(const char * filename,int verbose)5953b188dabSeric parse_config(const char *filename, int verbose)
5963b188dabSeric {
5973b188dabSeric 	struct sym     *sym, *next;
5983b188dabSeric 
5993b188dabSeric 	conf = calloc(1, sizeof(*conf));
6003b188dabSeric 	if (conf == NULL)
6013b188dabSeric 		return NULL;
6023b188dabSeric 	TAILQ_INIT(&conf->listeners);
6033b188dabSeric 
6043b188dabSeric 	errors = 0;
6053b188dabSeric 
6063b188dabSeric 	if ((file = pushfile(filename, 0)) == NULL) {
6073b188dabSeric 		config_free(conf);
6083b188dabSeric 		return NULL;
6093b188dabSeric 	}
6103b188dabSeric 	topfile = file;
6113b188dabSeric 
6123b188dabSeric 	/*
6133b188dabSeric 	 * parse configuration
6143b188dabSeric 	 */
6153b188dabSeric 	setservent(1);
6163b188dabSeric 	yyparse();
6173b188dabSeric 	errors = file->errors;
6183b188dabSeric 	popfile();
6193b188dabSeric 	endservent();
6203b188dabSeric 
6213b188dabSeric 	/* Free macros and check which have not been used. */
6223b188dabSeric 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
6233b188dabSeric 		if ((verbose) && !sym->used)
6243b188dabSeric 			log_warnx("warning: macro '%s' not used\n", sym->nam);
6253b188dabSeric 		if (!sym->persist) {
6263b188dabSeric 			free(sym->nam);
6273b188dabSeric 			free(sym->val);
6283b188dabSeric 			TAILQ_REMOVE(&symhead, sym, entry);
6293b188dabSeric 			free(sym);
6303b188dabSeric 		}
6313b188dabSeric 	}
6323b188dabSeric 
6333b188dabSeric 	if (errors) {
6343b188dabSeric 		config_free(conf);
6353b188dabSeric 		return NULL;
6363b188dabSeric 	}
6373b188dabSeric 
6383b188dabSeric 	return conf;
6393b188dabSeric }
6403b188dabSeric 
6413b188dabSeric int
symset(const char * nam,const char * val,int persist)6423b188dabSeric symset(const char *nam, const char *val, int persist)
6433b188dabSeric {
6443b188dabSeric 	struct sym	*sym;
6453b188dabSeric 
6463b188dabSeric 	TAILQ_FOREACH(sym, &symhead, entry) {
6473b188dabSeric 		if (strcmp(nam, sym->nam) == 0)
6483b188dabSeric 			break;
6493b188dabSeric 	}
6503b188dabSeric 
6513b188dabSeric 	if (sym != NULL) {
6523b188dabSeric 		if (sym->persist == 1)
6533b188dabSeric 			return (0);
6543b188dabSeric 		else {
6553b188dabSeric 			free(sym->nam);
6563b188dabSeric 			free(sym->val);
6573b188dabSeric 			TAILQ_REMOVE(&symhead, sym, entry);
6583b188dabSeric 			free(sym);
6593b188dabSeric 		}
6603b188dabSeric 	}
6613b188dabSeric 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
6623b188dabSeric 		return (-1);
6633b188dabSeric 
6643b188dabSeric 	sym->nam = strdup(nam);
6653b188dabSeric 	if (sym->nam == NULL) {
6663b188dabSeric 		free(sym);
6673b188dabSeric 		return (-1);
6683b188dabSeric 	}
6693b188dabSeric 	sym->val = strdup(val);
6703b188dabSeric 	if (sym->val == NULL) {
6713b188dabSeric 		free(sym->nam);
6723b188dabSeric 		free(sym);
6733b188dabSeric 		return (-1);
6743b188dabSeric 	}
6753b188dabSeric 	sym->used = 0;
6763b188dabSeric 	sym->persist = persist;
6773b188dabSeric 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
6783b188dabSeric 	return (0);
6793b188dabSeric }
6803b188dabSeric 
6813b188dabSeric int
cmdline_symset(char * s)6823b188dabSeric cmdline_symset(char *s)
6833b188dabSeric {
6843b188dabSeric 	char	*sym, *val;
6853b188dabSeric 	int	ret;
6863b188dabSeric 
6873b188dabSeric 	if ((val = strrchr(s, '=')) == NULL)
6883b188dabSeric 		return (-1);
689ed1b9eb8Smiko 	sym = strndup(s, val - s);
690ed1b9eb8Smiko 	if (sym == NULL)
691ed1b9eb8Smiko 		errx(1, "%s: strndup", __func__);
6923b188dabSeric 	ret = symset(sym, val + 1, 1);
6933b188dabSeric 	free(sym);
6943b188dabSeric 
6953b188dabSeric 	return (ret);
6963b188dabSeric }
6973b188dabSeric 
6983b188dabSeric char *
symget(const char * nam)6993b188dabSeric symget(const char *nam)
7003b188dabSeric {
7013b188dabSeric 	struct sym	*sym;
7023b188dabSeric 
7033b188dabSeric 	TAILQ_FOREACH(sym, &symhead, entry) {
7043b188dabSeric 		if (strcmp(nam, sym->nam) == 0) {
7053b188dabSeric 			sym->used = 1;
7063b188dabSeric 			return (sym->val);
7073b188dabSeric 		}
7083b188dabSeric 	}
7093b188dabSeric 	return (NULL);
7103b188dabSeric }
7113b188dabSeric 
7123b188dabSeric static void
config_free(struct lpd_conf * c)7133b188dabSeric config_free(struct lpd_conf *c)
7143b188dabSeric {
7153b188dabSeric 	struct listener *l;
7163b188dabSeric 
7173b188dabSeric 	while ((l = TAILQ_FIRST(&c->listeners))) {
7183b188dabSeric 		TAILQ_REMOVE(&c->listeners, l, entry);
7193b188dabSeric 		free(l);
7203b188dabSeric 	}
7213b188dabSeric 	free(c);
7223b188dabSeric }
7233b188dabSeric 
7243b188dabSeric static void
create_listeners(struct listen_opts * lo)7253b188dabSeric create_listeners(struct listen_opts *lo)
7263b188dabSeric {
7273b188dabSeric 	if (local(lo))
7283b188dabSeric 		return;
7293b188dabSeric 	if (interface(lo))
7303b188dabSeric 		return;
7313b188dabSeric 	if (host_v4(lo))
7323b188dabSeric 		return;
7333b188dabSeric 	if (host_v6(lo))
7343b188dabSeric 		return;
7353b188dabSeric 	if (host_dns(lo))
7363b188dabSeric 		return;
7373b188dabSeric 
7383b188dabSeric 	errx(1, "invalid virtual ip or interface: %s", lo->ifx);
7393b188dabSeric }
7403b188dabSeric 
7413b188dabSeric static void
config_listener(struct listener * l,struct listen_opts * lo)7423b188dabSeric config_listener(struct listener *l,  struct listen_opts *lo)
7433b188dabSeric {
7443b188dabSeric 	l->sock = -1;
7453b188dabSeric 	l->proto = lo->proto;
7463b188dabSeric 
7473b188dabSeric 	TAILQ_INSERT_TAIL(&conf->listeners, l, entry);
7483b188dabSeric }
7493b188dabSeric 
7503b188dabSeric static int
local(struct listen_opts * lo)7513b188dabSeric local(struct listen_opts *lo)
7523b188dabSeric {
7533b188dabSeric 	struct sockaddr_un	*sun;
7543b188dabSeric 	struct listener		*h;
7553b188dabSeric 
7563b188dabSeric 	if (lo->family != AF_UNSPEC && lo->family != AF_LOCAL)
7573b188dabSeric 		return 0;
7583b188dabSeric 
7593b188dabSeric 	if (lo->ifx[0] != '/')
7603b188dabSeric 		return 0;
7613b188dabSeric 
7623b188dabSeric 	h = calloc(1, sizeof(*h));
7633b188dabSeric 	sun = (struct sockaddr_un *)&h->ss;
7643b188dabSeric 	sun->sun_len = sizeof(*sun);
7653b188dabSeric 	sun->sun_family = AF_LOCAL;
7663b188dabSeric 	if (strlcpy(sun->sun_path, lo->ifx, sizeof(sun->sun_path))
7673b188dabSeric 	    >= sizeof(sun->sun_path))
7683b188dabSeric 		fatalx("path too long");
7693b188dabSeric 
7703b188dabSeric 	config_listener(h,  lo);
7713b188dabSeric 
7723b188dabSeric 	return (1);
7733b188dabSeric }
7743b188dabSeric 
7753b188dabSeric static int
host_v4(struct listen_opts * lo)7763b188dabSeric host_v4(struct listen_opts *lo)
7773b188dabSeric {
7783b188dabSeric 	struct in_addr		 ina;
7793b188dabSeric 	struct sockaddr_in	*sain;
7803b188dabSeric 	struct listener		*h;
7813b188dabSeric 
7823b188dabSeric 	if (lo->family != AF_UNSPEC && lo->family != AF_INET)
7833b188dabSeric 		return (0);
7843b188dabSeric 
7853b188dabSeric 	memset(&ina, 0, sizeof(ina));
7863b188dabSeric 	if (inet_pton(AF_INET, lo->ifx, &ina) != 1)
7873b188dabSeric 		return (0);
7883b188dabSeric 
7893b188dabSeric 	h = calloc(1, sizeof(*h));
7903b188dabSeric 	sain = (struct sockaddr_in *)&h->ss;
7913b188dabSeric 	sain->sin_len = sizeof(struct sockaddr_in);
7923b188dabSeric 	sain->sin_family = AF_INET;
7933b188dabSeric 	sain->sin_addr.s_addr = ina.s_addr;
7943b188dabSeric 	sain->sin_port = lo->port;
7953b188dabSeric 
7963b188dabSeric 	config_listener(h,  lo);
7973b188dabSeric 
7983b188dabSeric 	return (1);
7993b188dabSeric }
8003b188dabSeric 
8013b188dabSeric static int
host_v6(struct listen_opts * lo)8023b188dabSeric host_v6(struct listen_opts *lo)
8033b188dabSeric {
8043b188dabSeric 	struct in6_addr		 ina6;
8053b188dabSeric 	struct sockaddr_in6	*sin6;
8063b188dabSeric 	struct listener		*h;
8073b188dabSeric 
8083b188dabSeric 	if (lo->family != AF_UNSPEC && lo->family != AF_INET6)
8093b188dabSeric 		return (0);
8103b188dabSeric 
8113b188dabSeric 	memset(&ina6, 0, sizeof(ina6));
8123b188dabSeric 	if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1)
8133b188dabSeric 		return (0);
8143b188dabSeric 
8153b188dabSeric 	h = calloc(1, sizeof(*h));
8163b188dabSeric 	sin6 = (struct sockaddr_in6 *)&h->ss;
8173b188dabSeric 	sin6->sin6_len = sizeof(struct sockaddr_in6);
8183b188dabSeric 	sin6->sin6_family = AF_INET6;
8193b188dabSeric 	sin6->sin6_port = lo->port;
8203b188dabSeric 	memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
8213b188dabSeric 
8223b188dabSeric 	config_listener(h,  lo);
8233b188dabSeric 
8243b188dabSeric 	return (1);
8253b188dabSeric }
8263b188dabSeric 
8273b188dabSeric static int
host_dns(struct listen_opts * lo)8283b188dabSeric host_dns(struct listen_opts *lo)
8293b188dabSeric {
8303b188dabSeric 	struct addrinfo		 hints, *res0, *res;
8313b188dabSeric 	int			 error, cnt = 0;
8323b188dabSeric 	struct sockaddr_in	*sain;
8333b188dabSeric 	struct sockaddr_in6	*sin6;
8343b188dabSeric 	struct listener		*h;
8353b188dabSeric 
8363b188dabSeric 	memset(&hints, 0, sizeof(hints));
8373b188dabSeric 	hints.ai_family = lo->family;
8383b188dabSeric 	hints.ai_socktype = SOCK_STREAM;
8393b188dabSeric 	hints.ai_flags = AI_ADDRCONFIG;
8403b188dabSeric 	error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
8413b188dabSeric 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
8423b188dabSeric 		return (0);
8433b188dabSeric 	if (error) {
8443b188dabSeric 		log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
8453b188dabSeric 		    gai_strerror(error));
8463b188dabSeric 		return (-1);
8473b188dabSeric 	}
8483b188dabSeric 
8493b188dabSeric 	for (res = res0; res; res = res->ai_next) {
8503b188dabSeric 		if (res->ai_family != AF_INET &&
8513b188dabSeric 		    res->ai_family != AF_INET6)
8523b188dabSeric 			continue;
8533b188dabSeric 		h = calloc(1, sizeof(*h));
8543b188dabSeric 		h->ss.ss_family = res->ai_family;
8553b188dabSeric 		if (res->ai_family == AF_INET) {
8563b188dabSeric 			sain = (struct sockaddr_in *)&h->ss;
8573b188dabSeric 			sain->sin_len = sizeof(struct sockaddr_in);
8583b188dabSeric 			sain->sin_addr.s_addr = ((struct sockaddr_in *)
8593b188dabSeric 			    res->ai_addr)->sin_addr.s_addr;
8603b188dabSeric 			sain->sin_port = lo->port;
8613b188dabSeric 		} else {
8623b188dabSeric 			sin6 = (struct sockaddr_in6 *)&h->ss;
8633b188dabSeric 			sin6->sin6_len = sizeof(struct sockaddr_in6);
8643b188dabSeric 			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
8653b188dabSeric 			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
8663b188dabSeric 			sin6->sin6_port = lo->port;
8673b188dabSeric 		}
8683b188dabSeric 
8693b188dabSeric 		config_listener(h, lo);
8703b188dabSeric 
8713b188dabSeric 		cnt++;
8723b188dabSeric 	}
8733b188dabSeric 
8743b188dabSeric 	freeaddrinfo(res0);
8753b188dabSeric 	return (cnt);
8763b188dabSeric }
8773b188dabSeric 
8783b188dabSeric static int
interface(struct listen_opts * lo)8793b188dabSeric interface(struct listen_opts *lo)
8803b188dabSeric {
8813b188dabSeric 	struct ifaddrs *ifap, *p;
8823b188dabSeric 	struct sockaddr_in	*sain;
8833b188dabSeric 	struct sockaddr_in6	*sin6;
8843b188dabSeric 	struct listener		*h;
8853b188dabSeric 	int			ret = 0;
8863b188dabSeric 
8873b188dabSeric 	if (getifaddrs(&ifap) == -1)
8883b188dabSeric 		fatal("getifaddrs");
8893b188dabSeric 
8903b188dabSeric 	for (p = ifap; p != NULL; p = p->ifa_next) {
8913b188dabSeric 		if (p->ifa_addr == NULL)
8923b188dabSeric 			continue;
8933b188dabSeric 		if (strcmp(p->ifa_name, lo->ifx) != 0 &&
8943b188dabSeric 		    !is_if_in_group(p->ifa_name, lo->ifx))
8953b188dabSeric 			continue;
8963b188dabSeric 		if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
8973b188dabSeric 			continue;
8983b188dabSeric 
8993b188dabSeric 		h = calloc(1, sizeof(*h));
9003b188dabSeric 
9013b188dabSeric 		switch (p->ifa_addr->sa_family) {
9023b188dabSeric 		case AF_INET:
9033b188dabSeric 			sain = (struct sockaddr_in *)&h->ss;
9043b188dabSeric 			*sain = *(struct sockaddr_in *)p->ifa_addr;
9053b188dabSeric 			sain->sin_len = sizeof(struct sockaddr_in);
9063b188dabSeric 			sain->sin_port = lo->port;
9073b188dabSeric 			break;
9083b188dabSeric 
9093b188dabSeric 		case AF_INET6:
9103b188dabSeric 			sin6 = (struct sockaddr_in6 *)&h->ss;
9113b188dabSeric 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
9123b188dabSeric 			sin6->sin6_len = sizeof(struct sockaddr_in6);
9133b188dabSeric 			sin6->sin6_port = lo->port;
9143b188dabSeric 			break;
9153b188dabSeric 
9163b188dabSeric 		default:
9173b188dabSeric 			free(h);
9183b188dabSeric 			continue;
9193b188dabSeric 		}
9203b188dabSeric 
9213b188dabSeric 		config_listener(h, lo);
9223b188dabSeric 		ret = 1;
9233b188dabSeric 	}
9243b188dabSeric 
9253b188dabSeric 	freeifaddrs(ifap);
9263b188dabSeric 
9273b188dabSeric 	return ret;
9283b188dabSeric }
9293b188dabSeric 
9303b188dabSeric static int
is_if_in_group(const char * ifname,const char * groupname)9313b188dabSeric is_if_in_group(const char *ifname, const char *groupname)
9323b188dabSeric {
9333b188dabSeric         unsigned int		 len;
9343b188dabSeric         struct ifgroupreq        ifgr;
9353b188dabSeric         struct ifg_req          *ifg;
9363b188dabSeric 	int			 s;
9373b188dabSeric 	int			 ret = 0;
9383b188dabSeric 
939df69c215Sderaadt 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
9403b188dabSeric 		err(1, "socket");
9413b188dabSeric 
9423b188dabSeric         memset(&ifgr, 0, sizeof(ifgr));
9433b188dabSeric         if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ)
9443b188dabSeric 		errx(1, "interface name too large");
9453b188dabSeric 
9463b188dabSeric         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
9473b188dabSeric                 if (errno == EINVAL || errno == ENOTTY)
9483b188dabSeric 			goto end;
9493b188dabSeric 		err(1, "SIOCGIFGROUP");
9503b188dabSeric         }
9513b188dabSeric 
9523b188dabSeric         len = ifgr.ifgr_len;
9533b188dabSeric         ifgr.ifgr_groups = calloc(len/sizeof(struct ifg_req),
9543b188dabSeric 		sizeof(struct ifg_req));
9553b188dabSeric         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
9563b188dabSeric                 err(1, "SIOCGIFGROUP");
9573b188dabSeric 
9583b188dabSeric         ifg = ifgr.ifgr_groups;
9593b188dabSeric         for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
9603b188dabSeric                 len -= sizeof(struct ifg_req);
9613b188dabSeric 		if (strcmp(ifg->ifgrq_group, groupname) == 0) {
9623b188dabSeric 			ret = 1;
9633b188dabSeric 			break;
9643b188dabSeric 		}
9653b188dabSeric         }
9663b188dabSeric         free(ifgr.ifgr_groups);
9673b188dabSeric 
9683b188dabSeric end:
9693b188dabSeric 	close(s);
9703b188dabSeric 	return ret;
9713b188dabSeric }
972