/*
 * Copyright (c) 1983, 1988 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)rlogind.c	5.22.1.6 (Berkeley) 2/7/89";
#endif /* not lint */

/*
 * remote login server:
 *	\0
 *	remuser\0
 *	locuser\0
 *	terminal_type/speed\0
 *	data
 *
 * Automatic login protocol is done here, using login -f upon success,
 * unless OLD_LOGIN is defined (then done in login, ala 4.2/4.3BSD).
 */

#include "sys_defs.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>

#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <termios.h>
#include <stdio.h>
#include <netdb.h>
#include <syslog.h>
#include <string.h>
#define index		strchr
#define rindex		strrchr
#define bcopy(s,d,l)	memcpy(d,s,l)
#define bcmp(x,y,l)	memcmp(x,y,l)

#include <unistd.h>
#include <stdlib.h>

#ifndef O_RDWR		/* XXX */
#include <sys/fcntl.h>
#endif

#ifdef STREAM_PTY
#include <sys/stropts.h>
#include "../lib/tiocpkt.h"	/* for OOB control messages */
extern	char	*ptsname();
#define PTY_PKT_READ	pty_pkt_read
#else
#define PTY_PKT_READ	read
#endif

#ifndef TIOCSWINSZ	/* XXX */
#include <sys/ioctl.h>
#endif

#ifndef FIONBIO		/* XXX */
#include <sys/filio.h>
#endif

#ifndef NEW_LOGIN
#define OLD_LOGIN	/* use the -r option of /bin/login */
#endif

#if !defined(STREAM_PTY) && !defined(TIOCPKT)
#include <sys/pty.h>
#endif

#ifndef TIOCPKT_WINDOW
#define TIOCPKT_WINDOW 0x80
#endif

#define	NMAX 100
char	lusername[NMAX+1], rusername[NMAX+1];
static	char term[64] = "TERM=";
#define	ENVSIZE	(sizeof("TERM=")-1)	/* skip null for concatenation */
int	keepalive = 1;

#define	SUPERUSER(pwd)	((pwd)->pw_uid == 0)

/* Ultrix syslog(3) has no facility stuff. */
#ifndef LOG_DAEMON
#define LOG_DAEMON      0
#define LOG_CONS        0
#define LOG_AUTH        0
#endif

#ifdef ultrix
#define setsid()	setpgrp(0,0)
#endif

#ifndef _PATH_LOGIN
#define _PATH_LOGIN "/usr/bin/login"
#endif

extern	int _check_rhosts_file;
extern	int errno;
int	reapchild();
struct	passwd *getpwnam(), *pwd;

char    *procname;

/* On some systems, open ptys become wedged when the process exits. */
#ifdef CLOSE_PTY_MASTER
int	pty;
#endif /* CLOSE_PTY_MASTER */

main(argc, argv)
	int argc;
	char **argv;
{
	extern int opterr, optind;
	int ch;
	int on = 1, fromlen;
	struct sockaddr_in from;

	umask(022);

	procname = argv[0];

	openlog(procname, LOG_PID | LOG_CONS, FACILITY);

	opterr = 0;
	while ((ch = getopt(argc, argv, "ln")) != EOF)
		switch (ch) {
		case 'l':
			_check_rhosts_file = 0;
			break;
		case 'n':
			keepalive = 0;
			break;
		case '?':
		default:
			syslog(LOG_ERR, "usage: rlogind [-l] [-n]");
			break;
		}
	argc -= optind;
	argv += optind;

	fromlen = sizeof (from);
	if (getpeername(0, (struct sockaddr *) &from, &fromlen) < 0) {
		syslog(LOG_ERR, "Couldn't get peer name of remote host: %m");
		fatalperror(0, "Can't get peer name of host");
	}
	if (keepalive &&
	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on)) < 0)
		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
	doit(0, &from);
}

void	cleanup();
int	netf;
char	*line;
extern	char	*inet_ntoa();

