inetd.c revision 1.43
1/*	$NetBSD: inetd.c,v 1.43 1998/07/16 08:55:43 tron Exp $	*/
2
3/*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the NetBSD
22 *	Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 *    contributors may be used to endorse or promote products derived
25 *    from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/*
41 * Copyright (c) 1983, 1991, 1993, 1994
42 *	The Regents of the University of California.  All rights reserved.
43 *
44 * Redistribution and use in source and binary forms, with or without
45 * modification, are permitted provided that the following conditions
46 * are met:
47 * 1. Redistributions of source code must retain the above copyright
48 *    notice, this list of conditions and the following disclaimer.
49 * 2. Redistributions in binary form must reproduce the above copyright
50 *    notice, this list of conditions and the following disclaimer in the
51 *    documentation and/or other materials provided with the distribution.
52 * 3. All advertising materials mentioning features or use of this software
53 *    must display the following acknowledgement:
54 *	This product includes software developed by the University of
55 *	California, Berkeley and its contributors.
56 * 4. Neither the name of the University nor the names of its contributors
57 *    may be used to endorse or promote products derived from this software
58 *    without specific prior written permission.
59 *
60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
70 * SUCH DAMAGE.
71 */
72
73#include <sys/cdefs.h>
74#ifndef lint
75__COPYRIGHT("@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
76	The Regents of the University of California.  All rights reserved.\n");
77#if 0
78static char sccsid[] = "@(#)inetd.c	8.4 (Berkeley) 4/13/94";
79#else
80__RCSID("$NetBSD: inetd.c,v 1.43 1998/07/16 08:55:43 tron Exp $");
81#endif
82#endif /* not lint */
83
84/*
85 * Inetd - Internet super-server
86 *
87 * This program invokes all internet services as needed.  Connection-oriented
88 * services are invoked each time a connection is made, by creating a process.
89 * This process is passed the connection as file descriptor 0 and is expected
90 * to do a getpeername to find out the source host and port.
91 *
92 * Datagram oriented services are invoked when a datagram
93 * arrives; a process is created and passed a pending message
94 * on file descriptor 0.  Datagram servers may either connect
95 * to their peer, freeing up the original socket for inetd
96 * to receive further messages on, or ``take over the socket'',
97 * processing all arriving datagrams and, eventually, timing
98 * out.	 The first type of server is said to be ``multi-threaded'';
99 * the second type of server ``single-threaded''.
100 *
101 * Inetd uses a configuration file which is read at startup
102 * and, possibly, at some later time in response to a hangup signal.
103 * The configuration file is ``free format'' with fields given in the
104 * order shown below.  Continuation lines for an entry must being with
105 * a space or tab.  All fields must be present in each entry.
106 *
107 *	service name			must be in /etc/services or must
108 *					name a tcpmux service
109 *	socket type			stream/dgram/raw/rdm/seqpacket
110 *	protocol			must be in /etc/protocols
111 *	wait/nowait[.max]		single-threaded/multi-threaded, max #
112 *	user[.group]			user/group to run daemon as
113 *	server program			full path name
114 *	server program arguments	maximum of MAXARGS (20)
115 *
116 * For RPC services
117 *      service name/version            must be in /etc/rpc
118 *	socket type			stream/dgram/raw/rdm/seqpacket
119 *	protocol			must be in /etc/protocols
120 *	wait/nowait[.max]		single-threaded/multi-threaded
121 *	user[.group]			user to run daemon as
122 *	server program			full path name
123 *	server program arguments	maximum of MAXARGS (20)
124 *
125 * For non-RPC services, the "service name" can be of the form
126 * hostaddress:servicename, in which case the hostaddress is used
127 * as the host portion of the address to listen on.  If hostaddress
128 * consists of a single `*' character, INADDR_ANY is used.
129 *
130 * A line can also consist of just
131 *	hostaddress:
132 * where hostaddress is as in the preceding paragraph.  Such a line must
133 * have no further fields; the specified hostaddress is remembered and
134 * used for all further lines that have no hostaddress specified,
135 * until the next such line (or EOF).  (This is why * is provided to
136 * allow explicit specification of INADDR_ANY.)  A line
137 *	*:
138 * is implicitly in effect at the beginning of the file.
139 *
140 * The hostaddress specifier may (and often will) contain dots;
141 * the service name must not.
142 *
143 * For RPC services, host-address specifiers are accepted and will
144 * work to some extent; however, because of limitations in the
145 * portmapper interface, it will not work to try to give more than
146 * one line for any given RPC service, even if the host-address
147 * specifiers are different.
148 *
149 * TCP services without official port numbers are handled with the
150 * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
151 * requests. When a connection is made from a foreign host, the service
152 * requested is passed to tcpmux, which looks it up in the servtab list
153 * and returns the proper entry for the service. Tcpmux returns a
154 * negative reply if the service doesn't exist, otherwise the invoked
155 * server is expected to return the positive reply if the service type in
156 * inetd.conf file has the prefix "tcpmux/". If the service type has the
157 * prefix "tcpmux/+", tcpmux will return the positive reply for the
158 * process; this is for compatibility with older server code, and also
159 * allows you to invoke programs that use stdin/stdout without putting any
160 * special server code in them. Services that use tcpmux are "nowait"
161 * because they do not have a well-known port and hence cannot listen
162 * for new requests.
163 *
164 * Comment lines are indicated by a `#' in column 1.
165 */
166
167/*
168 * Here's the scoop concerning the user.group feature:
169 *
170 * 1) set-group-option off.
171 *
172 * 	a) user = root:	NO setuid() or setgid() is done
173 *
174 * 	b) other:	setuid()
175 * 			setgid(primary group as found in passwd)
176 * 			initgroups(name, primary group)
177 *
178 * 2) set-group-option on.
179 *
180 * 	a) user = root:	NO setuid()
181 * 			setgid(specified group)
182 * 			NO initgroups()
183 *
184 * 	b) other:	setuid()
185 * 			setgid(specified group)
186 * 			initgroups(name, specified group)
187 *
188 */
189
190#include <sys/param.h>
191#include <sys/stat.h>
192#include <sys/ioctl.h>
193#include <sys/socket.h>
194#include <sys/un.h>
195#include <sys/wait.h>
196#include <sys/time.h>
197#include <sys/resource.h>
198
199#ifndef RLIMIT_NOFILE
200#define RLIMIT_NOFILE	RLIMIT_OFILE
201#endif
202
203#define RPC
204
205#include <netinet/in.h>
206#include <arpa/inet.h>
207#ifdef RPC
208#include <rpc/rpc.h>
209#include <rpc/pmap_clnt.h>
210#endif
211
212#include <ctype.h>
213#include <errno.h>
214#include <fcntl.h>
215#include <grp.h>
216#include <netdb.h>
217#include <pwd.h>
218#include <signal.h>
219#include <stdio.h>
220#include <stdlib.h>
221#include <string.h>
222#include <syslog.h>
223#include <unistd.h>
224
225#include "pathnames.h"
226
227#ifdef LIBWRAP
228# include <tcpd.h>
229#ifndef LIBWRAP_ALLOW_FACILITY
230# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
231#endif
232#ifndef LIBWRAP_ALLOW_SEVERITY
233# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
234#endif
235#ifndef LIBWRAP_DENY_FACILITY
236# define LIBWRAP_DENY_FACILITY LOG_AUTH
237#endif
238#ifndef LIBWRAP_DENY_SEVERITY
239# define LIBWRAP_DENY_SEVERITY LOG_WARNING
240#endif
241int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
242int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
243#endif
244
245#define	TOOMANY		40		/* don't start more than TOOMANY */
246#define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
247#define	RETRYTIME	(60*10)		/* retry after bind or server fail */
248
249#define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
250
251int	debug;
252#ifdef LIBWRAP
253int	lflag;
254#endif
255int	nsock, maxsock;
256fd_set	allsock;
257int	options;
258int	timingout;
259struct	servent *sp;
260char	*curdom;
261
262#ifndef OPEN_MAX
263#define OPEN_MAX	64
264#endif
265
266/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
267#define FD_MARGIN	(8)
268typeof(((struct rlimit *)0)->rlim_cur)	rlim_ofile_cur = OPEN_MAX;
269
270#ifdef RLIMIT_NOFILE
271struct rlimit	rlim_ofile;
272#endif
273
274struct	servtab {
275	char	*se_hostaddr;		/* host address to listen on */
276	char	*se_service;		/* name of service */
277	int	se_socktype;		/* type of socket to use */
278	int	se_family;		/* address family */
279	char	*se_proto;		/* protocol used */
280	int	se_sndbuf;		/* sndbuf size */
281	int	se_rcvbuf;		/* rcvbuf size */
282	int	se_rpcprog;		/* rpc program number */
283	int	se_rpcversl;		/* rpc program lowest version */
284	int	se_rpcversh;		/* rpc program highest version */
285#define isrpcservice(sep)	((sep)->se_rpcversl != 0)
286	short	se_wait;		/* single threaded server */
287	short	se_checked;		/* looked at during merge */
288	char	*se_user;		/* user name to run as */
289	char	*se_group;		/* group name to run as */
290	struct	biltin *se_bi;		/* if built-in, description */
291	char	*se_server;		/* server program */
292#define	MAXARGV 20
293	char	*se_argv[MAXARGV+1];	/* program arguments */
294	int	se_fd;			/* open descriptor */
295	int	se_type;		/* type */
296	union {
297		struct	sockaddr se_un_ctrladdr;
298		struct	sockaddr_in se_un_ctrladdr_in;
299		struct	sockaddr_un se_un_ctrladdr_un;
300	} se_un;			/* bound address */
301#define se_ctrladdr	se_un.se_un_ctrladdr
302#define se_ctrladdr_in	se_un.se_un_ctrladdr_in
303#define se_ctrladdr_un	se_un.se_un_ctrladdr_un
304	int	se_ctrladdr_size;
305	int	se_max;			/* max # of instances of this service */
306	int	se_count;		/* number started since se_time */
307	struct	timeval se_time;	/* start of se_count */
308#ifdef MULOG
309	int	se_log;
310#define MULOG_RFC931	0x40000000
311#endif
312	struct	servtab *se_next;
313} *servtab;
314
315#define NORM_TYPE	0
316#define MUX_TYPE	1
317#define MUXPLUS_TYPE	2
318#define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
319			 ((sep)->se_type == MUXPLUS_TYPE))
320#define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
321
322
323void		chargen_dg __P((int, struct servtab *));
324void		chargen_stream __P((int, struct servtab *));
325void		close_sep __P((struct servtab *));
326void		config __P((int));
327void		daytime_dg __P((int, struct servtab *));
328void		daytime_stream __P((int, struct servtab *));
329void		discard_dg __P((int, struct servtab *));
330void		discard_stream __P((int, struct servtab *));
331void		echo_dg __P((int, struct servtab *));
332void		echo_stream __P((int, struct servtab *));
333void		endconfig __P((void));
334struct servtab *enter __P((struct servtab *));
335void		freeconfig __P((struct servtab *));
336struct servtab *getconfigent __P((void));
337void		goaway __P((int));
338void		machtime_dg __P((int, struct servtab *));
339void		machtime_stream __P((int, struct servtab *));
340char	       *newstr __P((char *));
341char	       *nextline __P((FILE *));
342void		print_service __P((char *, struct servtab *));
343void		reapchild __P((int));
344void		retry __P((int));
345void		run_service __P((int, struct servtab *));
346int		setconfig __P((void));
347void		setup __P((struct servtab *));
348char	       *sskip __P((char **));
349char	       *skip __P((char **));
350void		tcpmux __P((int, struct servtab *));
351void		usage __P((void));
352void		logpid __P((void));
353void		register_rpc __P((struct servtab *sep));
354void		unregister_rpc __P((struct servtab *sep));
355void		bump_nofile __P((void));
356void		inetd_setproctitle __P((char *, int));
357void		initring __P((void));
358long		machtime __P((void));
359static int	getline __P((int, char *, int));
360int		main __P((int, char *[], char *[]));
361
362struct biltin {
363	char	*bi_service;		/* internally provided service name */
364	int	bi_socktype;		/* type of socket supported */
365	short	bi_fork;		/* 1 if should fork before call */
366	short	bi_wait;		/* 1 if should wait for child */
367	void	(*bi_fn) __P((int, struct servtab *));
368					/* function which performs it */
369} biltins[] = {
370	/* Echo received data */
371	{ "echo",	SOCK_STREAM,	1, 0,	echo_stream },
372	{ "echo",	SOCK_DGRAM,	0, 0,	echo_dg },
373
374	/* Internet /dev/null */
375	{ "discard",	SOCK_STREAM,	1, 0,	discard_stream },
376	{ "discard",	SOCK_DGRAM,	0, 0,	discard_dg },
377
378	/* Return 32 bit time since 1970 */
379	{ "time",	SOCK_STREAM,	0, 0,	machtime_stream },
380	{ "time",	SOCK_DGRAM,	0, 0,	machtime_dg },
381
382	/* Return human-readable time */
383	{ "daytime",	SOCK_STREAM,	0, 0,	daytime_stream },
384	{ "daytime",	SOCK_DGRAM,	0, 0,	daytime_dg },
385
386	/* Familiar character generator */
387	{ "chargen",	SOCK_STREAM,	1, 0,	chargen_stream },
388	{ "chargen",	SOCK_DGRAM,	0, 0,	chargen_dg },
389
390	{ "tcpmux",	SOCK_STREAM,	1, 0,	tcpmux },
391
392	{ NULL }
393};
394
395#define NUMINT	(sizeof(intab) / sizeof(struct inent))
396char	*CONFIG = _PATH_INETDCONF;
397char	**Argv;
398char 	*LastArg;
399extern char	*__progname;
400
401#ifdef sun
402/*
403 * Sun's RPC library caches the result of `dtablesize()'
404 * This is incompatible with our "bumping" of file descriptors "on demand"
405 */
406int
407_rpc_dtablesize()
408{
409	return rlim_ofile_cur;
410}
411#endif
412
413int
414main(argc, argv, envp)
415	int argc;
416	char *argv[], *envp[];
417{
418	struct servtab *sep, *nsep;
419	struct sigvec sv;
420	int ch, dofork;
421	pid_t pid;
422
423	Argv = argv;
424	if (envp == 0 || *envp == 0)
425		envp = argv;
426	while (*envp)
427		envp++;
428	LastArg = envp[-1] + strlen(envp[-1]);
429
430	while ((ch = getopt(argc, argv,
431#ifdef LIBWRAP
432					"dl"
433#else
434					"d"
435#endif
436					   )) != -1)
437		switch(ch) {
438		case 'd':
439			debug = 1;
440			options |= SO_DEBUG;
441			break;
442#ifdef LIBWRAP
443		case 'l':
444			lflag = 1;
445			break;
446#endif
447		case '?':
448		default:
449			usage();
450		}
451	argc -= optind;
452	argv += optind;
453
454	if (argc > 0)
455		CONFIG = argv[0];
456
457	if (debug == 0)
458		daemon(0, 0);
459	openlog(__progname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
460	logpid();
461
462#ifdef RLIMIT_NOFILE
463	if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
464		syslog(LOG_ERR, "getrlimit: %m");
465	} else {
466		rlim_ofile_cur = rlim_ofile.rlim_cur;
467		if (rlim_ofile_cur == RLIM_INFINITY)	/* ! */
468			rlim_ofile_cur = OPEN_MAX;
469	}
470#endif
471
472	memset(&sv, 0, sizeof(sv));
473	sv.sv_mask = SIGBLOCK;
474	sv.sv_handler = retry;
475	sigvec(SIGALRM, &sv, (struct sigvec *)0);
476	config(SIGHUP);
477	sv.sv_handler = config;
478	sigvec(SIGHUP, &sv, (struct sigvec *)0);
479	sv.sv_handler = reapchild;
480	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
481	sv.sv_handler = goaway;
482	sigvec(SIGTERM, &sv, (struct sigvec *)0);
483	sv.sv_handler = goaway;
484	sigvec(SIGINT, &sv, (struct sigvec *)0);
485	sv.sv_mask = 0L;
486	sv.sv_handler = SIG_IGN;
487	sigvec(SIGPIPE, &sv, (struct sigvec *)0);
488
489	{
490		/* space for daemons to overwrite environment for ps */
491#define	DUMMYSIZE	100
492		char dummy[DUMMYSIZE];
493
494		(void)memset(dummy, 'x', DUMMYSIZE - 1);
495		dummy[DUMMYSIZE - 1] = '\0';
496
497		(void)setenv("inetd_dummy", dummy, 1);
498	}
499
500	for (;;) {
501	    int n, ctrl;
502	    fd_set readable;
503
504	    if (nsock == 0) {
505		(void) sigblock(SIGBLOCK);
506		while (nsock == 0)
507		    sigpause(0L);
508		(void) sigsetmask(0L);
509	    }
510	    readable = allsock;
511	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
512		(fd_set *)0, (struct timeval *)0)) <= 0) {
513		    if (n == -1 && errno != EINTR) {
514			syslog(LOG_WARNING, "select: %m");
515			sleep(1);
516		    }
517		    continue;
518	    }
519	    for (sep = servtab; n && sep; sep = nsep) {
520	    nsep = sep->se_next;
521	    if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
522		n--;
523		if (debug)
524			fprintf(stderr, "someone wants %s\n", sep->se_service);
525		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
526			/* XXX here do the libwrap check-before-accept */
527			ctrl = accept(sep->se_fd, (struct sockaddr *)0,
528			    (int *)0);
529			if (debug)
530				fprintf(stderr, "accept, ctrl %d\n", ctrl);
531			if (ctrl < 0) {
532				if (errno != EINTR)
533					syslog(LOG_WARNING,
534					    "accept (for %s): %m",
535					    sep->se_service);
536				continue;
537			}
538		} else
539			ctrl = sep->se_fd;
540		(void) sigblock(SIGBLOCK);
541		pid = 0;
542#ifdef LIBWRAP_INTERNAL
543		dofork = 1;
544#else
545		dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
546#endif
547		if (dofork) {
548			if (sep->se_count++ == 0)
549			    (void)gettimeofday(&sep->se_time,
550			        (struct timezone *)0);
551			else if (sep->se_count >= sep->se_max) {
552				struct timeval now;
553
554				(void)gettimeofday(&now, (struct timezone *)0);
555				if (now.tv_sec - sep->se_time.tv_sec >
556				    CNT_INTVL) {
557					sep->se_time = now;
558					sep->se_count = 1;
559				} else {
560					syslog(LOG_ERR,
561			"%s/%s server failing (looping), service terminated\n",
562					    sep->se_service, sep->se_proto);
563					if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
564					    close(ctrl);
565					close_sep(sep);
566					sigsetmask(0L);
567					if (!timingout) {
568						timingout = 1;
569						alarm(RETRYTIME);
570					}
571					continue;
572				}
573			}
574			pid = fork();
575			if (pid < 0) {
576				syslog(LOG_ERR, "fork: %m");
577				if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
578					close(ctrl);
579				sigsetmask(0L);
580				sleep(1);
581				continue;
582			}
583			if (pid != 0 && sep->se_wait) {
584				sep->se_wait = pid;
585				FD_CLR(sep->se_fd, &allsock);
586				nsock--;
587			}
588			if (pid == 0) {
589				sv.sv_mask = 0L;
590				sv.sv_handler = SIG_DFL;
591				sigvec(SIGPIPE, &sv, (struct sigvec *)0);
592				if (debug)
593					setsid();
594			}
595		}
596		sigsetmask(0L);
597		if (pid == 0) {
598			run_service(ctrl, sep);
599			if (dofork)
600				exit(0);
601		}
602		if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
603			close(ctrl);
604	    }
605	    }
606	}
607}
608
609void
610run_service(ctrl, sep)
611	int ctrl;
612	struct servtab *sep;
613{
614	struct passwd *pwd;
615	struct group *grp = NULL;	/* XXX gcc */
616	char buf[7];
617#ifdef LIBWRAP
618	struct request_info req;
619	int denied;
620	char *service = NULL;	/* XXX gcc */
621#endif
622
623#ifdef LIBWRAP
624#ifndef LIBWRAP_INTERNAL
625	if (sep->se_bi == 0)
626#endif
627	if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
628		request_init(&req, RQ_DAEMON, sep->se_argv[0] ?
629		    sep->se_argv[0] : sep->se_service, RQ_FILE, ctrl, NULL);
630		fromhost(&req);
631		denied = !hosts_access(&req);
632		if (denied || lflag) {
633			sp = getservbyport(sep->se_ctrladdr_in.sin_port,
634			    sep->se_proto);
635			if (sp == NULL) {
636				(void)snprintf(buf, sizeof buf, "%d",
637				    ntohs(sep->se_ctrladdr_in.sin_port));
638				service = buf;
639			} else
640				service = sp->s_name;
641		}
642		if (denied) {
643			syslog(deny_severity,
644			    "refused connection from %.500s, service %s (%s)",
645			    eval_client(&req), service, sep->se_proto);
646			goto reject;
647		}
648		if (lflag) {
649			syslog(allow_severity,
650			    "connection from %.500s, service %s (%s)",
651			    eval_client(&req), service, sep->se_proto);
652		}
653	}
654#endif /* LIBWRAP */
655
656	if (sep->se_bi) {
657		(*sep->se_bi->bi_fn)(ctrl, sep);
658	} else {
659		if ((pwd = getpwnam(sep->se_user)) == NULL) {
660			syslog(LOG_ERR, "%s/%s: %s: No such user",
661			    sep->se_service, sep->se_proto, sep->se_user);
662			goto reject;
663		}
664		if (sep->se_group &&
665		    (grp = getgrnam(sep->se_group)) == NULL) {
666			syslog(LOG_ERR, "%s/%s: %s: No such group",
667			    sep->se_service, sep->se_proto, sep->se_group);
668			goto reject;
669		}
670		if (pwd->pw_uid) {
671			if (sep->se_group)
672				pwd->pw_gid = grp->gr_gid;
673			if (setgid(pwd->pw_gid) < 0) {
674				syslog(LOG_ERR,
675				 "%s/%s: can't set gid %d: %m", sep->se_service,
676				    sep->se_proto, pwd->pw_gid);
677				goto reject;
678			}
679			(void) initgroups(pwd->pw_name,
680			    pwd->pw_gid);
681			if (setuid(pwd->pw_uid) < 0) {
682				syslog(LOG_ERR,
683				 "%s/%s: can't set uid %d: %m", sep->se_service,
684				    sep->se_proto, pwd->pw_uid);
685				goto reject;
686			}
687		} else if (sep->se_group) {
688			(void) setgid((gid_t)grp->gr_gid);
689		}
690		if (debug)
691			fprintf(stderr, "%d execl %s\n",
692			    getpid(), sep->se_server);
693#ifdef MULOG
694		if (sep->se_log)
695			dolog(sep, ctrl);
696#endif
697		/* Set our control descriptor to not close-on-exec... */
698		if (fcntl(ctrl, F_SETFD, 0) < 0)
699			syslog(LOG_ERR, "fcntl (F_SETFD, 0): %m");
700		/* ...and dup it to stdin, stdout, and stderr. */
701		if (ctrl != 0) {
702			dup2(ctrl, 0);
703			close(ctrl);
704			ctrl = 0;
705		}
706		dup2(0, 1);
707		dup2(0, 2);
708#ifdef RLIMIT_NOFILE
709		if (rlim_ofile.rlim_cur != rlim_ofile_cur &&
710		    setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
711			syslog(LOG_ERR, "setrlimit: %m");
712#endif
713		execv(sep->se_server, sep->se_argv);
714		syslog(LOG_ERR, "cannot execute %s: %m", sep->se_server);
715	reject:
716		if (sep->se_socktype != SOCK_STREAM)
717			recv(ctrl, buf, sizeof (buf), 0);
718		_exit(1);
719	}
720}
721
722void
723reapchild(signo)
724	int signo;
725{
726	int status;
727	pid_t pid;
728	struct servtab *sep;
729
730	for (;;) {
731		pid = wait3(&status, WNOHANG, (struct rusage *)0);
732		if (pid <= 0)
733			break;
734		if (debug)
735			fprintf(stderr, "%d reaped, status %#x\n",
736			    pid, status);
737		for (sep = servtab; sep; sep = sep->se_next)
738			if (sep->se_wait == pid) {
739				if (WIFEXITED(status) && WEXITSTATUS(status))
740					syslog(LOG_WARNING,
741					    "%s: exit status 0x%x",
742					    sep->se_server, WEXITSTATUS(status));
743				else if (WIFSIGNALED(status))
744					syslog(LOG_WARNING,
745					    "%s: exit signal 0x%x",
746					    sep->se_server, WTERMSIG(status));
747				sep->se_wait = 1;
748				FD_SET(sep->se_fd, &allsock);
749				nsock++;
750				if (debug)
751					fprintf(stderr, "restored %s, fd %d\n",
752					    sep->se_service, sep->se_fd);
753			}
754	}
755}
756
757void
758config(signo)
759	int signo;
760{
761	struct servtab *sep, *cp, **sepp;
762	long omask;
763	int n;
764
765	if (!setconfig()) {
766		syslog(LOG_ERR, "%s: %m", CONFIG);
767		return;
768	}
769	for (sep = servtab; sep; sep = sep->se_next)
770		sep->se_checked = 0;
771	while ((cp = getconfigent())) {
772		for (sep = servtab; sep; sep = sep->se_next)
773			if (strcmp(sep->se_service, cp->se_service) == 0 &&
774			    strcmp(sep->se_hostaddr, cp->se_hostaddr) == 0 &&
775			    strcmp(sep->se_proto, cp->se_proto) == 0 &&
776			    ISMUX(sep) == ISMUX(cp))
777				break;
778		if (sep != 0) {
779			int i;
780
781#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
782
783			omask = sigblock(SIGBLOCK);
784			/*
785			 * sep->se_wait may be holding the pid of a daemon
786			 * that we're waiting for.  If so, don't overwrite
787			 * it unless the config file explicitly says don't
788			 * wait.
789			 */
790			if (cp->se_bi == 0 &&
791			    (sep->se_wait == 1 || cp->se_wait == 0))
792				sep->se_wait = cp->se_wait;
793			SWAP(char *, sep->se_user, cp->se_user);
794			SWAP(char *, sep->se_group, cp->se_group);
795			SWAP(char *, sep->se_server, cp->se_server);
796			for (i = 0; i < MAXARGV; i++)
797				SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
798			SWAP(int, cp->se_type, sep->se_type);
799			SWAP(int, cp->se_max, sep->se_max);
800#undef SWAP
801			if (isrpcservice(sep))
802				unregister_rpc(sep);
803			sep->se_rpcversl = cp->se_rpcversl;
804			sep->se_rpcversh = cp->se_rpcversh;
805			sigsetmask(omask);
806			freeconfig(cp);
807			if (debug)
808				print_service("REDO", sep);
809		} else {
810			sep = enter(cp);
811			if (debug)
812				print_service("ADD ", sep);
813		}
814		sep->se_checked = 1;
815
816		switch (sep->se_family) {
817		case AF_UNIX:
818			if (sep->se_fd != -1)
819				break;
820			n = strlen(sep->se_service);
821			if (n > sizeof(sep->se_ctrladdr_un.sun_path)) {
822				syslog(LOG_ERR, "%s: address too long",
823				    sep->se_service);
824				sep->se_checked = 0;
825				continue;
826			}
827			(void)unlink(sep->se_service);
828			strncpy(sep->se_ctrladdr_un.sun_path,
829			    sep->se_service, n);
830			sep->se_ctrladdr_un.sun_family = AF_UNIX;
831			sep->se_ctrladdr_size = n +
832			    sizeof(sep->se_ctrladdr_un) -
833			    sizeof(sep->se_ctrladdr_un.sun_path);
834			if (!ISMUX(sep))
835				setup(sep);
836			break;
837		case AF_INET:
838			sep->se_ctrladdr_in.sin_family = AF_INET;
839			if (!strcmp(sep->se_hostaddr,"*"))
840				sep->se_ctrladdr_in.sin_addr.s_addr =
841				    INADDR_ANY;
842			else if (!inet_aton(sep->se_hostaddr,
843			    &sep->se_ctrladdr_in.sin_addr)) {
844				/* Do we really want to support hostname lookups here? */
845				struct hostent *hp;
846				hp = gethostbyname(sep->se_hostaddr);
847				if (hp == 0) {
848					syslog(LOG_ERR, "%s: unknown host",
849					    sep->se_hostaddr);
850					sep->se_checked = 0;
851					continue;
852				} else if (hp->h_addrtype != AF_INET) {
853					syslog(LOG_ERR,
854				       "%s: address isn't an Internet address",
855					    sep->se_hostaddr);
856					sep->se_checked = 0;
857					continue;
858				} else if (hp->h_length != sizeof(struct in_addr)) {
859					syslog(LOG_ERR,
860		       "%s: address size wrong (under DNS corruption attack?)",
861					    sep->se_hostaddr);
862					sep->se_checked = 0;
863					continue;
864				} else {
865					memcpy(&sep->se_ctrladdr_in.sin_addr,
866					    hp->h_addr_list[0],
867					    sizeof(struct in_addr));
868				}
869			}
870			if (ISMUX(sep)) {
871				sep->se_fd = -1;
872				continue;
873			}
874			sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr_in);
875			if (isrpcservice(sep)) {
876				struct rpcent *rp;
877
878				sep->se_rpcprog = atoi(sep->se_service);
879				if (sep->se_rpcprog == 0) {
880					rp = getrpcbyname(sep->se_service);
881					if (rp == 0) {
882						syslog(LOG_ERR,
883						    "%s/%s: unknown service",
884						    sep->se_service,
885						    sep->se_proto);
886						sep->se_checked = 0;
887						continue;
888					}
889					sep->se_rpcprog = rp->r_number;
890				}
891				if (sep->se_fd == -1 && !ISMUX(sep))
892					setup(sep);
893				if (sep->se_fd != -1)
894					register_rpc(sep);
895			} else {
896				u_short port = htons(atoi(sep->se_service));
897
898				if (!port) {
899					sp = getservbyname(sep->se_service,
900					    sep->se_proto);
901					if (sp == 0) {
902						syslog(LOG_ERR,
903						    "%s/%s: unknown service",
904						    sep->se_service,
905						    sep->se_proto);
906						sep->se_checked = 0;
907						continue;
908					}
909					port = sp->s_port;
910				}
911				if (port != sep->se_ctrladdr_in.sin_port) {
912					sep->se_ctrladdr_in.sin_port = port;
913					if (sep->se_fd >= 0)
914						close_sep(sep);
915				}
916				if (sep->se_fd == -1 && !ISMUX(sep))
917					setup(sep);
918			}
919		}
920	}
921	endconfig();
922	/*
923	 * Purge anything not looked at above.
924	 */
925	omask = sigblock(SIGBLOCK);
926	sepp = &servtab;
927	while ((sep = *sepp)) {
928		if (sep->se_checked) {
929			sepp = &sep->se_next;
930			continue;
931		}
932		*sepp = sep->se_next;
933		if (sep->se_fd >= 0)
934			close_sep(sep);
935		if (isrpcservice(sep))
936			unregister_rpc(sep);
937		if (sep->se_family == AF_UNIX)
938			(void)unlink(sep->se_service);
939		if (debug)
940			print_service("FREE", sep);
941		freeconfig(sep);
942		free((char *)sep);
943	}
944	(void) sigsetmask(omask);
945}
946
947void
948retry(signo)
949	int signo;
950{
951	struct servtab *sep;
952
953	timingout = 0;
954	for (sep = servtab; sep; sep = sep->se_next) {
955		if (sep->se_fd == -1 && !ISMUX(sep)) {
956			switch (sep->se_family) {
957			case AF_UNIX:
958			case AF_INET:
959				setup(sep);
960				if (sep->se_fd != -1 && isrpcservice(sep))
961					register_rpc(sep);
962				break;
963			}
964		}
965	}
966}
967
968void
969goaway(signo)
970	int signo;
971{
972	struct servtab *sep;
973
974	for (sep = servtab; sep; sep = sep->se_next) {
975		if (sep->se_fd == -1)
976			continue;
977
978		switch (sep->se_family) {
979		case AF_UNIX:
980			(void)unlink(sep->se_service);
981			break;
982		case AF_INET:
983			if (sep->se_wait == 1 && isrpcservice(sep))
984				unregister_rpc(sep);
985			break;
986		}
987		(void)close(sep->se_fd);
988	}
989	(void)unlink(_PATH_INETDPID);
990	exit(0);
991}
992
993void
994setup(sep)
995	struct servtab *sep;
996{
997	int on = 1;
998
999	if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
1000		if (debug)
1001			fprintf(stderr, "socket failed on %s/%s: %s\n",
1002			    sep->se_service, sep->se_proto, strerror(errno));
1003		syslog(LOG_ERR, "%s/%s: socket: %m",
1004		    sep->se_service, sep->se_proto);
1005		return;
1006	}
1007	/* Set all listening sockets to close-on-exec. */
1008	if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0)
1009		syslog(LOG_ERR, "fcntl (F_SETFD, FD_CLOEXEC): %m");
1010
1011#define	turnon(fd, opt) \
1012setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
1013	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
1014	    turnon(sep->se_fd, SO_DEBUG) < 0)
1015		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
1016	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
1017		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
1018#undef turnon
1019
1020	/* Set the socket buffer sizes, if specified. */
1021	if (sep->se_sndbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
1022	    SO_SNDBUF, (char *)&sep->se_sndbuf, sizeof(sep->se_sndbuf)) < 0)
1023		syslog(LOG_ERR, "setsockopt (SO_SNDBUF %d): %m",
1024		    sep->se_sndbuf);
1025	if (sep->se_rcvbuf != 0 && setsockopt(sep->se_fd, SOL_SOCKET,
1026	    SO_RCVBUF, (char *)&sep->se_rcvbuf, sizeof(sep->se_rcvbuf)) < 0)
1027		syslog(LOG_ERR, "setsockopt (SO_RCVBUF %d): %m",
1028		    sep->se_rcvbuf);
1029
1030	if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
1031		if (debug)
1032			fprintf(stderr, "bind failed on %s/%s: %s\n",
1033			    sep->se_service, sep->se_proto, strerror(errno));
1034		syslog(LOG_ERR, "%s/%s: bind: %m",
1035		    sep->se_service, sep->se_proto);
1036		(void) close(sep->se_fd);
1037		sep->se_fd = -1;
1038		if (!timingout) {
1039			timingout = 1;
1040			alarm(RETRYTIME);
1041		}
1042		return;
1043	}
1044	if (sep->se_socktype == SOCK_STREAM)
1045		listen(sep->se_fd, 10);
1046
1047	FD_SET(sep->se_fd, &allsock);
1048	nsock++;
1049	if (sep->se_fd > maxsock) {
1050		maxsock = sep->se_fd;
1051		if (maxsock > rlim_ofile_cur - FD_MARGIN)
1052			bump_nofile();
1053	}
1054	if (debug)
1055		fprintf(stderr, "registered %s on %d\n",
1056		    sep->se_server, sep->se_fd);
1057}
1058
1059/*
1060 * Finish with a service and its socket.
1061 */
1062void
1063close_sep(sep)
1064	struct servtab *sep;
1065{
1066	if (sep->se_fd >= 0) {
1067		nsock--;
1068		FD_CLR(sep->se_fd, &allsock);
1069		(void) close(sep->se_fd);
1070		sep->se_fd = -1;
1071	}
1072	sep->se_count = 0;
1073	/*
1074	 * Don't keep the pid of this running deamon: when reapchild()
1075	 * reaps this pid, it would erroneously increment nsock.
1076	 */
1077	if (sep->se_wait > 1)
1078		sep->se_wait = 1;
1079}
1080
1081void
1082register_rpc(sep)
1083	struct servtab *sep;
1084{
1085#ifdef RPC
1086	int n;
1087	struct sockaddr_in sin;
1088	struct protoent *pp;
1089
1090	if ((pp = getprotobyname(sep->se_proto+4)) == NULL) {
1091		syslog(LOG_ERR, "%s: getproto: %m",
1092		    sep->se_proto);
1093		return;
1094	}
1095	n = sizeof sin;
1096	if (getsockname(sep->se_fd, (struct sockaddr *)&sin, &n) < 0) {
1097		syslog(LOG_ERR, "%s/%s: getsockname: %m",
1098		    sep->se_service, sep->se_proto);
1099		return;
1100	}
1101
1102	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
1103		if (debug)
1104			fprintf(stderr, "pmap_set: %u %u %u %u\n",
1105			    sep->se_rpcprog, n, pp->p_proto,
1106			    ntohs(sin.sin_port));
1107		(void)pmap_unset(sep->se_rpcprog, n);
1108		if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(sin.sin_port)))
1109			syslog(LOG_ERR, "pmap_set: %u %u %u %u: %m",
1110			    sep->se_rpcprog, n, pp->p_proto,
1111			    ntohs(sin.sin_port));
1112	}
1113#endif /* RPC */
1114}
1115
1116void
1117unregister_rpc(sep)
1118	struct servtab *sep;
1119{
1120#ifdef RPC
1121	int n;
1122
1123	for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
1124		if (debug)
1125			fprintf(stderr, "pmap_unset(%u, %u)\n",
1126			    sep->se_rpcprog, n);
1127		if (!pmap_unset(sep->se_rpcprog, n))
1128			syslog(LOG_ERR, "pmap_unset(%u, %u)\n",
1129			    sep->se_rpcprog, n);
1130	}
1131#endif /* RPC */
1132}
1133
1134
1135struct servtab *
1136enter(cp)
1137	struct servtab *cp;
1138{
1139	struct servtab *sep;
1140	long omask;
1141
1142	sep = (struct servtab *)malloc(sizeof (*sep));
1143	if (sep == (struct servtab *)0) {
1144		syslog(LOG_ERR, "Out of memory.");
1145		exit(-1);
1146	}
1147	*sep = *cp;
1148	sep->se_fd = -1;
1149	sep->se_rpcprog = -1;
1150	omask = sigblock(SIGBLOCK);
1151	sep->se_next = servtab;
1152	servtab = sep;
1153	sigsetmask(omask);
1154	return (sep);
1155}
1156
1157FILE	*fconfig = NULL;
1158struct	servtab serv;
1159char	line[LINE_MAX];
1160char    *defhost;
1161
1162int
1163setconfig()
1164{
1165	if (defhost) free(defhost);
1166	defhost = newstr("*");
1167	if (fconfig != NULL) {
1168		fseek(fconfig, 0L, SEEK_SET);
1169		return (1);
1170	}
1171	fconfig = fopen(CONFIG, "r");
1172	return (fconfig != NULL);
1173}
1174
1175void
1176endconfig()
1177{
1178	if (fconfig) {
1179		(void) fclose(fconfig);
1180		fconfig = NULL;
1181	}
1182	if (defhost) {
1183		free(defhost);
1184		defhost = 0;
1185	}
1186}
1187
1188struct servtab *
1189getconfigent()
1190{
1191	struct servtab *sep = &serv;
1192	int argc, val;
1193	char *cp, *cp0, *arg, *buf0, *buf1, *sz0, *sz1;
1194	static char TCPMUX_TOKEN[] = "tcpmux/";
1195#define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
1196	char *hostdelim;
1197
1198more:
1199#ifdef MULOG
1200	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0')) {
1201		/* Avoid use of `skip' if there is a danger of it looking
1202		 * at continuation lines.
1203		 */
1204		do {
1205			cp++;
1206		} while (*cp == ' ' || *cp == '\t');
1207		if (*cp == '\0')
1208			continue;
1209		if ((arg = skip(&cp)) == NULL)
1210			continue;
1211		if (strcmp(arg, "DOMAIN"))
1212			continue;
1213		if (curdom)
1214			free(curdom);
1215		curdom = NULL;
1216		while (*cp == ' ' || *cp == '\t')
1217			cp++;
1218		if (*cp == '\0')
1219			continue;
1220		arg = cp;
1221		while (*cp && *cp != ' ' && *cp != '\t')
1222			cp++;
1223		if (*cp != '\0')
1224			*cp++ = '\0';
1225		curdom = newstr(arg);
1226	}
1227#else
1228	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
1229		;
1230#endif
1231	if (cp == NULL)
1232		return ((struct servtab *)0);
1233	/*
1234	 * clear the static buffer, since some fields (se_ctrladdr,
1235	 * for example) don't get initialized here.
1236	 */
1237	memset((caddr_t)sep, 0, sizeof *sep);
1238	arg = skip(&cp);
1239	if (cp == NULL) {
1240		/* got an empty line containing just blanks/tabs. */
1241		goto more;
1242	}
1243	/* Check for a host name. */
1244	hostdelim = strrchr(arg, ':');
1245	if (hostdelim) {
1246		*hostdelim = '\0';
1247		sep->se_hostaddr = newstr(arg);
1248		arg = hostdelim + 1;
1249		/*
1250		 * If the line is of the form `host:', then just change the
1251		 * default host for the following lines.
1252		 */
1253		if (*arg == '\0') {
1254			arg = skip(&cp);
1255			if (cp == NULL) {
1256				free(defhost);
1257				defhost = sep->se_hostaddr;
1258				goto more;
1259			}
1260		}
1261	} else
1262		sep->se_hostaddr = newstr(defhost);
1263	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
1264		char *c = arg + MUX_LEN;
1265		if (*c == '+') {
1266			sep->se_type = MUXPLUS_TYPE;
1267			c++;
1268		} else
1269			sep->se_type = MUX_TYPE;
1270		sep->se_service = newstr(c);
1271	} else {
1272		sep->se_service = newstr(arg);
1273		sep->se_type = NORM_TYPE;
1274	}
1275
1276	arg = sskip(&cp);
1277	if (strcmp(arg, "stream") == 0)
1278		sep->se_socktype = SOCK_STREAM;
1279	else if (strcmp(arg, "dgram") == 0)
1280		sep->se_socktype = SOCK_DGRAM;
1281	else if (strcmp(arg, "rdm") == 0)
1282		sep->se_socktype = SOCK_RDM;
1283	else if (strcmp(arg, "seqpacket") == 0)
1284		sep->se_socktype = SOCK_SEQPACKET;
1285	else if (strcmp(arg, "raw") == 0)
1286		sep->se_socktype = SOCK_RAW;
1287	else
1288		sep->se_socktype = -1;
1289
1290	sep->se_proto = newstr(sskip(&cp));
1291
1292#define	MALFORMED(arg) \
1293do { \
1294	syslog(LOG_ERR, "%s: malformed buffer size option `%s'", \
1295	    sep->se_service, (arg)); \
1296	goto more; \
1297} while (0)
1298
1299#define	GETVAL(arg) \
1300do { \
1301	if (!isdigit(*(arg))) \
1302		MALFORMED(arg); \
1303	val = strtol((arg), &cp0, 10); \
1304	if (cp0 != NULL) { \
1305		if (cp0[1] != '\0') \
1306			MALFORMED((arg)); \
1307		if (cp0[0] == 'k') \
1308			val *= 1024; \
1309		if (cp0[0] == 'm') \
1310			val *= 1024 * 1024; \
1311	} \
1312	if (val < 1) { \
1313		syslog(LOG_ERR, "%s: invalid buffer size `%s'", \
1314		    sep->se_service, (arg)); \
1315		goto more; \
1316	} \
1317} while (0)
1318
1319#define	ASSIGN(arg) \
1320do { \
1321	if (strcmp((arg), "sndbuf") == 0) \
1322		sep->se_sndbuf = val; \
1323	else if (strcmp((arg), "rcvbuf") == 0) \
1324		sep->se_rcvbuf = val; \
1325	else \
1326		MALFORMED((arg)); \
1327} while (0)
1328
1329	/*
1330	 * Extract the send and receive buffer sizes before parsing
1331	 * the protocol.
1332	 */
1333	sep->se_sndbuf = sep->se_rcvbuf = 0;
1334	buf0 = buf1 = sz0 = sz1 = NULL;
1335	if ((buf0 = strchr(sep->se_proto, ',')) != NULL) {
1336		/* Not meaningful for Tcpmux services. */
1337		if (sep->se_type != NORM_TYPE) {
1338			syslog(LOG_ERR, "%s: can't specify buffer sizes for "
1339			    "tcpmux services", sep->se_service);
1340			goto more;
1341		}
1342
1343		/* Skip the , */
1344		*buf0++ = '\0';
1345
1346		/* Check to see if another socket buffer size was specified. */
1347		if ((buf1 = strchr(buf0, ',')) != NULL) {
1348			/* Skip the , */
1349			*buf1++ = '\0';
1350
1351			/* Make sure a 3rd one wasn't specified. */
1352			if (strchr(buf1, ',') != NULL) {
1353				syslog(LOG_ERR, "%s: too many buffer sizes",
1354				    sep->se_service);
1355				goto more;
1356			}
1357
1358			/* Locate the size. */
1359			if ((sz1 = strchr(buf1, '=')) == NULL)
1360				MALFORMED(buf1);
1361
1362			/* Skip the = */
1363			*sz1++ = '\0';
1364		}
1365
1366		/* Locate the size. */
1367		if ((sz0 = strchr(buf0, '=')) == NULL)
1368			MALFORMED(buf0);
1369
1370		/* Skip the = */
1371		*sz0++ = '\0';
1372
1373		GETVAL(sz0);
1374		ASSIGN(buf0);
1375
1376		if (buf1 != NULL) {
1377			GETVAL(sz1);
1378			ASSIGN(buf1);
1379		}
1380	}
1381
1382#undef ASSIGN
1383#undef GETVAL
1384#undef MALFORMED
1385
1386	if (strcmp(sep->se_proto, "unix") == 0) {
1387		sep->se_family = AF_UNIX;
1388	} else {
1389		sep->se_family = AF_INET;
1390		if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
1391#ifdef RPC
1392			char *cp, *ccp;
1393			cp = strchr(sep->se_service, '/');
1394			if (cp == 0) {
1395				syslog(LOG_ERR, "%s: no rpc version",
1396				    sep->se_service);
1397				goto more;
1398			}
1399			*cp++ = '\0';
1400			sep->se_rpcversl = sep->se_rpcversh =
1401			    strtol(cp, &ccp, 0);
1402			if (ccp == cp) {
1403		badafterall:
1404				syslog(LOG_ERR, "%s/%s: bad rpc version",
1405				    sep->se_service, cp);
1406				goto more;
1407			}
1408			if (*ccp == '-') {
1409				cp = ccp + 1;
1410				sep->se_rpcversh = strtol(cp, &ccp, 0);
1411				if (ccp == cp)
1412					goto badafterall;
1413			}
1414#else
1415			syslog(LOG_ERR, "%s: rpc services not suported",
1416			    sep->se_service);
1417			goto more;
1418#endif /* RPC */
1419		}
1420	}
1421	arg = sskip(&cp);
1422	{
1423		char *cp;
1424		cp = strchr(arg, '.');
1425		if (cp) {
1426			*cp++ = '\0';
1427			sep->se_max = atoi(cp);
1428		} else
1429			sep->se_max = TOOMANY;
1430	}
1431	sep->se_wait = strcmp(arg, "wait") == 0;
1432	if (ISMUX(sep)) {
1433		/*
1434		 * Silently enforce "nowait" for TCPMUX services since
1435		 * they don't have an assigned port to listen on.
1436		 */
1437		sep->se_wait = 0;
1438
1439		if (strcmp(sep->se_proto, "tcp")) {
1440			syslog(LOG_ERR,
1441			    "%s: bad protocol for tcpmux service %s",
1442			    CONFIG, sep->se_service);
1443			goto more;
1444		}
1445		if (sep->se_socktype != SOCK_STREAM) {
1446			syslog(LOG_ERR,
1447			    "%s: bad socket type for tcpmux service %s",
1448			    CONFIG, sep->se_service);
1449			goto more;
1450		}
1451	}
1452	sep->se_user = newstr(sskip(&cp));
1453	if ((sep->se_group = strchr(sep->se_user, '.')))
1454		*sep->se_group++ = '\0';
1455	sep->se_server = newstr(sskip(&cp));
1456	if (strcmp(sep->se_server, "internal") == 0) {
1457		struct biltin *bi;
1458
1459		for (bi = biltins; bi->bi_service; bi++)
1460			if (bi->bi_socktype == sep->se_socktype &&
1461			    strcmp(bi->bi_service, sep->se_service) == 0)
1462				break;
1463		if (bi->bi_service == 0) {
1464			syslog(LOG_ERR, "internal service %s unknown",
1465			    sep->se_service);
1466			goto more;
1467		}
1468		sep->se_bi = bi;
1469		sep->se_wait = bi->bi_wait;
1470	} else
1471		sep->se_bi = NULL;
1472	argc = 0;
1473	for (arg = skip(&cp); cp; arg = skip(&cp)) {
1474#if MULOG
1475		char *colon;
1476
1477		if (argc == 0 && (colon = strrchr(arg, ':'))) {
1478			while (arg < colon) {
1479				int	x;
1480				char	*ccp;
1481
1482				switch (*arg++) {
1483				case 'l':
1484					x = 1;
1485					if (isdigit(*arg)) {
1486						x = strtol(arg, &ccp, 0);
1487						if (ccp == arg)
1488							break;
1489						arg = ccp;
1490					}
1491					sep->se_log &= ~MULOG_RFC931;
1492					sep->se_log |= x;
1493					break;
1494				case 'a':
1495					sep->se_log |= MULOG_RFC931;
1496					break;
1497				default:
1498					break;
1499				}
1500			}
1501			arg = colon + 1;
1502		}
1503#endif
1504		if (argc < MAXARGV)
1505			sep->se_argv[argc++] = newstr(arg);
1506	}
1507	while (argc <= MAXARGV)
1508		sep->se_argv[argc++] = NULL;
1509	return (sep);
1510}
1511
1512void
1513freeconfig(cp)
1514	struct servtab *cp;
1515{
1516	int i;
1517
1518	if (cp->se_hostaddr)
1519		free(cp->se_hostaddr);
1520	if (cp->se_service)
1521		free(cp->se_service);
1522	if (cp->se_proto)
1523		free(cp->se_proto);
1524	if (cp->se_user)
1525		free(cp->se_user);
1526	/* Note: se_group is part of the newstr'ed se_user */
1527	if (cp->se_server)
1528		free(cp->se_server);
1529	for (i = 0; i < MAXARGV; i++)
1530		if (cp->se_argv[i])
1531			free(cp->se_argv[i]);
1532}
1533
1534
1535/*
1536 * Safe skip - if skip returns null, log a syntax error in the
1537 * configuration file and exit.
1538 */
1539char *
1540sskip(cpp)
1541	char **cpp;
1542{
1543	char *cp;
1544
1545	cp = skip(cpp);
1546	if (cp == NULL) {
1547		syslog(LOG_ERR, "%s: syntax error", CONFIG);
1548		exit(-1);
1549	}
1550	return (cp);
1551}
1552
1553char *
1554skip(cpp)
1555	char **cpp;
1556{
1557	char *cp = *cpp;
1558	char *start;
1559
1560	if (*cpp == NULL)
1561		return ((char *)0);
1562
1563again:
1564	while (*cp == ' ' || *cp == '\t')
1565		cp++;
1566	if (*cp == '\0') {
1567		int c;
1568
1569		c = getc(fconfig);
1570		(void) ungetc(c, fconfig);
1571		if (c == ' ' || c == '\t')
1572			if ((cp = nextline(fconfig)))
1573				goto again;
1574		*cpp = (char *)0;
1575		return ((char *)0);
1576	}
1577	start = cp;
1578	while (*cp && *cp != ' ' && *cp != '\t')
1579		cp++;
1580	if (*cp != '\0')
1581		*cp++ = '\0';
1582	*cpp = cp;
1583	return (start);
1584}
1585
1586char *
1587nextline(fd)
1588	FILE *fd;
1589{
1590	char *cp;
1591
1592	if (fgets(line, sizeof (line), fd) == NULL)
1593		return ((char *)0);
1594	cp = strchr(line, '\n');
1595	if (cp)
1596		*cp = '\0';
1597	return (line);
1598}
1599
1600char *
1601newstr(cp)
1602	char *cp;
1603{
1604	if ((cp = strdup(cp ? cp : "")))
1605		return (cp);
1606	syslog(LOG_ERR, "strdup: %m");
1607	exit(-1);
1608}
1609
1610void
1611inetd_setproctitle(a, s)
1612	char *a;
1613	int s;
1614{
1615	int size;
1616	char *cp;
1617	struct sockaddr_in sin;
1618	char buf[80];
1619
1620	cp = Argv[0];
1621	size = sizeof(sin);
1622	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
1623		(void)snprintf(buf, sizeof buf, "-%s [%s]", a,
1624		    inet_ntoa(sin.sin_addr));
1625	else
1626		(void)snprintf(buf, sizeof buf, "-%s", a);
1627	strncpy(cp, buf, LastArg - cp);
1628	cp += strlen(cp);
1629	while (cp < LastArg)
1630		*cp++ = ' ';
1631}
1632
1633void
1634logpid()
1635{
1636	FILE *fp;
1637
1638	if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
1639		fprintf(fp, "%u\n", getpid());
1640		(void)fclose(fp);
1641	}
1642}
1643
1644void
1645bump_nofile()
1646{
1647#ifdef RLIMIT_NOFILE
1648
1649#define FD_CHUNK	32
1650
1651	struct rlimit rl;
1652
1653	if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
1654		syslog(LOG_ERR, "getrlimit: %m");
1655		return;
1656	}
1657	rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
1658	if (rl.rlim_cur <= rlim_ofile_cur) {
1659		syslog(LOG_ERR,
1660		    "bump_nofile: cannot extend file limit, max = %d",
1661		    (int)rl.rlim_cur);
1662		return;
1663	}
1664
1665	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
1666		syslog(LOG_ERR, "setrlimit: %m");
1667		return;
1668	}
1669
1670	rlim_ofile_cur = rl.rlim_cur;
1671	return;
1672
1673#else
1674	syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
1675	return;
1676#endif
1677}
1678
1679/*
1680 * Internet services provided internally by inetd:
1681 */
1682#define	BUFSIZE	4096
1683
1684/* ARGSUSED */
1685void
1686echo_stream(s, sep)		/* Echo service -- echo data back */
1687	int s;
1688	struct servtab *sep;
1689{
1690	char buffer[BUFSIZE];
1691	int i;
1692
1693	inetd_setproctitle(sep->se_service, s);
1694	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1695	    write(s, buffer, i) > 0)
1696		;
1697}
1698
1699/* ARGSUSED */
1700void
1701echo_dg(s, sep)			/* Echo service -- echo data back */
1702	int s;
1703	struct servtab *sep;
1704{
1705	char buffer[BUFSIZE];
1706	int i, size;
1707	struct sockaddr sa;
1708
1709	size = sizeof(sa);
1710	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
1711		return;
1712	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
1713}
1714
1715/* ARGSUSED */
1716void
1717discard_stream(s, sep)		/* Discard service -- ignore data */
1718	int s;
1719	struct servtab *sep;
1720{
1721	char buffer[BUFSIZE];
1722
1723	inetd_setproctitle(sep->se_service, s);
1724	while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1725			errno == EINTR)
1726		;
1727}
1728
1729/* ARGSUSED */
1730void
1731discard_dg(s, sep)		/* Discard service -- ignore data */
1732	int s;
1733	struct servtab *sep;
1734{
1735	char buffer[BUFSIZE];
1736
1737	(void) read(s, buffer, sizeof(buffer));
1738}
1739
1740#include <ctype.h>
1741#define LINESIZ 72
1742char ring[128];
1743char *endring;
1744
1745void
1746initring()
1747{
1748	int i;
1749
1750	endring = ring;
1751
1752	for (i = 0; i <= 128; ++i)
1753		if (isprint(i))
1754			*endring++ = i;
1755}
1756
1757/* ARGSUSED */
1758void
1759chargen_stream(s, sep)		/* Character generator */
1760	int s;
1761	struct servtab *sep;
1762{
1763	int len;
1764	char *rs, text[LINESIZ+2];
1765
1766	inetd_setproctitle(sep->se_service, s);
1767
1768	if (!endring) {
1769		initring();
1770		rs = ring;
1771	}
1772
1773	text[LINESIZ] = '\r';
1774	text[LINESIZ + 1] = '\n';
1775	for (rs = ring;;) {
1776		if ((len = endring - rs) >= LINESIZ)
1777			memmove(text, rs, LINESIZ);
1778		else {
1779			memmove(text, rs, len);
1780			memmove(text + len, ring, LINESIZ - len);
1781		}
1782		if (++rs == endring)
1783			rs = ring;
1784		if (write(s, text, sizeof(text)) != sizeof(text))
1785			break;
1786	}
1787}
1788
1789/* ARGSUSED */
1790void
1791chargen_dg(s, sep)		/* Character generator */
1792	int s;
1793	struct servtab *sep;
1794{
1795	struct sockaddr sa;
1796	static char *rs;
1797	int len, size;
1798	char text[LINESIZ+2];
1799
1800	if (endring == 0) {
1801		initring();
1802		rs = ring;
1803	}
1804
1805	size = sizeof(sa);
1806	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
1807		return;
1808
1809	if ((len = endring - rs) >= LINESIZ)
1810		memmove(text, rs, LINESIZ);
1811	else {
1812		memmove(text, rs, len);
1813		memmove(text + len, ring, LINESIZ - len);
1814	}
1815	if (++rs == endring)
1816		rs = ring;
1817	text[LINESIZ] = '\r';
1818	text[LINESIZ + 1] = '\n';
1819	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1820}
1821
1822/*
1823 * Return a machine readable date and time, in the form of the
1824 * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
1825 * returns the number of seconds since midnight, Jan 1, 1970,
1826 * we must add 2208988800 seconds to this figure to make up for
1827 * some seventy years Bell Labs was asleep.
1828 */
1829
1830long
1831machtime()
1832{
1833	struct timeval tv;
1834
1835	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1836		if (debug)
1837			fprintf(stderr, "Unable to get time of day\n");
1838		return (0L);
1839	}
1840#define	OFFSET ((u_long)25567 * 24*60*60)
1841	return (htonl((long)(tv.tv_sec + OFFSET)));
1842#undef OFFSET
1843}
1844
1845/* ARGSUSED */
1846void
1847machtime_stream(s, sep)
1848	int s;
1849	struct servtab *sep;
1850{
1851	long result;
1852
1853	result = machtime();
1854	(void) write(s, (char *) &result, sizeof(result));
1855}
1856
1857/* ARGSUSED */
1858void
1859machtime_dg(s, sep)
1860	int s;
1861	struct servtab *sep;
1862{
1863	long result;
1864	struct sockaddr sa;
1865	int size;
1866
1867	size = sizeof(sa);
1868	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
1869		return;
1870	result = machtime();
1871	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1872}
1873
1874/* ARGSUSED */
1875void
1876daytime_stream(s, sep)		/* Return human-readable time of day */
1877	int s;
1878	struct servtab *sep;
1879{
1880	char buffer[256];
1881	time_t clock;
1882	int len;
1883
1884	clock = time((time_t *) 0);
1885
1886	len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clock));
1887	(void) write(s, buffer, len);
1888}
1889
1890/* ARGSUSED */
1891void
1892daytime_dg(s, sep)		/* Return human-readable time of day */
1893	int s;
1894	struct servtab *sep;
1895{
1896	char buffer[256];
1897	time_t clock;
1898	struct sockaddr sa;
1899	int size, len;
1900
1901	clock = time((time_t *) 0);
1902
1903	size = sizeof(sa);
1904	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1905		return;
1906	len = snprintf(buffer, sizeof buffer, "%.24s\r\n", ctime(&clock));
1907	(void) sendto(s, buffer, len, 0, &sa, sizeof(sa));
1908}
1909
1910/*
1911 * print_service:
1912 *	Dump relevant information to stderr
1913 */
1914void
1915print_service(action, sep)
1916	char *action;
1917	struct servtab *sep;
1918{
1919	if (isrpcservice(sep))
1920		fprintf(stderr,
1921		    "%s: %s rpcprog=%d, rpcvers = %d/%d, proto=%s, wait.max=%d.%d, user.group=%s.%s builtin=%lx server=%s\n",
1922		    action, sep->se_service,
1923		    sep->se_rpcprog, sep->se_rpcversh, sep->se_rpcversl, sep->se_proto,
1924		    sep->se_wait, sep->se_max, sep->se_user, sep->se_group,
1925		    (long)sep->se_bi, sep->se_server);
1926	else
1927		fprintf(stderr,
1928		    "%s: %s proto=%s, wait.max=%d.%d, user.group=%s.%s builtin=%lx server=%s\n",
1929		    action, sep->se_service, sep->se_proto,
1930		    sep->se_wait, sep->se_max, sep->se_user, sep->se_group,
1931		    (long)sep->se_bi, sep->se_server);
1932}
1933
1934void
1935usage()
1936{
1937
1938#ifdef LIBWRAP
1939	(void)fprintf(stderr, "usage: %s [-dl] [conf]\n", __progname);
1940#else
1941	(void)fprintf(stderr, "usage: %s [-d] [conf]\n", __progname);
1942#endif
1943	exit(1);
1944}
1945
1946
1947/*
1948 *  Based on TCPMUX.C by Mark K. Lottor November 1988
1949 *  sri-nic::ps:<mkl>tcpmux.c
1950 */
1951
1952static int		/* # of characters upto \r,\n or \0 */
1953getline(fd, buf, len)
1954	int fd;
1955	char *buf;
1956	int len;
1957{
1958	int count = 0, n;
1959
1960	do {
1961		n = read(fd, buf, len-count);
1962		if (n == 0)
1963			return (count);
1964		if (n < 0)
1965			return (-1);
1966		while (--n >= 0) {
1967			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1968				return (count);
1969			count++;
1970			buf++;
1971		}
1972	} while (count < len);
1973	return (count);
1974}
1975
1976#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
1977
1978#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
1979
1980void
1981tcpmux(ctrl, sep)
1982	int ctrl;
1983	struct servtab *sep;
1984{
1985	char service[MAX_SERV_LEN+1];
1986	int len;
1987
1988	/* Get requested service name */
1989	if ((len = getline(ctrl, service, MAX_SERV_LEN)) < 0) {
1990		strwrite(ctrl, "-Error reading service name\r\n");
1991		goto reject;
1992	}
1993	service[len] = '\0';
1994
1995	if (debug)
1996		fprintf(stderr, "tcpmux: someone wants %s\n", service);
1997
1998	/*
1999	 * Help is a required command, and lists available services,
2000	 * one per line.
2001	 */
2002	if (!strcasecmp(service, "help")) {
2003		strwrite(ctrl, "+Available services:\r\n");
2004		strwrite(ctrl, "help\r\n");
2005		for (sep = servtab; sep; sep = sep->se_next) {
2006			if (!ISMUX(sep))
2007				continue;
2008			(void)write(ctrl, sep->se_service,
2009			    strlen(sep->se_service));
2010			strwrite(ctrl, "\r\n");
2011		}
2012		goto reject;
2013	}
2014
2015	/* Try matching a service in inetd.conf with the request */
2016	for (sep = servtab; sep; sep = sep->se_next) {
2017		if (!ISMUX(sep))
2018			continue;
2019		if (!strcasecmp(service, sep->se_service)) {
2020			if (ISMUXPLUS(sep))
2021				strwrite(ctrl, "+Go\r\n");
2022			run_service(ctrl, sep);
2023			return;
2024		}
2025	}
2026	strwrite(ctrl, "-Service not available\r\n");
2027reject:
2028	_exit(1);
2029}
2030
2031
2032#ifdef MULOG
2033dolog(sep, ctrl)
2034	struct servtab *sep;
2035	int		ctrl;
2036{
2037	struct sockaddr		sa;
2038	struct sockaddr_in	*sin = (struct sockaddr_in *)&sa;
2039	int			len = sizeof(sa);
2040	struct hostent		*hp;
2041	char			*host, *dp, buf[BUFSIZ], *rfc931_name();
2042	int			connected = 1;
2043
2044	if (sep->se_family != AF_INET)
2045		return;
2046
2047	if (getpeername(ctrl, &sa, &len) < 0) {
2048		if (errno != ENOTCONN) {
2049			syslog(LOG_ERR, "getpeername: %m");
2050			return;
2051		}
2052		if (recvfrom(ctrl, buf, sizeof(buf), MSG_PEEK, &sa, &len) < 0) {
2053			syslog(LOG_ERR, "recvfrom: %m");
2054			return;
2055		}
2056		connected = 0;
2057	}
2058	if (sa.sa_family != AF_INET) {
2059		syslog(LOG_ERR, "unexpected address family %u", sa.sa_family);
2060		return;
2061	}
2062
2063	hp = gethostbyaddr((char *) &sin->sin_addr.s_addr,
2064				sizeof (sin->sin_addr.s_addr), AF_INET);
2065
2066	host = hp?hp->h_name:inet_ntoa(sin->sin_addr);
2067
2068	switch (sep->se_log & ~MULOG_RFC931) {
2069	case 0:
2070		return;
2071	case 1:
2072		if (curdom == NULL || *curdom == '\0')
2073			break;
2074		dp = host + strlen(host) - strlen(curdom);
2075		if (dp < host)
2076			break;
2077		if (debug)
2078			fprintf(stderr, "check \"%s\" against curdom \"%s\"\n",
2079			    host, curdom);
2080		if (strcasecmp(dp, curdom) == 0)
2081			return;
2082		break;
2083	case 2:
2084	default:
2085		break;
2086	}
2087
2088	openlog("", LOG_NOWAIT, MULOG);
2089
2090	if (connected && (sep->se_log & MULOG_RFC931))
2091		syslog(LOG_INFO, "%s@%s wants %s",
2092		    rfc931_name(sin, ctrl), host, sep->se_service);
2093	else
2094		syslog(LOG_INFO, "%s wants %s",
2095		    host, sep->se_service);
2096}
2097
2098/*
2099 * From tcp_log by
2100 *  Wietse Venema, Eindhoven University of Technology, The Netherlands.
2101 */
2102#if 0
2103static char sccsid[] = "@(#) rfc931.c 1.3 92/08/31 22:54:46";
2104#endif
2105
2106#include <setjmp.h>
2107
2108#define	RFC931_PORT	113		/* Semi-well-known port */
2109#define	TIMEOUT		4
2110#define	TIMEOUT2	10
2111
2112static jmp_buf timebuf;
2113
2114/* timeout - handle timeouts */
2115
2116static void timeout(sig)
2117int     sig;
2118{
2119	longjmp(timebuf, sig);
2120}
2121
2122/* rfc931_name - return remote user name */
2123
2124char *
2125rfc931_name(there, ctrl)
2126struct sockaddr_in *there;		/* remote link information */
2127int	ctrl;
2128{
2129	struct sockaddr_in here;	/* local link information */
2130	struct sockaddr_in sin;		/* for talking to RFC931 daemon */
2131	int		length;
2132	int		s;
2133	unsigned	remote;
2134	unsigned	local;
2135	static char	user[256];		/* XXX */
2136	char		buf[256];
2137	char		*cp;
2138	char		*result = "USER_UNKNOWN";
2139	int		len;
2140
2141	/* Find out local port number of our stdin. */
2142
2143	length = sizeof(here);
2144	if (getsockname(ctrl, (struct sockaddr *) &here, &length) == -1) {
2145		syslog(LOG_ERR, "getsockname: %m");
2146		return (result);
2147	}
2148	/* Set up timer so we won't get stuck. */
2149
2150	if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
2151		syslog(LOG_ERR, "socket: %m");
2152		return (result);
2153	}
2154
2155	sin = here;
2156	sin.sin_port = htons(0);
2157	if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
2158		syslog(LOG_ERR, "bind: %m");
2159		return (result);
2160	}
2161
2162	signal(SIGALRM, timeout);
2163	if (setjmp(timebuf)) {
2164		close(s);			/* not: fclose(fp) */
2165		return (result);
2166	}
2167	alarm(TIMEOUT);
2168
2169	/* Connect to the RFC931 daemon. */
2170
2171	sin = *there;
2172	sin.sin_port = htons(RFC931_PORT);
2173	if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
2174		close(s);
2175		alarm(0);
2176		return (result);
2177	}
2178
2179	/* Query the RFC 931 server. Would 13-byte writes ever be broken up? */
2180	(void)snprintf(buf, sizeof buf, "%u,%u\r\n", ntohs(there->sin_port),
2181	    ntohs(here.sin_port));
2182
2183
2184	for (len = 0, cp = buf; len < strlen(buf); ) {
2185		int	n;
2186
2187		if ((n = write(s, cp, strlen(buf) - len)) == -1) {
2188			close(s);
2189			alarm(0);
2190			return (result);
2191		}
2192		cp += n;
2193		len += n;
2194	}
2195
2196	/* Read response */
2197	for (cp = buf; cp < buf + sizeof(buf) - 1; ) {
2198		char	c;
2199		if (read(s, &c, 1) != 1) {
2200			close(s);
2201			alarm(0);
2202			return (result);
2203		}
2204		if (c == '\n')
2205			break;
2206		*cp++ = c;
2207	}
2208	*cp = '\0';
2209
2210	if (sscanf(buf, "%u , %u : USERID :%*[^:]:%255s", &remote, &local, user) == 3
2211		&& ntohs(there->sin_port) == remote
2212		&& ntohs(here.sin_port) == local) {
2213
2214		/* Strip trailing carriage return. */
2215		if (cp = strchr(user, '\r'))
2216			*cp = 0;
2217		result = user;
2218	}
2219
2220	alarm(0);
2221	close(s);
2222	return (result);
2223}
2224#endif
2225