xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 69118)
144346Skarels /*
262213Sbostic  * Copyright (c) 1983, 1990, 1993
362213Sbostic  *	The Regents of the University of California.  All rights reserved.
435539Sbostic  *
542763Sbostic  * %sccs.include.redist.c%
621595Sdist  */
721595Sdist 
86444Swnj #ifndef lint
962213Sbostic static char copyright[] =
1062213Sbostic "@(#) Copyright (c) 1983, 1990, 1993\n\
1162213Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1235539Sbostic #endif /* not lint */
136444Swnj 
1421595Sdist #ifndef lint
15*69118Svjs static char sccsid[] = "@(#)rlogin.c	8.4 (Berkeley) 04/29/95";
1635539Sbostic #endif /* not lint */
1721595Sdist 
1812990Ssam /*
1912990Ssam  * rlogin - remote login
2012990Ssam  */
2126981Skarels #include <sys/param.h>
226444Swnj #include <sys/socket.h>
2329729Smckusick #include <sys/time.h>
2429729Smckusick #include <sys/resource.h>
2513620Ssam #include <sys/wait.h>
2667737Spendry #include <sys/ioctl.h>
279365Ssam 
289207Ssam #include <netinet/in.h>
2944346Skarels #include <netinet/in_systm.h>
3044346Skarels #include <netinet/ip.h>
319365Ssam 
326444Swnj #include <errno.h>
3358470Sbostic #include <fcntl.h>
3458470Sbostic #include <netdb.h>
356444Swnj #include <pwd.h>
3658470Sbostic #include <setjmp.h>
3767737Spendry #include <termios.h>
3858470Sbostic #include <signal.h>
3940858Sbostic #include <stdio.h>
4058470Sbostic #include <stdlib.h>
4158470Sbostic #include <string.h>
4240858Sbostic #include <unistd.h>
436444Swnj 
4458766Sbostic #ifdef __STDC__
4558766Sbostic #include <stdarg.h>
4658766Sbostic #else
4758766Sbostic #include <varargs.h>
4858766Sbostic #endif
4958766Sbostic 
5040858Sbostic #ifdef KERBEROS
5141760Skfall #include <kerberosIV/des.h>
5240683Sbostic #include <kerberosIV/krb.h>
5336511Skfall 
5458470Sbostic #include "krb.h"
5558470Sbostic 
5640858Sbostic CREDENTIALS cred;
5740858Sbostic Key_schedule schedule;
5846850Sbostic int use_kerberos = 1, doencrypt;
5940858Sbostic char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
6040858Sbostic #endif
6124726Smckusick 
6240858Sbostic #ifndef TIOCPKT_WINDOW
6340858Sbostic #define	TIOCPKT_WINDOW	0x80
6440858Sbostic #endif
6529729Smckusick 
6640858Sbostic /* concession to Sun */
6740858Sbostic #ifndef SIGUSR1
6840858Sbostic #define	SIGUSR1	30
6926981Skarels #endif
7040858Sbostic 
7140858Sbostic int eight, litout, rem;
7245063Sbostic 
7345063Sbostic int noescape;
7445063Sbostic u_char escapechar = '~';
7545063Sbostic 
7659175Storek #ifdef OLDSUN
7726981Skarels struct winsize {
7826981Skarels 	unsigned short ws_row, ws_col;
7926981Skarels 	unsigned short ws_xpixel, ws_ypixel;
8026981Skarels };
8158470Sbostic #else
8258470Sbostic #define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
8340858Sbostic #endif
8418358Ssam struct	winsize winsize;
856444Swnj 
8658470Sbostic void		catch_child __P((int));
8758470Sbostic void		copytochild __P((int));
8867737Spendry __dead void	doit __P((sigset_t *));
8958470Sbostic __dead void	done __P((int));
9058470Sbostic void		echo __P((char));
9158470Sbostic u_int		getescape __P((char *));
9258470Sbostic void		lostpeer __P((int));
9358470Sbostic void		mode __P((int));
9458470Sbostic void		msg __P((char *));
9558470Sbostic void		oob __P((int));
9667737Spendry int		reader __P((sigset_t *));
9758470Sbostic void		sendwindow __P((void));
9858470Sbostic void		setsignal __P((int));
9967749Spendry int		speed __P((int));
10058470Sbostic void		sigwinch __P((int));
10158470Sbostic void		stop __P((char));
10258470Sbostic __dead void	usage __P((void));
10358470Sbostic void		writer __P((void));
10458470Sbostic void		writeroob __P((int));
10558470Sbostic 
10658470Sbostic #ifdef	KERBEROS
10758470Sbostic void		warning __P((const char *, ...));
10840858Sbostic #endif
10959175Storek #ifdef OLDSUN
11058470Sbostic int		get_window_size __P((int, struct winsize *));
11158470Sbostic #endif
11229729Smckusick 
11358470Sbostic int
main(argc,argv)1146444Swnj main(argc, argv)
1156444Swnj 	int argc;
11658470Sbostic 	char *argv[];
1176444Swnj {
11840858Sbostic 	struct passwd *pw;
11940858Sbostic 	struct servent *sp;
12067737Spendry 	sigset_t smask;
12167737Spendry 	uid_t uid;
12267737Spendry 	int argoff, ch, dflag, one;
12340858Sbostic 	char *host, *p, *user, term[1024];
12467737Spendry 	struct sigaction sa;
1256444Swnj 
12640858Sbostic 	argoff = dflag = 0;
12740858Sbostic 	one = 1;
12840858Sbostic 	host = user = NULL;
12940858Sbostic 
13067737Spendry 	if (p = strrchr(argv[0], '/'))
13140858Sbostic 		++p;
1326444Swnj 	else
13340858Sbostic 		p = argv[0];
13440858Sbostic 
13567737Spendry 	if (strcmp(p, "rlogin") != 0)
13640858Sbostic 		host = p;
13740858Sbostic 
13840858Sbostic 	/* handle "rlogin host flags" */
13940858Sbostic 	if (!host && argc > 2 && argv[1][0] != '-') {
14040858Sbostic 		host = argv[1];
14140858Sbostic 		argoff = 1;
1426444Swnj 	}
14336511Skfall 
14440858Sbostic #ifdef KERBEROS
14545063Sbostic #define	OPTIONS	"8EKLde:k:l:x"
14640858Sbostic #else
14745063Sbostic #define	OPTIONS	"8EKLde:l:"
14840858Sbostic #endif
14940858Sbostic 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
15040858Sbostic 		switch(ch) {
15140858Sbostic 		case '8':
15240858Sbostic 			eight = 1;
15340858Sbostic 			break;
15445063Sbostic 		case 'E':
15545063Sbostic 			noescape = 1;
15645063Sbostic 			break;
15740868Sbostic 		case 'K':
15840868Sbostic #ifdef KERBEROS
15940868Sbostic 			use_kerberos = 0;
16040868Sbostic #endif
16140868Sbostic 			break;
16240858Sbostic 		case 'L':
16340858Sbostic 			litout = 1;
16440858Sbostic 			break;
16540858Sbostic 		case 'd':
16640858Sbostic 			dflag = 1;
16740858Sbostic 			break;
16840858Sbostic 		case 'e':
16953033Sleres 			noescape = 0;
17045063Sbostic 			escapechar = getescape(optarg);
17140858Sbostic 			break;
17240858Sbostic #ifdef KERBEROS
17340858Sbostic 		case 'k':
17440858Sbostic 			dest_realm = dst_realm_buf;
17540858Sbostic 			(void)strncpy(dest_realm, optarg, REALM_SZ);
17640858Sbostic 			break;
17740858Sbostic #endif
17840858Sbostic 		case 'l':
17940858Sbostic 			user = optarg;
18040858Sbostic 			break;
18153033Sleres #ifdef CRYPT
18253033Sleres #ifdef KERBEROS
18353033Sleres 		case 'x':
18453033Sleres 			doencrypt = 1;
18553033Sleres 			des_set_key(cred.session, schedule);
18653033Sleres 			break;
18753033Sleres #endif
18853033Sleres #endif
18940858Sbostic 		case '?':
19040858Sbostic 		default:
19140858Sbostic 			usage();
19236524Skfall 		}
19340858Sbostic 	optind += argoff;
19440858Sbostic 	argc -= optind;
19540858Sbostic 	argv += optind;
19636524Skfall 
19740858Sbostic 	/* if haven't gotten a host yet, do so */
19840858Sbostic 	if (!host && !(host = *argv++))
19940858Sbostic 		usage();
20036511Skfall 
20140858Sbostic 	if (*argv)
20240858Sbostic 		usage();
20340858Sbostic 
20467737Spendry 	if (!(pw = getpwuid(uid = getuid())))
20567737Spendry 		errx(1, "unknown user id.");
206*69118Svjs 	/* Accept user1@host format, though "-l user2" overrides user1 */
207*69118Svjs 	p = strchr(host, '@');
208*69118Svjs 	if (p) {
209*69118Svjs 		*p = '\0';
210*69118Svjs 		if (!user && p > host)
211*69118Svjs 			user = host;
212*69118Svjs 		host = p + 1;
213*69118Svjs 		if (*host == '\0')
214*69118Svjs 			usage();
215*69118Svjs 	}
21640858Sbostic 	if (!user)
21740858Sbostic 		user = pw->pw_name;
21840858Sbostic 
21940868Sbostic 	sp = NULL;
22040858Sbostic #ifdef KERBEROS
22140868Sbostic 	if (use_kerberos) {
22246850Sbostic 		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
22340868Sbostic 		if (sp == NULL) {
22440868Sbostic 			use_kerberos = 0;
22540868Sbostic 			warning("can't get entry for %s/tcp service",
22646850Sbostic 			    doencrypt ? "eklogin" : "klogin");
22740868Sbostic 		}
22836512Skfall 	}
22936512Skfall #endif
23040868Sbostic 	if (sp == NULL)
23140868Sbostic 		sp = getservbyname("login", "tcp");
23267737Spendry 	if (sp == NULL)
23367737Spendry 		errx(1, "login/tcp: unknown service.");
23440858Sbostic 
23567737Spendry 	(void)snprintf(term, sizeof(term), "%s/%d",
23667737Spendry 			((p = getenv("TERM")) ? p : "network"),
23767749Spendry 			speed(0));
23840858Sbostic 
23940858Sbostic 	(void)get_window_size(0, &winsize);
24040858Sbostic 
24167737Spendry 	sigemptyset(&sa.sa_mask);
24267737Spendry 	sa.sa_flags = SA_RESTART;
24367737Spendry 	sa.sa_handler = lostpeer;
24467737Spendry 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
24529729Smckusick 	/* will use SIGUSR1 for window size hack, so hold it off */
24667737Spendry 	sigemptyset(&smask);
24767737Spendry 	sigaddset(&smask, SIGURG);
24867737Spendry 	sigaddset(&smask, SIGUSR1);
24967737Spendry 	(void)sigprocmask(SIG_SETMASK, &smask, &smask);
25058261Smckusick 	/*
25158261Smckusick 	 * We set SIGURG and SIGUSR1 below so that an
25258261Smckusick 	 * incoming signal will be held pending rather than being
25358261Smckusick 	 * discarded. Note that these routines will be ready to get
25458261Smckusick 	 * a signal by the time that they are unblocked below.
25558261Smckusick 	 */
25667737Spendry 	sa.sa_handler = copytochild;
25767737Spendry 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
25867737Spendry 	sa.sa_handler = writeroob;
25967737Spendry 	(void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
26036511Skfall 
26140858Sbostic #ifdef KERBEROS
26236512Skfall try_connect:
26340858Sbostic 	if (use_kerberos) {
26458470Sbostic 		struct hostent *hp;
26558470Sbostic 
26658470Sbostic 		/* Fully qualify hostname (needed for krb_realmofhost). */
26756960Seric 		hp = gethostbyname(host);
26867737Spendry 		if (hp != NULL && !(host = strdup(hp->h_name)))
26967737Spendry 			errx(1, "%s", strerror(ENOMEM));
27056960Seric 
27136512Skfall 		rem = KSUCCESS;
27240858Sbostic 		errno = 0;
27338728Skfall 		if (dest_realm == NULL)
27438728Skfall 			dest_realm = krb_realmofhost(host);
27538728Skfall 
27653033Sleres #ifdef CRYPT
27753033Sleres 		if (doencrypt)
27853033Sleres 			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
27953033Sleres 			    dest_realm, &cred, schedule);
28053033Sleres 		else
28153033Sleres #endif /* CRYPT */
28240858Sbostic 			rem = krcmd(&host, sp->s_port, user, term, 0,
28340858Sbostic 			    dest_realm);
28438728Skfall 		if (rem < 0) {
28536512Skfall 			use_kerberos = 0;
28636628Skfall 			sp = getservbyname("login", "tcp");
28767737Spendry 			if (sp == NULL)
28867737Spendry 				errx(1, "unknown service login/tcp.");
28938728Skfall 			if (errno == ECONNREFUSED)
29040858Sbostic 				warning("remote host doesn't support Kerberos");
29138728Skfall 			if (errno == ENOENT)
29240858Sbostic 				warning("can't provide Kerberos auth data");
29336512Skfall 			goto try_connect;
29436512Skfall 		}
29536511Skfall 	} else {
29653033Sleres #ifdef CRYPT
29767737Spendry 		if (doencrypt)
29867737Spendry 			errx(1, "the -x flag requires Kerberos authentication.");
29953033Sleres #endif /* CRYPT */
30040858Sbostic 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
30136511Skfall 	}
30236512Skfall #else
30340858Sbostic 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
30445256Smckusick #endif /* KERBEROS */
30536511Skfall 
30640858Sbostic 	if (rem < 0)
30736511Skfall 		exit(1);
30836511Skfall 
30940858Sbostic 	if (dflag &&
31040858Sbostic 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
31167737Spendry 		warn("setsockopt DEBUG (ignored)");
31244346Skarels 	one = IPTOS_LOWDELAY;
31344346Skarels 	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
31467737Spendry 		warn("setsockopt TOS (ignored)");
31540858Sbostic 
31640858Sbostic 	(void)setuid(uid);
31767737Spendry 	doit(&smask);
3189365Ssam 	/*NOTREACHED*/
3196444Swnj }
3206444Swnj 
32167749Spendry #if BSD >= 198810
32267749Spendry int
speed(fd)32367749Spendry speed(fd)
32467749Spendry 	int fd;
32567749Spendry {
32667749Spendry 	struct termios tt;
32767749Spendry 
32867749Spendry 	(void)tcgetattr(fd, &tt);
32967749Spendry 
33067749Spendry 	return ((int) cfgetispeed(&tt));
33167749Spendry }
33267749Spendry #else
33367749Spendry int    speeds[] = {	/* for older systems, B0 .. EXTB */
33467749Spendry 	0, 50, 75, 110,
33567749Spendry 	134, 150, 200, 300,
33667749Spendry 	600, 1200, 1800, 2400,
33767749Spendry 	4800, 9600, 19200, 38400
33867749Spendry };
33967749Spendry 
34067749Spendry int
speed(fd)34167749Spendry speed(fd)
34267749Spendry 	int fd;
34367749Spendry {
34467749Spendry 	struct termios tt;
34567749Spendry 
34667749Spendry 	(void)tcgetattr(fd, &tt);
34767749Spendry 
34867749Spendry 	return (speeds[(int)cfgetispeed(&tt)]);
34967749Spendry }
35067749Spendry #endif
35167749Spendry 
35267737Spendry pid_t child;
35367737Spendry struct termios deftt;
35467737Spendry struct termios nott;
3556444Swnj 
35658470Sbostic void
doit(smask)35767737Spendry doit(smask)
35867737Spendry 	sigset_t *smask;
3596444Swnj {
36067737Spendry 	int i;
36167737Spendry 	struct sigaction sa;
3626444Swnj 
36367737Spendry 	for (i = 0; i < NCCS; i++)
36467737Spendry 		nott.c_cc[i] = _POSIX_VDISABLE;
36567737Spendry 	tcgetattr(0, &deftt);
36667737Spendry 	nott.c_cc[VSTART] = deftt.c_cc[VSTART];
36767737Spendry 	nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
36867737Spendry 	sigemptyset(&sa.sa_mask);
36967737Spendry 	sa.sa_flags = SA_RESTART;
37067737Spendry 	sa.sa_handler = SIG_IGN;
37167737Spendry 	(void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
37258470Sbostic 	setsignal(SIGHUP);
37358470Sbostic 	setsignal(SIGQUIT);
3749365Ssam 	child = fork();
3759365Ssam 	if (child == -1) {
37667737Spendry 		warn("fork");
37725424Skarels 		done(1);
3789365Ssam 	}
3799365Ssam 	if (child == 0) {
38024726Smckusick 		mode(1);
38167737Spendry 		if (reader(smask) == 0) {
38240858Sbostic 			msg("connection closed.");
38325424Skarels 			exit(0);
38425424Skarels 		}
38512155Ssam 		sleep(1);
38640858Sbostic 		msg("\007connection closed.");
38740858Sbostic 		exit(1);
3886444Swnj 	}
38929729Smckusick 
39029729Smckusick 	/*
39140858Sbostic 	 * We may still own the socket, and may have a pending SIGURG (or might
39258261Smckusick 	 * receive one soon) that we really want to send to the reader.  When
39358261Smckusick 	 * one of these comes in, the trap copytochild simply copies such
39458261Smckusick 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
39558261Smckusick 	 * that were set above.
39629729Smckusick 	 */
39767737Spendry 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
39867737Spendry 	sa.sa_handler = catch_child;
39967737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
4009365Ssam 	writer();
40140858Sbostic 	msg("closed connection.");
40225424Skarels 	done(0);
4036444Swnj }
4046444Swnj 
40540858Sbostic /* trap a signal, unless it is being ignored. */
40658470Sbostic void
setsignal(sig)40758470Sbostic setsignal(sig)
40840858Sbostic 	int sig;
40929729Smckusick {
41067737Spendry 	struct sigaction sa;
41167737Spendry 	sigset_t sigs;
41229729Smckusick 
41367737Spendry 	sigemptyset(&sigs);
41467737Spendry 	sigaddset(&sigs, sig);
41567737Spendry 	sigprocmask(SIG_BLOCK, &sigs, &sigs);
41667737Spendry 
41767737Spendry 	sigemptyset(&sa.sa_mask);
41867737Spendry 	sa.sa_handler = exit;
41967737Spendry 	sa.sa_flags = SA_RESTART;
42067737Spendry 	(void)sigaction(sig, &sa, &sa);
42167737Spendry 	if (sa.sa_handler == SIG_IGN)
42267737Spendry 		(void)sigaction(sig, &sa, (struct sigaction *) 0);
42367737Spendry 
42467737Spendry 	(void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
42529729Smckusick }
42629729Smckusick 
42758470Sbostic __dead void
done(status)42825424Skarels done(status)
42925424Skarels 	int status;
4306444Swnj {
43167737Spendry 	pid_t w;
43267737Spendry 	int wstatus;
43367737Spendry 	struct sigaction sa;
4346444Swnj 
4356444Swnj 	mode(0);
43629729Smckusick 	if (child > 0) {
43740858Sbostic 		/* make sure catch_child does not snap it up */
43867737Spendry 		sigemptyset(&sa.sa_mask);
43967737Spendry 		sa.sa_handler = SIG_DFL;
44067737Spendry 		sa.sa_flags = 0;
44167737Spendry 		(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
44229729Smckusick 		if (kill(child, SIGKILL) >= 0)
44367737Spendry 			while ((w = wait(&wstatus)) > 0 && w != child)
44467737Spendry 				continue;
44529729Smckusick 	}
44625424Skarels 	exit(status);
4476444Swnj }
4486444Swnj 
44940858Sbostic int dosigwinch;
45029729Smckusick 
45129729Smckusick /*
45224726Smckusick  * This is called when the reader process gets the out-of-band (urgent)
45324726Smckusick  * request to turn on the window-changing protocol.
45424726Smckusick  */
45540858Sbostic void
writeroob(signo)45658470Sbostic writeroob(signo)
45758470Sbostic 	int signo;
45824726Smckusick {
45967737Spendry 	struct sigaction sa;
46067737Spendry 
46125341Smckusick 	if (dosigwinch == 0) {
46224919Smckusick 		sendwindow();
46367737Spendry 		sigemptyset(&sa.sa_mask);
46467737Spendry 		sa.sa_handler = sigwinch;
46567737Spendry 		sa.sa_flags = SA_RESTART;
46667737Spendry 		(void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
46725341Smckusick 	}
46824726Smckusick 	dosigwinch = 1;
46924726Smckusick }
47024726Smckusick 
47140858Sbostic void
catch_child(signo)47258470Sbostic catch_child(signo)
47358470Sbostic 	int signo;
47411803Sedward {
47567737Spendry 	int status;
47667737Spendry 	pid_t pid;
47711803Sedward 
47840858Sbostic 	for (;;) {
47967737Spendry 		pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
48040858Sbostic 		if (pid == 0)
48140858Sbostic 			return;
48240858Sbostic 		/* if the child (reader) dies, just quit */
48358470Sbostic 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
48467737Spendry 			done(WEXITSTATUS(status) | WTERMSIG(status));
48540858Sbostic 	}
48640858Sbostic 	/* NOTREACHED */
48711803Sedward }
48811803Sedward 
4896444Swnj /*
4909365Ssam  * writer: write to remote: 0 -> line.
49145063Sbostic  * ~.				terminate
49245063Sbostic  * ~^Z				suspend rlogin process.
49345063Sbostic  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
4946444Swnj  */
49558470Sbostic void
writer()4969365Ssam writer()
4976444Swnj {
49845063Sbostic 	register int bol, local, n;
49923530Sbloom 	char c;
5006444Swnj 
50140858Sbostic 	bol = 1;			/* beginning of line */
50240858Sbostic 	local = 0;
50311803Sedward 	for (;;) {
50440858Sbostic 		n = read(STDIN_FILENO, &c, 1);
50518358Ssam 		if (n <= 0) {
50618358Ssam 			if (n < 0 && errno == EINTR)
50718358Ssam 				continue;
50811803Sedward 			break;
50918358Ssam 		}
5109365Ssam 		/*
51140858Sbostic 		 * If we're at the beginning of the line and recognize a
51240858Sbostic 		 * command character, then we echo locally.  Otherwise,
51340858Sbostic 		 * characters are echo'd remotely.  If the command character
51440858Sbostic 		 * is doubled, this acts as a force and local echo is
51540858Sbostic 		 * suppressed.
5169365Ssam 		 */
51723530Sbloom 		if (bol) {
51823530Sbloom 			bol = 0;
51945063Sbostic 			if (!noescape && c == escapechar) {
52023530Sbloom 				local = 1;
52123530Sbloom 				continue;
5226444Swnj 			}
52323530Sbloom 		} else if (local) {
52423530Sbloom 			local = 0;
52567737Spendry 			if (c == '.' || c == deftt.c_cc[VEOF]) {
52623530Sbloom 				echo(c);
52723530Sbloom 				break;
5286444Swnj 			}
52967737Spendry 			if (c == deftt.c_cc[VSUSP] || c == deftt.c_cc[VDSUSP]) {
53023530Sbloom 				bol = 1;
53123530Sbloom 				echo(c);
53223530Sbloom 				stop(c);
53323530Sbloom 				continue;
53423530Sbloom 			}
53545063Sbostic 			if (c != escapechar)
53653033Sleres #ifdef CRYPT
53753033Sleres #ifdef KERBEROS
53853033Sleres 				if (doencrypt)
53958470Sbostic 					(void)des_write(rem,
54058470Sbostic 					    (char *)&escapechar, 1);
54153033Sleres 				else
54253033Sleres #endif
54353033Sleres #endif
54445063Sbostic 					(void)write(rem, &escapechar, 1);
5456444Swnj 		}
54636511Skfall 
54753033Sleres #ifdef CRYPT
54853033Sleres #ifdef KERBEROS
54953033Sleres 		if (doencrypt) {
55053033Sleres 			if (des_write(rem, &c, 1) == 0) {
55153033Sleres 				msg("line gone");
55253033Sleres 				break;
55353033Sleres 			}
55453033Sleres 		} else
55553033Sleres #endif
55653033Sleres #endif
55736511Skfall 			if (write(rem, &c, 1) == 0) {
55840858Sbostic 				msg("line gone");
55936511Skfall 				break;
56036511Skfall 			}
56167737Spendry 		bol = c == deftt.c_cc[VKILL] || c == deftt.c_cc[VEOF] ||
56267737Spendry 		    c == deftt.c_cc[VINTR] || c == deftt.c_cc[VSUSP] ||
56323530Sbloom 		    c == '\r' || c == '\n';
5646444Swnj 	}
5656444Swnj }
5666444Swnj 
56758470Sbostic void
56858470Sbostic #if __STDC__
echo(register char c)56958470Sbostic echo(register char c)
57058470Sbostic #else
57123530Sbloom echo(c)
57258470Sbostic 	register char c;
57358470Sbostic #endif
57423530Sbloom {
57540858Sbostic 	register char *p;
57623530Sbloom 	char buf[8];
57723530Sbloom 
57840858Sbostic 	p = buf;
57923530Sbloom 	c &= 0177;
58045063Sbostic 	*p++ = escapechar;
58123530Sbloom 	if (c < ' ') {
58223530Sbloom 		*p++ = '^';
58323530Sbloom 		*p++ = c + '@';
58423530Sbloom 	} else if (c == 0177) {
58523530Sbloom 		*p++ = '^';
58623530Sbloom 		*p++ = '?';
58723530Sbloom 	} else
58823530Sbloom 		*p++ = c;
58923530Sbloom 	*p++ = '\r';
59023530Sbloom 	*p++ = '\n';
59145063Sbostic 	(void)write(STDOUT_FILENO, buf, p - buf);
59223530Sbloom }
59323530Sbloom 
59458470Sbostic void
59558470Sbostic #if __STDC__
stop(char cmdc)59658470Sbostic stop(char cmdc)
59758470Sbostic #else
59818358Ssam stop(cmdc)
59918358Ssam 	char cmdc;
60058470Sbostic #endif
60118358Ssam {
60267737Spendry 	struct sigaction sa;
60367737Spendry 
60418358Ssam 	mode(0);
60567737Spendry 	sigemptyset(&sa.sa_mask);
60667737Spendry 	sa.sa_handler = SIG_IGN;
60767737Spendry 	sa.sa_flags = SA_RESTART;
60867737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
60967737Spendry 	(void)kill(cmdc == deftt.c_cc[VSUSP] ? 0 : getpid(), SIGTSTP);
61067737Spendry 	sa.sa_handler = catch_child;
61167737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
61218358Ssam 	mode(1);
61358470Sbostic 	sigwinch(0);			/* check for size changes */
61418358Ssam }
61518358Ssam 
61640858Sbostic void
sigwinch(signo)61758470Sbostic sigwinch(signo)
61858470Sbostic 	int signo;
61918358Ssam {
62018358Ssam 	struct winsize ws;
62118358Ssam 
62229729Smckusick 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
62367737Spendry 	    memcmp(&ws, &winsize, sizeof(ws))) {
62418358Ssam 		winsize = ws;
62524726Smckusick 		sendwindow();
62618358Ssam 	}
62718358Ssam }
62818358Ssam 
62924726Smckusick /*
63024726Smckusick  * Send the window size to the server via the magic escape
63124726Smckusick  */
63258470Sbostic void
sendwindow()63324726Smckusick sendwindow()
63424726Smckusick {
63540858Sbostic 	struct winsize *wp;
63624726Smckusick 	char obuf[4 + sizeof (struct winsize)];
63724726Smckusick 
63840858Sbostic 	wp = (struct winsize *)(obuf+4);
63924726Smckusick 	obuf[0] = 0377;
64024726Smckusick 	obuf[1] = 0377;
64124726Smckusick 	obuf[2] = 's';
64224726Smckusick 	obuf[3] = 's';
64324726Smckusick 	wp->ws_row = htons(winsize.ws_row);
64424726Smckusick 	wp->ws_col = htons(winsize.ws_col);
64524726Smckusick 	wp->ws_xpixel = htons(winsize.ws_xpixel);
64624726Smckusick 	wp->ws_ypixel = htons(winsize.ws_ypixel);
64736511Skfall 
64853033Sleres #ifdef CRYPT
64953033Sleres #ifdef KERBEROS
65053033Sleres 	if(doencrypt)
65153033Sleres 		(void)des_write(rem, obuf, sizeof(obuf));
65253033Sleres 	else
65353033Sleres #endif
65453033Sleres #endif
65540858Sbostic 		(void)write(rem, obuf, sizeof(obuf));
65624726Smckusick }
65724726Smckusick 
65825424Skarels /*
65925424Skarels  * reader: read from remote: line -> 1
66025424Skarels  */
66125424Skarels #define	READING	1
66225424Skarels #define	WRITING	2
66325424Skarels 
66440858Sbostic jmp_buf rcvtop;
66567737Spendry pid_t ppid;
66667737Spendry int rcvcnt, rcvstate;
66740858Sbostic char rcvbuf[8 * 1024];
66825424Skarels 
66940858Sbostic void
oob(signo)67058470Sbostic oob(signo)
67158470Sbostic 	int signo;
6726444Swnj {
67367737Spendry 	struct termios tt;
67440858Sbostic 	int atmark, n, out, rcvd;
6759365Ssam 	char waste[BUFSIZ], mark;
6766444Swnj 
67742230Sbostic 	out = O_RDWR;
67840858Sbostic 	rcvd = 0;
67959175Storek 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
68025424Skarels 		switch (errno) {
68125424Skarels 		case EWOULDBLOCK:
68225424Skarels 			/*
68340858Sbostic 			 * Urgent data not here yet.  It may not be possible
68440858Sbostic 			 * to send it yet if we are blocked for output and
68540858Sbostic 			 * our input buffer is full.
68625424Skarels 			 */
68725424Skarels 			if (rcvcnt < sizeof(rcvbuf)) {
68825424Skarels 				n = read(rem, rcvbuf + rcvcnt,
68940858Sbostic 				    sizeof(rcvbuf) - rcvcnt);
69025424Skarels 				if (n <= 0)
69125424Skarels 					return;
69225424Skarels 				rcvd += n;
69325424Skarels 			} else {
69425424Skarels 				n = read(rem, waste, sizeof(waste));
69525424Skarels 				if (n <= 0)
69625424Skarels 					return;
69725424Skarels 			}
69825424Skarels 			continue;
69925424Skarels 		default:
70025424Skarels 			return;
70159175Storek 		}
7026444Swnj 	}
70325424Skarels 	if (mark & TIOCPKT_WINDOW) {
70440858Sbostic 		/* Let server know about window size changes */
70540858Sbostic 		(void)kill(ppid, SIGUSR1);
70624726Smckusick 	}
70725424Skarels 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
70867737Spendry 		tcgetattr(0, &tt);
70967749Spendry 		tt.c_iflag &= ~(IXON | IXOFF);
71067737Spendry 		tt.c_cc[VSTOP] = _POSIX_VDISABLE;
71167737Spendry 		tt.c_cc[VSTART] = _POSIX_VDISABLE;
71267737Spendry 		tcsetattr(0, TCSANOW, &tt);
7136444Swnj 	}
71425424Skarels 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
71567737Spendry 		tcgetattr(0, &tt);
71667737Spendry 		tt.c_iflag |= (IXON|IXOFF);
71767737Spendry 		tt.c_cc[VSTOP] = deftt.c_cc[VSTOP];
71867737Spendry 		tt.c_cc[VSTART] = deftt.c_cc[VSTART];
71967737Spendry 		tcsetattr(0, TCSANOW, &tt);
7206444Swnj 	}
72125424Skarels 	if (mark & TIOCPKT_FLUSHWRITE) {
72240858Sbostic 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
72325424Skarels 		for (;;) {
72425424Skarels 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
72567737Spendry 				warn("ioctl SIOCATMARK (ignored)");
72625424Skarels 				break;
72725424Skarels 			}
72825424Skarels 			if (atmark)
72925424Skarels 				break;
73025424Skarels 			n = read(rem, waste, sizeof (waste));
73125424Skarels 			if (n <= 0)
73225424Skarels 				break;
73325424Skarels 		}
73425424Skarels 		/*
73540858Sbostic 		 * Don't want any pending data to be output, so clear the recv
73640858Sbostic 		 * buffer.  If we were hanging on a write when interrupted,
73740858Sbostic 		 * don't want it to restart.  If we were reading, restart
73840858Sbostic 		 * anyway.
73925424Skarels 		 */
74025424Skarels 		rcvcnt = 0;
74125424Skarels 		longjmp(rcvtop, 1);
74225424Skarels 	}
74329729Smckusick 
74440858Sbostic 	/* oob does not do FLUSHREAD (alas!) */
74529729Smckusick 
74629729Smckusick 	/*
74740858Sbostic 	 * If we filled the receive buffer while a read was pending, longjmp
74840858Sbostic 	 * to the top to restart appropriately.  Don't abort a pending write,
74940858Sbostic 	 * however, or we won't know how much was written.
75025424Skarels 	 */
75125424Skarels 	if (rcvd && rcvstate == READING)
75225424Skarels 		longjmp(rcvtop, 1);
7536444Swnj }
7546444Swnj 
75540858Sbostic /* reader: read from remote: line -> 1 */
75658470Sbostic int
reader(smask)75767737Spendry reader(smask)
75867737Spendry 	sigset_t *smask;
7596444Swnj {
76067737Spendry 	pid_t pid;
76167737Spendry 	int n, remaining;
76259175Storek 	char *bufp;
76367737Spendry 	struct sigaction sa;
76440858Sbostic 
76559175Storek #if BSD >= 43 || defined(SUNOS4)
76659175Storek 	pid = getpid();		/* modern systems use positives for pid */
76726981Skarels #else
76859175Storek 	pid = -getpid();	/* old broken systems use negatives */
76926981Skarels #endif
77067737Spendry 	sigemptyset(&sa.sa_mask);
77167737Spendry 	sa.sa_flags = SA_RESTART;
77267737Spendry 	sa.sa_handler = SIG_IGN;
77367737Spendry 	(void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
77467737Spendry 	sa.sa_handler = oob;
77567737Spendry 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
77626981Skarels 	ppid = getppid();
77740858Sbostic 	(void)fcntl(rem, F_SETOWN, pid);
77840858Sbostic 	(void)setjmp(rcvtop);
77967737Spendry 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
78059175Storek 	bufp = rcvbuf;
7816444Swnj 	for (;;) {
78225424Skarels 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
78325424Skarels 			rcvstate = WRITING;
78445063Sbostic 			n = write(STDOUT_FILENO, bufp, remaining);
78525424Skarels 			if (n < 0) {
78625424Skarels 				if (errno != EINTR)
78758470Sbostic 					return (-1);
78825424Skarels 				continue;
78925424Skarels 			}
79025424Skarels 			bufp += n;
79125424Skarels 		}
79225424Skarels 		bufp = rcvbuf;
79325424Skarels 		rcvcnt = 0;
79425424Skarels 		rcvstate = READING;
79536511Skfall 
79653033Sleres #ifdef CRYPT
79753033Sleres #ifdef KERBEROS
79853033Sleres 		if (doencrypt)
79953033Sleres 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
80053033Sleres 		else
80153033Sleres #endif
80253033Sleres #endif
80336511Skfall 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
80425424Skarels 		if (rcvcnt == 0)
80525424Skarels 			return (0);
80625424Skarels 		if (rcvcnt < 0) {
8079365Ssam 			if (errno == EINTR)
8086444Swnj 				continue;
80967737Spendry 			warn("read");
81058470Sbostic 			return (-1);
8116444Swnj 		}
8126444Swnj 	}
8136444Swnj }
8146444Swnj 
81558470Sbostic void
mode(f)8166444Swnj mode(f)
81758470Sbostic 	int f;
8186444Swnj {
81967737Spendry 	struct termios tt;
8209365Ssam 
82167737Spendry 	switch (f) {
8229962Ssam 	case 0:
82367737Spendry 		tcsetattr(0, TCSADRAIN, &deftt);
8249962Ssam 		break;
8259962Ssam 	case 1:
82667737Spendry 		tt = deftt;
82767749Spendry 		tt.c_oflag &= ~(OPOST);
82867737Spendry 		tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
82967749Spendry 		tt.c_iflag &= ~(ICRNL);
83067737Spendry 		tt.c_cc[VMIN] = 1;
83167737Spendry 		tt.c_cc[VTIME] = 0;
83267737Spendry 		if (eight) {
83367749Spendry 			tt.c_iflag &= ~(IXON | IXOFF | ISTRIP);
83467737Spendry 			tt.c_cc[VSTOP] = _POSIX_VDISABLE;
83567737Spendry 			tt.c_cc[VSTART] = _POSIX_VDISABLE;
83667737Spendry 		}
83767737Spendry 		/*if (litout)
83867737Spendry 			lflags |= LLITOUT;*/
83967737Spendry 		tcsetattr(0, TCSADRAIN, &tt);
8409962Ssam 		break;
84167737Spendry 
8429962Ssam 	default:
8439962Ssam 		return;
8446444Swnj 	}
8456444Swnj }
8466444Swnj 
84740858Sbostic void
lostpeer(signo)84858470Sbostic lostpeer(signo)
84958470Sbostic 	int signo;
8506444Swnj {
85167737Spendry 	struct sigaction sa;
85267737Spendry 
85367737Spendry 	sigemptyset(&sa.sa_mask);
85467737Spendry 	sa.sa_flags = SA_RESTART;
85567737Spendry 	sa.sa_handler = SIG_IGN;
85667737Spendry 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
85740858Sbostic 	msg("\007connection closed.");
85840858Sbostic 	done(1);
85940858Sbostic }
86029729Smckusick 
86140858Sbostic /* copy SIGURGs to the child process. */
86240858Sbostic void
copytochild(signo)86358470Sbostic copytochild(signo)
86458470Sbostic 	int signo;
86540858Sbostic {
86667737Spendry 
86740858Sbostic 	(void)kill(child, SIGURG);
8686444Swnj }
8696444Swnj 
87058470Sbostic void
msg(str)87140858Sbostic msg(str)
87240858Sbostic 	char *str;
8736444Swnj {
87467737Spendry 
87540858Sbostic 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
87640858Sbostic }
87729729Smckusick 
87840858Sbostic #ifdef KERBEROS
87940858Sbostic /* VARARGS */
88058470Sbostic void
88158470Sbostic #if __STDC__
warning(const char * fmt,...)88258470Sbostic warning(const char *fmt, ...)
88358470Sbostic #else
88458470Sbostic warning(fmt, va_alist)
88558470Sbostic 	char *fmt;
88658470Sbostic 	va_dcl
88758470Sbostic #endif
88840858Sbostic {
88940858Sbostic 	va_list ap;
89040858Sbostic 
89140858Sbostic 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
89258470Sbostic #ifdef __STDC__
89358470Sbostic 	va_start(ap, fmt);
89458470Sbostic #else
89540858Sbostic 	va_start(ap);
89658470Sbostic #endif
89740858Sbostic 	vfprintf(stderr, fmt, ap);
89840858Sbostic 	va_end(ap);
89940858Sbostic 	(void)fprintf(stderr, ".\n");
9006444Swnj }
90140858Sbostic #endif
90236512Skfall 
90358470Sbostic __dead void
usage()90440858Sbostic usage()
90536512Skfall {
90640858Sbostic 	(void)fprintf(stderr,
907*69118Svjs 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n",
90840858Sbostic #ifdef KERBEROS
90953033Sleres #ifdef CRYPT
91054125Sbostic 	    "8EKLx", " [-k realm] ");
91153033Sleres #else
91254125Sbostic 	    "8EKL", " [-k realm] ");
91353033Sleres #endif
91445256Smckusick #else
91545063Sbostic 	    "8EL", " ");
91640858Sbostic #endif
91740858Sbostic 	exit(1);
91836512Skfall }
91940858Sbostic 
92040858Sbostic /*
92159175Storek  * The following routine provides compatibility (such as it is) between older
92240858Sbostic  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
92340858Sbostic  */
92459175Storek #ifdef OLDSUN
92558470Sbostic int
get_window_size(fd,wp)92640858Sbostic get_window_size(fd, wp)
92740858Sbostic 	int fd;
92840858Sbostic 	struct winsize *wp;
92940858Sbostic {
93040858Sbostic 	struct ttysize ts;
93140858Sbostic 	int error;
93240858Sbostic 
93340858Sbostic 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
93458470Sbostic 		return (error);
93540858Sbostic 	wp->ws_row = ts.ts_lines;
93640858Sbostic 	wp->ws_col = ts.ts_cols;
93740858Sbostic 	wp->ws_xpixel = 0;
93840858Sbostic 	wp->ws_ypixel = 0;
93958470Sbostic 	return (0);
94040858Sbostic }
94140858Sbostic #endif
94245063Sbostic 
94358470Sbostic u_int
getescape(p)94445063Sbostic getescape(p)
94545063Sbostic 	register char *p;
94645063Sbostic {
94745063Sbostic 	long val;
94845063Sbostic 	int len;
94945063Sbostic 
95045063Sbostic 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
95158470Sbostic 		return ((u_int)*p);
95245063Sbostic 					/* otherwise, \nnn */
95345063Sbostic 	if (*p == '\\' && len >= 2 && len <= 4) {
95458470Sbostic 		val = strtol(++p, NULL, 8);
95545063Sbostic 		for (;;) {
95645063Sbostic 			if (!*++p)
95758470Sbostic 				return ((u_int)val);
95845063Sbostic 			if (*p < '0' || *p > '8')
95945063Sbostic 				break;
96045063Sbostic 		}
96145063Sbostic 	}
96245063Sbostic 	msg("illegal option value -- e");
96345063Sbostic 	usage();
96445063Sbostic 	/* NOTREACHED */
96545063Sbostic }
966