struct winsize win = { 0, 0, 0, 0 };

#ifdef REQUEST_INFO_DECLARED
#define request_info tcpd_request_info
#endif

#include <tcpd.h>


doit(f, fromp)
	int f;
	struct sockaddr_in *fromp;
{
	int i, p, t, pid, on = 1;
	int authenticated = 0, hostok = 0;
	char remotehost[2 * MAXHOSTNAMELEN + 1];
	register struct hostent *hp;
	char c;
	struct termios termios;
	struct request_info request;
	struct sockaddr_in me;
	int me_len;

	alarm(60);
	read(f, &c, 1);
	if (c != 0)
		exit(1);

	alarm(0);
	fromp->sin_port = ntohs((u_short)fromp->sin_port);
	hp = gethostbyaddr((char *) &fromp->sin_addr, sizeof (struct in_addr),
		fromp->sin_family);
	if (hp == 0) {
		strcpy(remotehost, inet_ntoa(fromp->sin_addr));
		hostok++;
	} else {
		/*
		 * Attempt to verify that we haven't been fooled by someone
		 * in a remote net; look up the name and check that this
		 * address corresponds to the name.
		 */
		strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
		remotehost[sizeof(remotehost) - 1] = 0;
		hp = gethostbyname(remotehost);
		if (hp) {
		    for (; hp->h_addr_list[0]; hp->h_addr_list++)
			if (!bcmp(hp->h_addr_list[0], (caddr_t)&fromp->sin_addr,
			    sizeof(fromp->sin_addr))) {
				hostok++;
				break;
			}
		}
		if (hostok == 0) {
			syslog(LOG_NOTICE, 
				"Host addr %s not listed for host %s", 
				inet_ntoa(fromp->sin_addr), remotehost);
			strcpy(remotehost, inet_ntoa(fromp->sin_addr));
		}
	}

	if (fromp->sin_family != AF_INET ||
	    fromp->sin_port >= IPPORT_RESERVED ||
	    fromp->sin_port < IPPORT_RESERVED/2) {
		syslog(LOG_NOTICE, "Connection from %s on illegal port",
			inet_ntoa(fromp->sin_addr));
		fatal(f, "Permission denied");
	}
#ifdef IP_OPTIONS
      {
	u_char optbuf[BUFSIZ/3], *cp;
	char lbuf[BUFSIZ], *lp;
	int optsize = sizeof(optbuf), ipproto;
	struct protoent *ip;

	if ((ip = getprotobyname("ip")) != NULL)
		ipproto = ip->p_proto;
	else
		ipproto = IPPROTO_IP;
	if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 &&
	    optsize != 0) {
		int	source_routed = 0;
		int	optlen;
		int	opt;
		struct in_addr dummy;

#define ADDR_LEN sizeof(dummy.s_addr)

		/*
		 * Spot any nasty IP source-routing options.
		 */
		for (cp = optbuf + ADDR_LEN; cp < optbuf + optsize; cp += optlen) {
		    opt = cp[IPOPT_OPTVAL];
		    if (opt == IPOPT_LSRR || opt == IPOPT_SSRR)
			source_routed = 1;
		    if (opt == IPOPT_EOL)
			break;
		    if (opt == IPOPT_NOP) {
			optlen = 1;
		    } else {
			optlen = cp[IPOPT_OLEN];
			if (optlen <= 0)
			    break;
		    }
		}
		/*
		 * Stomp on the options, but only if there were known nasties.
		 */
		if (source_routed) {
			lp = lbuf;
			for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
				sprintf(lp, " %2.2x", *cp);
			syslog(LOG_NOTICE,
			    "Connection received using IP options (ignored):%s", lbuf);
			if (setsockopt(0, ipproto, IP_OPTIONS,
			    (char *)NULL, optsize) != 0) {
				syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
				exit(1);
			}
		}
	}
      }
#endif
	write(f, "", 1);
	if (do_rlogin(remotehost) == 0) {
		if (hostok)
		    authenticated++;
		else
		    write(f, "rlogind: Host address mismatch.\r\n",
		     sizeof("rlogind: Host address mismatch.\r\n") - 1);
	}
	me_len = sizeof(me);
	if (getsockname(f, (struct sockaddr *) &me, &me_len) < 0) {
		syslog(LOG_ERR, "getsockname: %m");
		exit(1);
	}
	request_init(&request,
		     RQ_DAEMON, procname,
		     RQ_CLIENT_NAME, NOT_INADDR(remotehost) ? remotehost : "unknown",
		     RQ_CLIENT_SIN, fromp,
		     RQ_USER, rusername,
		     RQ_FILE, f,
		     RQ_SERVER_SIN, &me,
		     0);
	sock_methods(&request);
	do_access(&request, remotehost, rusername, lusername);

#ifdef STREAM_PTY
	if ((p = open("/dev/ptmx", O_RDWR)) < 0)
		fatal(f, "All network ports in use");
	if (grantpt(p) < 0 || unlockpt(p) < 0)
		fatal(f, "Cannot initialize pty slave");
	if ((line = ptsname(p)) == 0 || (t = open(line, O_RDWR)) < 0)
		fatal(f, "Cannot find  pty slave");
	if (ioctl(t, I_PUSH, "ptem") < 0 || ioctl(t, I_PUSH, "ldterm") < 0 
	|| ioctl(t, I_PUSH, "ttcompat") < 0 || ioctl(p, I_PUSH, "pckt") < 0)
		fatal(f, "Cannot push streams modules onto pty");
#else /* STREAM_PTY */
	for (c = 'p'; c <= 'z'; c++) {
		struct stat stb;
		static char ptyname[] = "/dev/ptyXX";
		line = ptyname;
		line[strlen("/dev/pty")] = c;
		line[strlen("/dev/ptyp")] = '0';
		if (stat(line, &stb) < 0)
			break;
		for (i = 0; i < 16; i++) {
			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
			close(open(line, O_RDWR | O_NOCTTY));
			p = open(line, O_RDWR | O_NOCTTY);
			if (p > 0)
				goto gotpty;
		}
	}
	fatal(f, "Out of ptys");
	/*NOTREACHED*/
gotpty:
	line[strlen("/dev/")] = 't';
	t = open(line, O_RDWR | O_NOCTTY);
	if (t < 0)
		fatalperror(f, line);
	if (fchmod(t, 0))
		fatalperror(f, line);
#endif /* STREAM_PTY */
	(void) ioctl(p, TIOCSWINSZ, &win);
	netf = f;

	tcgetattr(t, &termios);
#ifdef OLD_LOGIN
	/* Do not echo the protocol startup data */
	termios.c_lflag &= ~(ECHO|ICANON);
#if 0
	termios.c_cc[VMIN] = 1;
	termios.c_cc[VTIME] = 0;
#endif
#else /* OLD_LOGIN */
	setup_term(&termios);
#endif /* OLD_LOGIN */
	/* Modern rloginds are 8-bit clean. */
	if ((termios.c_cflag & CSIZE) != CS8) {
		termios.c_cflag &= ~(CSIZE | PARENB);
		termios.c_cflag |= CS8;
		termios.c_iflag &= ~ISTRIP;
	}
	tcsetattr(t, TCSANOW, &termios);

	pid = fork();
	if (pid < 0)
		fatalperror(f, "");
	if (pid == 0) {
	/* Acquire a controlling terminal. */
#if defined(TIOCSCTTY) && !defined(BROKEN_TIOCSCTTY)
		setsid();
		ioctl(t, TIOCSCTTY, (caddr_t) 0);
#else
		int tt = t;
		setsid();
		if ((t = open(line, O_RDWR)) < 0)
			fatalperror(2, line);
		close(tt);
#endif
		close(f), close(p);
		dup2(t, 0), dup2(t, 1), dup2(t, 2);
		close(t);
#ifdef SYSV_UTMP
#ifndef NO_UTMP_INIT
		if (UTMP_INIT(line + sizeof("/dev/") - 1, ".rlogin", "rl") < 0)
			fatalperror(2, "makeutx failed");
#endif
#endif /* SYSV_UTMP */
#ifdef OLD_LOGIN
		execl(_PATH_LOGIN, "login", _check_rhosts_file ? "-r" : "-lr",
			remotehost, (char *) 0);
#else /* OLD_LOGIN */
#ifndef SYSV_ENV
		if (authenticated)
			execl(_PATH_LOGIN, "login", "-p", "-h", remotehost,
			    "-f", lusername, (char *) 0);
		else
			execl(_PATH_LOGIN, "login", "-p", "-h", remotehost,
			    lusername, (char *) 0);
#else /* SYSV_ENV Does not need -p to preserve TERM */
		if (authenticated)
			execl(_PATH_LOGIN, "login", "-h", remotehost,
			    "-f", lusername, (char *) 0);
		else
			execl(_PATH_LOGIN, "login", "-h", remotehost,
			    lusername, (char *) 0);
#endif /* SYSV_ENV */
#endif /* OLD_LOGIN */
		fatalperror(2, _PATH_LOGIN);
		/*NOTREACHED*/
	}
	close(t);

	ioctl(f, FIONBIO, &on);
	ioctl(p, FIONBIO, &on);
#ifndef STREAM_PTY
	ioctl(p, TIOCPKT, &on);
#endif
	signal(SIGTSTP, SIG_IGN);
	signal(SIGCHLD, cleanup);
	setsid();
#ifdef CLOSE_PTY_MASTER
	pty = p;
#endif /* CLOSE_PTY_MASTER */
	protocol(f, p);
	signal(SIGCHLD, SIG_IGN);
	cleanup();
}

char	magic[2] = { 0377, 0377 };
char	oobdata[] = {TIOCPKT_WINDOW};

/*
 * Handle a "control" request (signaled by magic being present)
 * in the data stream.  For now, we are only willing to handle
 * window size changes.
 */
control(pty, cp, n)
	int pty;
	char *cp;
	int n;
{
	struct winsize w;

	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
		return (0);
	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
	bcopy(cp+4, (char *)&w, sizeof(w));
	w.ws_row = ntohs(w.ws_row);
	w.ws_col = ntohs(w.ws_col);
	w.ws_xpixel = ntohs(w.ws_xpixel);
	w.ws_ypixel = ntohs(w.ws_ypixel);
	(void)ioctl(pty, TIOCSWINSZ, &w);
	return (4+sizeof (w));
}

/*
 * rlogin "protocol" machine.
 */
protocol(f, p)
	int f, p;
{
	char pibuf[1024], fibuf[1024], *pbp, *fbp;
	register pcc = 0, fcc = 0;
	int cc, nfd;
	char cntl;

#ifdef OLD_LOGIN
	/* Push back user name etc. (Darren Reed). */
	fbp = fibuf;
#define PUSH_BACK(s) ( fbp += strlen(strcpy(fbp, (s))) + 1 )
	PUSH_BACK(rusername);
	PUSH_BACK(lusername);
	PUSH_BACK(term + ENVSIZE);
	fcc = fbp - fibuf;
	fbp = fibuf;
#endif /* OLD_LOGIN */

	/*
	 * Must ignore SIGTTOU, otherwise we'll stop
	 * when we try and set slave pty's window shape
	 * (our controlling tty is the master pty).
	 */
	(void) signal(SIGTTOU, SIG_IGN);
	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
	if (f > p)
		nfd = f + 1;
	else
		nfd = p + 1;
	for (;;) {
		fd_set ibits, obits, ebits;

		FD_ZERO(&ibits);
		FD_ZERO(&obits);
		if (fcc)
			FD_SET(p, &obits);
		else
			FD_SET(f, &ibits);
		if (pcc >= 0)
			if (pcc)
				FD_SET(f, &obits);
			else
				FD_SET(p, &ibits);
		FD_ZERO(&ebits);
		FD_SET(p, &ebits);
		if (select(nfd, &ibits, &obits, &ebits, (struct timeval *)0) < 0) {
			if (errno == EINTR)
				continue;
			fatalperror(f, "select");
		}
#define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
		if (FD_ISSET(p, &ebits)) {
			cc = PTY_PKT_READ(p, &cntl, 1);
			if (cc == 1 && pkcontrol(cntl)) {
				cntl |= oobdata[0];
				send(f, &cntl, 1, MSG_OOB);
				if (cntl & TIOCPKT_FLUSHWRITE) {
					pcc = 0;
					FD_CLR(p, &ibits);
				}
			}
		}
		if (FD_ISSET(f, &ibits)) {
			fcc = read(f, fibuf, sizeof(fibuf));
			if (fcc < 0 && errno == EWOULDBLOCK)
				fcc = 0;
			else {
				register char *cp;
				int left, n;

				if (fcc <= 0)
					break;
				fbp = fibuf;

			top:
				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
					if (cp[0] == magic[0] &&
					    cp[1] == magic[1]) {
						left = fcc - (cp-fibuf);
						n = control(p, cp, left);
						if (n) {
							left -= n;
							if (left > 0)
								bcopy(cp+n, cp, left);
							fcc -= n;
							goto top; /* n^2 */
						}
					}
				FD_SET(p, &obits);		/* try write */
			}
		}

		if (FD_ISSET(p, &obits) && fcc > 0) {
			cc = write(p, fbp, fcc);
			if (cc > 0) {
				fcc -= cc;
				fbp += cc;
			}
		}

		if (FD_ISSET(p, &ibits)) {
			pcc = PTY_PKT_READ(p, pibuf, sizeof (pibuf));
			pbp = pibuf;
			if (pcc < 0 && errno == EWOULDBLOCK)
				pcc = 0;
			else if (pcc <= 0)
				break;
			else if (pibuf[0] == 0) {
				pbp++, pcc--;
				FD_SET(f, &obits);		/* try a write */
			} else {
				if (pkcontrol(pibuf[0])) {
					pibuf[0] |= oobdata[0];
					send(f, &pibuf[0], 1, MSG_OOB);
				}
				pcc = 0;
			}
		}
		if (FD_ISSET(f, &obits) && pcc > 0) {
			cc = write(f, pbp, pcc);
			if (cc < 0 && errno == EWOULDBLOCK) {
				/* also shouldn't happen */
				sleep(1);
				continue;
			}
			if (cc > 0) {
				pcc -= cc;
				pbp += cc;
			}
		}
	}
}

void	cleanup()
{
	char *p;

	p = line + sizeof("/dev/") - 1;
#ifdef SYSV_UTMP
	UTMP_LOGOUT(p);
	(void)chmod(line, 0644);
	(void)chown(line, 0, 0);
#else /* SYSV_UTMP */
	if (logout(p))
		logwtmp(p, "", "");
	(void)chmod(line, 0666);
	(void)chown(line, 0, 0);
	*p = 'p';
	(void)chmod(line, 0666);
	(void)chown(line, 0, 0);
#endif /* SYSV_UTMP */
#ifdef CLOSE_PTY_MASTER
	close(pty);
#endif /* CLOSE_PTY_MASTER */
	shutdown(netf, 2);
	exit(1);
}

fatal(f, msg)
	int f;
	char *msg;
{
	char buf[BUFSIZ];

	buf[0] = '\01';		/* error indicator */
	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
	(void) write(f, buf, strlen(buf));
	exit(1);
}

fatalperror(f, msg)
	int f;
	char *msg;
{
	char buf[BUFSIZ];
	extern int sys_nerr;
#ifndef STDIO_DECLARES_SYS_ERRLIST
	extern char *sys_errlist[];
#endif

	if ((unsigned)errno < sys_nerr)
		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
	else
		(void) sprintf(buf, "%s: Error %d", msg, errno);
	fatal(f, buf);
}

do_rlogin(host)
	char *host;
{

	getstr(rusername, sizeof(rusername), "remuser too long");
	getstr(lusername, sizeof(lusername), "locuser too long");
	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");

#ifndef OLD_LOGIN /* just pick up the user info */
	if (getuid())
		return(-1);
	pwd = getpwnam(lusername);
	if (pwd == NULL)
		return(-1);
	return(ruserok(host, SUPERUSER(pwd), rusername, lusername));
#else
	return(-1);
#endif /* OLD_LOGIN */
}


getstr(buf, cnt, errmsg)
	char *buf;
	int cnt;
	char *errmsg;
{
	char c;

	do {
		if (read(0, &c, 1) != 1)
			exit(1);
		if (--cnt < 0)
			fatal(1, errmsg);
		*buf++ = c;
	} while (c != 0);
}

#ifndef OLD_LOGIN
#ifndef SYSV_ENV /* Preserve TZ etc. */
extern	char **environ;
#endif

struct speed_map {
	char   *speed;
	int     code;
} speeds[] = {
	{"0", 0},
	{"50", B50},
	{"75", B75},
	{"110", B110},
	{"134", B134},
	{"150", B150},
	{"200", B200},
	{"300", B300},
	{"600", B600},
#ifdef _B900
	{"900", _B900},
#endif
	{"1200", B1200},
	{"1800", B1800},
	{"2400", B2400},
#ifdef _B3600
	{"3600", _B3600},
#endif
	{"4800", B4800},
#ifdef _B7200
	{"7200", _B7200},
#endif
	{"9600", B9600},
#ifdef B19200
	{"19200", B19200},
#endif
#ifdef EXTA
	{"19200", EXTA},
#endif
#ifdef B38400
	{"38400", B38400},
#endif
#ifdef EXTB
	{"38400", EXTB},
#endif
#ifdef _B57600
	{"57600", _B57600},
#endif
#ifdef _B115200
	{"115200", _B115200},
#endif
#ifdef _B230400
	{"230400", _B230400},
#endif
#ifdef _B460800
	{"460800", _B460800},
#endif	/* For blindingly-fast dumb terminals? */
};

#define	NSPEEDS	(sizeof(speeds) / sizeof(speeds[0]))

setup_term(tp)
	struct termios *tp;
{
	register char *cp = index(term, '/');
	struct speed_map *cpp;
	char *speed;

	if (cp) {
		*cp++ = '\0';
		speed = cp;
		cp = index(speed, '/');
		if (cp)
			*cp++ = '\0';
		for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
		    if (strcmp(cpp->speed, speed) == 0) {
			cfsetispeed(tp, cpp->code);
			cfsetospeed(tp, cpp->code);
			break;
		    }
	}
#ifndef SYSV_ENV /* Preserve TZ etc. */
	if (environ)
		environ[0] = 0;
#endif
	putenv(term);
}

#endif /* OLD_LOGIN */

int     allow_severity = LOG_INFO;
int     deny_severity = LOG_WARNING;

do_access(request, host, ruser, luser)
struct request_info *request;
char   *host;
char   *ruser;
char   *luser;
{
    int     allow = hosts_access(request) && (luser[0] != '-');
    char   *text = allow ? "" : "refused ";
    int     severity = allow ? allow_severity : deny_severity;

    if (strcmp(ruser, luser) == 0)
	syslog(severity, "%sconnect from %s@%s",
	       text, ruser, host);
    else
	syslog(severity, "%sconnect from %s@%s to %s",
	       text, ruser, host, luser);
    if (!allow)
	exit(0);
}

