xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 67737)
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*67737Spendry static char sccsid[] = "@(#)rlogin.c	8.2 (Berkeley) 08/23/94";
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>
26*67737Spendry #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>
37*67737Spendry #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));
88*67737Spendry __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));
96*67737Spendry int		reader __P((sigset_t *));
9758470Sbostic void		sendwindow __P((void));
9858470Sbostic void		setsignal __P((int));
9958470Sbostic void		sigwinch __P((int));
10058470Sbostic void		stop __P((char));
10158470Sbostic __dead void	usage __P((void));
10258470Sbostic void		writer __P((void));
10358470Sbostic void		writeroob __P((int));
10458470Sbostic 
10558470Sbostic #ifdef	KERBEROS
10658470Sbostic void		warning __P((const char *, ...));
10740858Sbostic #endif
10859175Storek #ifdef OLDSUN
10958470Sbostic int		get_window_size __P((int, struct winsize *));
11058470Sbostic #endif
11129729Smckusick 
11258470Sbostic int
1136444Swnj main(argc, argv)
1146444Swnj 	int argc;
11558470Sbostic 	char *argv[];
1166444Swnj {
11740858Sbostic 	struct passwd *pw;
11840858Sbostic 	struct servent *sp;
119*67737Spendry 	sigset_t smask;
120*67737Spendry 	uid_t uid;
121*67737Spendry 	int argoff, ch, dflag, one;
12240858Sbostic 	char *host, *p, *user, term[1024];
123*67737Spendry 	struct sigaction sa;
1246444Swnj 
12540858Sbostic 	argoff = dflag = 0;
12640858Sbostic 	one = 1;
12740858Sbostic 	host = user = NULL;
12840858Sbostic 
129*67737Spendry 	if (p = strrchr(argv[0], '/'))
13040858Sbostic 		++p;
1316444Swnj 	else
13240858Sbostic 		p = argv[0];
13340858Sbostic 
134*67737Spendry 	if (strcmp(p, "rlogin") != 0)
13540858Sbostic 		host = p;
13640858Sbostic 
13740858Sbostic 	/* handle "rlogin host flags" */
13840858Sbostic 	if (!host && argc > 2 && argv[1][0] != '-') {
13940858Sbostic 		host = argv[1];
14040858Sbostic 		argoff = 1;
1416444Swnj 	}
14236511Skfall 
14340858Sbostic #ifdef KERBEROS
14445063Sbostic #define	OPTIONS	"8EKLde:k:l:x"
14540858Sbostic #else
14645063Sbostic #define	OPTIONS	"8EKLde:l:"
14740858Sbostic #endif
14840858Sbostic 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
14940858Sbostic 		switch(ch) {
15040858Sbostic 		case '8':
15140858Sbostic 			eight = 1;
15240858Sbostic 			break;
15345063Sbostic 		case 'E':
15445063Sbostic 			noescape = 1;
15545063Sbostic 			break;
15640868Sbostic 		case 'K':
15740868Sbostic #ifdef KERBEROS
15840868Sbostic 			use_kerberos = 0;
15940868Sbostic #endif
16040868Sbostic 			break;
16140858Sbostic 		case 'L':
16240858Sbostic 			litout = 1;
16340858Sbostic 			break;
16440858Sbostic 		case 'd':
16540858Sbostic 			dflag = 1;
16640858Sbostic 			break;
16740858Sbostic 		case 'e':
16853033Sleres 			noescape = 0;
16945063Sbostic 			escapechar = getescape(optarg);
17040858Sbostic 			break;
17140858Sbostic #ifdef KERBEROS
17240858Sbostic 		case 'k':
17340858Sbostic 			dest_realm = dst_realm_buf;
17440858Sbostic 			(void)strncpy(dest_realm, optarg, REALM_SZ);
17540858Sbostic 			break;
17640858Sbostic #endif
17740858Sbostic 		case 'l':
17840858Sbostic 			user = optarg;
17940858Sbostic 			break;
18053033Sleres #ifdef CRYPT
18153033Sleres #ifdef KERBEROS
18253033Sleres 		case 'x':
18353033Sleres 			doencrypt = 1;
18453033Sleres 			des_set_key(cred.session, schedule);
18553033Sleres 			break;
18653033Sleres #endif
18753033Sleres #endif
18840858Sbostic 		case '?':
18940858Sbostic 		default:
19040858Sbostic 			usage();
19136524Skfall 		}
19240858Sbostic 	optind += argoff;
19340858Sbostic 	argc -= optind;
19440858Sbostic 	argv += optind;
19536524Skfall 
19640858Sbostic 	/* if haven't gotten a host yet, do so */
19740858Sbostic 	if (!host && !(host = *argv++))
19840858Sbostic 		usage();
19936511Skfall 
20040858Sbostic 	if (*argv)
20140858Sbostic 		usage();
20240858Sbostic 
203*67737Spendry 	if (!(pw = getpwuid(uid = getuid())))
204*67737Spendry 		errx(1, "unknown user id.");
205*67737Spendry 
20640858Sbostic 	if (!user)
20740858Sbostic 		user = pw->pw_name;
20840858Sbostic 
20940868Sbostic 	sp = NULL;
21040858Sbostic #ifdef KERBEROS
21140868Sbostic 	if (use_kerberos) {
21246850Sbostic 		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
21340868Sbostic 		if (sp == NULL) {
21440868Sbostic 			use_kerberos = 0;
21540868Sbostic 			warning("can't get entry for %s/tcp service",
21646850Sbostic 			    doencrypt ? "eklogin" : "klogin");
21740868Sbostic 		}
21836512Skfall 	}
21936512Skfall #endif
22040868Sbostic 	if (sp == NULL)
22140868Sbostic 		sp = getservbyname("login", "tcp");
222*67737Spendry 	if (sp == NULL)
223*67737Spendry 		errx(1, "login/tcp: unknown service.");
22440858Sbostic 
225*67737Spendry 	(void)snprintf(term, sizeof(term), "%s/%d",
226*67737Spendry 			((p = getenv("TERM")) ? p : "network"),
227*67737Spendry 			cfgetispeed(0));
22840858Sbostic 
22940858Sbostic 	(void)get_window_size(0, &winsize);
23040858Sbostic 
231*67737Spendry 	sigemptyset(&sa.sa_mask);
232*67737Spendry 	sa.sa_flags = SA_RESTART;
233*67737Spendry 	sa.sa_handler = lostpeer;
234*67737Spendry 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
23529729Smckusick 	/* will use SIGUSR1 for window size hack, so hold it off */
236*67737Spendry 	sigemptyset(&smask);
237*67737Spendry 	sigaddset(&smask, SIGURG);
238*67737Spendry 	sigaddset(&smask, SIGUSR1);
239*67737Spendry 	(void)sigprocmask(SIG_SETMASK, &smask, &smask);
24058261Smckusick 	/*
24158261Smckusick 	 * We set SIGURG and SIGUSR1 below so that an
24258261Smckusick 	 * incoming signal will be held pending rather than being
24358261Smckusick 	 * discarded. Note that these routines will be ready to get
24458261Smckusick 	 * a signal by the time that they are unblocked below.
24558261Smckusick 	 */
246*67737Spendry 	sa.sa_handler = copytochild;
247*67737Spendry 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
248*67737Spendry 	sa.sa_handler = writeroob;
249*67737Spendry 	(void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
25036511Skfall 
25140858Sbostic #ifdef KERBEROS
25236512Skfall try_connect:
25340858Sbostic 	if (use_kerberos) {
25458470Sbostic 		struct hostent *hp;
25558470Sbostic 
25658470Sbostic 		/* Fully qualify hostname (needed for krb_realmofhost). */
25756960Seric 		hp = gethostbyname(host);
258*67737Spendry 		if (hp != NULL && !(host = strdup(hp->h_name)))
259*67737Spendry 			errx(1, "%s", strerror(ENOMEM));
26056960Seric 
26136512Skfall 		rem = KSUCCESS;
26240858Sbostic 		errno = 0;
26338728Skfall 		if (dest_realm == NULL)
26438728Skfall 			dest_realm = krb_realmofhost(host);
26538728Skfall 
26653033Sleres #ifdef CRYPT
26753033Sleres 		if (doencrypt)
26853033Sleres 			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
26953033Sleres 			    dest_realm, &cred, schedule);
27053033Sleres 		else
27153033Sleres #endif /* CRYPT */
27240858Sbostic 			rem = krcmd(&host, sp->s_port, user, term, 0,
27340858Sbostic 			    dest_realm);
27438728Skfall 		if (rem < 0) {
27536512Skfall 			use_kerberos = 0;
27636628Skfall 			sp = getservbyname("login", "tcp");
277*67737Spendry 			if (sp == NULL)
278*67737Spendry 				errx(1, "unknown service login/tcp.");
27938728Skfall 			if (errno == ECONNREFUSED)
28040858Sbostic 				warning("remote host doesn't support Kerberos");
28138728Skfall 			if (errno == ENOENT)
28240858Sbostic 				warning("can't provide Kerberos auth data");
28336512Skfall 			goto try_connect;
28436512Skfall 		}
28536511Skfall 	} else {
28653033Sleres #ifdef CRYPT
287*67737Spendry 		if (doencrypt)
288*67737Spendry 			errx(1, "the -x flag requires Kerberos authentication.");
28953033Sleres #endif /* CRYPT */
29040858Sbostic 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
29136511Skfall 	}
29236512Skfall #else
29340858Sbostic 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
29445256Smckusick #endif /* KERBEROS */
29536511Skfall 
29640858Sbostic 	if (rem < 0)
29736511Skfall 		exit(1);
29836511Skfall 
29940858Sbostic 	if (dflag &&
30040858Sbostic 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
301*67737Spendry 		warn("setsockopt DEBUG (ignored)");
30244346Skarels 	one = IPTOS_LOWDELAY;
30344346Skarels 	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
304*67737Spendry 		warn("setsockopt TOS (ignored)");
30540858Sbostic 
30640858Sbostic 	(void)setuid(uid);
307*67737Spendry 	doit(&smask);
3089365Ssam 	/*NOTREACHED*/
3096444Swnj }
3106444Swnj 
311*67737Spendry pid_t child;
312*67737Spendry struct termios deftt;
313*67737Spendry struct termios nott;
3146444Swnj 
31558470Sbostic void
316*67737Spendry doit(smask)
317*67737Spendry 	sigset_t *smask;
3186444Swnj {
319*67737Spendry 	int i;
320*67737Spendry 	struct sigaction sa;
3216444Swnj 
322*67737Spendry 	for (i = 0; i < NCCS; i++)
323*67737Spendry 		nott.c_cc[i] = _POSIX_VDISABLE;
324*67737Spendry 	tcgetattr(0, &deftt);
325*67737Spendry 	nott.c_cc[VSTART] = deftt.c_cc[VSTART];
326*67737Spendry 	nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
327*67737Spendry 	sigemptyset(&sa.sa_mask);
328*67737Spendry 	sa.sa_flags = SA_RESTART;
329*67737Spendry 	sa.sa_handler = SIG_IGN;
330*67737Spendry 	(void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
33158470Sbostic 	setsignal(SIGHUP);
33258470Sbostic 	setsignal(SIGQUIT);
3339365Ssam 	child = fork();
3349365Ssam 	if (child == -1) {
335*67737Spendry 		warn("fork");
33625424Skarels 		done(1);
3379365Ssam 	}
3389365Ssam 	if (child == 0) {
33924726Smckusick 		mode(1);
340*67737Spendry 		if (reader(smask) == 0) {
34140858Sbostic 			msg("connection closed.");
34225424Skarels 			exit(0);
34325424Skarels 		}
34412155Ssam 		sleep(1);
34540858Sbostic 		msg("\007connection closed.");
34640858Sbostic 		exit(1);
3476444Swnj 	}
34829729Smckusick 
34929729Smckusick 	/*
35040858Sbostic 	 * We may still own the socket, and may have a pending SIGURG (or might
35158261Smckusick 	 * receive one soon) that we really want to send to the reader.  When
35258261Smckusick 	 * one of these comes in, the trap copytochild simply copies such
35358261Smckusick 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
35458261Smckusick 	 * that were set above.
35529729Smckusick 	 */
356*67737Spendry 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
357*67737Spendry 	sa.sa_handler = catch_child;
358*67737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
3599365Ssam 	writer();
36040858Sbostic 	msg("closed connection.");
36125424Skarels 	done(0);
3626444Swnj }
3636444Swnj 
36440858Sbostic /* trap a signal, unless it is being ignored. */
36558470Sbostic void
36658470Sbostic setsignal(sig)
36740858Sbostic 	int sig;
36829729Smckusick {
369*67737Spendry 	struct sigaction sa;
370*67737Spendry 	sigset_t sigs;
37129729Smckusick 
372*67737Spendry 	sigemptyset(&sigs);
373*67737Spendry 	sigaddset(&sigs, sig);
374*67737Spendry 	sigprocmask(SIG_BLOCK, &sigs, &sigs);
375*67737Spendry 
376*67737Spendry 	sigemptyset(&sa.sa_mask);
377*67737Spendry 	sa.sa_handler = exit;
378*67737Spendry 	sa.sa_flags = SA_RESTART;
379*67737Spendry 	(void)sigaction(sig, &sa, &sa);
380*67737Spendry 	if (sa.sa_handler == SIG_IGN)
381*67737Spendry 		(void)sigaction(sig, &sa, (struct sigaction *) 0);
382*67737Spendry 
383*67737Spendry 	(void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
38429729Smckusick }
38529729Smckusick 
38658470Sbostic __dead void
38725424Skarels done(status)
38825424Skarels 	int status;
3896444Swnj {
390*67737Spendry 	pid_t w;
391*67737Spendry 	int wstatus;
392*67737Spendry 	struct sigaction sa;
3936444Swnj 
3946444Swnj 	mode(0);
39529729Smckusick 	if (child > 0) {
39640858Sbostic 		/* make sure catch_child does not snap it up */
397*67737Spendry 		sigemptyset(&sa.sa_mask);
398*67737Spendry 		sa.sa_handler = SIG_DFL;
399*67737Spendry 		sa.sa_flags = 0;
400*67737Spendry 		(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
40129729Smckusick 		if (kill(child, SIGKILL) >= 0)
402*67737Spendry 			while ((w = wait(&wstatus)) > 0 && w != child)
403*67737Spendry 				continue;
40429729Smckusick 	}
40525424Skarels 	exit(status);
4066444Swnj }
4076444Swnj 
40840858Sbostic int dosigwinch;
40929729Smckusick 
41029729Smckusick /*
41124726Smckusick  * This is called when the reader process gets the out-of-band (urgent)
41224726Smckusick  * request to turn on the window-changing protocol.
41324726Smckusick  */
41440858Sbostic void
41558470Sbostic writeroob(signo)
41658470Sbostic 	int signo;
41724726Smckusick {
418*67737Spendry 	struct sigaction sa;
419*67737Spendry 
42025341Smckusick 	if (dosigwinch == 0) {
42124919Smckusick 		sendwindow();
422*67737Spendry 		sigemptyset(&sa.sa_mask);
423*67737Spendry 		sa.sa_handler = sigwinch;
424*67737Spendry 		sa.sa_flags = SA_RESTART;
425*67737Spendry 		(void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
42625341Smckusick 	}
42724726Smckusick 	dosigwinch = 1;
42824726Smckusick }
42924726Smckusick 
43040858Sbostic void
43158470Sbostic catch_child(signo)
43258470Sbostic 	int signo;
43311803Sedward {
434*67737Spendry 	int status;
435*67737Spendry 	pid_t pid;
43611803Sedward 
43740858Sbostic 	for (;;) {
438*67737Spendry 		pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
43940858Sbostic 		if (pid == 0)
44040858Sbostic 			return;
44140858Sbostic 		/* if the child (reader) dies, just quit */
44258470Sbostic 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
443*67737Spendry 			done(WEXITSTATUS(status) | WTERMSIG(status));
44440858Sbostic 	}
44540858Sbostic 	/* NOTREACHED */
44611803Sedward }
44711803Sedward 
4486444Swnj /*
4499365Ssam  * writer: write to remote: 0 -> line.
45045063Sbostic  * ~.				terminate
45145063Sbostic  * ~^Z				suspend rlogin process.
45245063Sbostic  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
4536444Swnj  */
45458470Sbostic void
4559365Ssam writer()
4566444Swnj {
45745063Sbostic 	register int bol, local, n;
45823530Sbloom 	char c;
4596444Swnj 
46040858Sbostic 	bol = 1;			/* beginning of line */
46140858Sbostic 	local = 0;
46211803Sedward 	for (;;) {
46340858Sbostic 		n = read(STDIN_FILENO, &c, 1);
46418358Ssam 		if (n <= 0) {
46518358Ssam 			if (n < 0 && errno == EINTR)
46618358Ssam 				continue;
46711803Sedward 			break;
46818358Ssam 		}
4699365Ssam 		/*
47040858Sbostic 		 * If we're at the beginning of the line and recognize a
47140858Sbostic 		 * command character, then we echo locally.  Otherwise,
47240858Sbostic 		 * characters are echo'd remotely.  If the command character
47340858Sbostic 		 * is doubled, this acts as a force and local echo is
47440858Sbostic 		 * suppressed.
4759365Ssam 		 */
47623530Sbloom 		if (bol) {
47723530Sbloom 			bol = 0;
47845063Sbostic 			if (!noescape && c == escapechar) {
47923530Sbloom 				local = 1;
48023530Sbloom 				continue;
4816444Swnj 			}
48223530Sbloom 		} else if (local) {
48323530Sbloom 			local = 0;
484*67737Spendry 			if (c == '.' || c == deftt.c_cc[VEOF]) {
48523530Sbloom 				echo(c);
48623530Sbloom 				break;
4876444Swnj 			}
488*67737Spendry 			if (c == deftt.c_cc[VSUSP] || c == deftt.c_cc[VDSUSP]) {
48923530Sbloom 				bol = 1;
49023530Sbloom 				echo(c);
49123530Sbloom 				stop(c);
49223530Sbloom 				continue;
49323530Sbloom 			}
49445063Sbostic 			if (c != escapechar)
49553033Sleres #ifdef CRYPT
49653033Sleres #ifdef KERBEROS
49753033Sleres 				if (doencrypt)
49858470Sbostic 					(void)des_write(rem,
49958470Sbostic 					    (char *)&escapechar, 1);
50053033Sleres 				else
50153033Sleres #endif
50253033Sleres #endif
50345063Sbostic 					(void)write(rem, &escapechar, 1);
5046444Swnj 		}
50536511Skfall 
50653033Sleres #ifdef CRYPT
50753033Sleres #ifdef KERBEROS
50853033Sleres 		if (doencrypt) {
50953033Sleres 			if (des_write(rem, &c, 1) == 0) {
51053033Sleres 				msg("line gone");
51153033Sleres 				break;
51253033Sleres 			}
51353033Sleres 		} else
51453033Sleres #endif
51553033Sleres #endif
51636511Skfall 			if (write(rem, &c, 1) == 0) {
51740858Sbostic 				msg("line gone");
51836511Skfall 				break;
51936511Skfall 			}
520*67737Spendry 		bol = c == deftt.c_cc[VKILL] || c == deftt.c_cc[VEOF] ||
521*67737Spendry 		    c == deftt.c_cc[VINTR] || c == deftt.c_cc[VSUSP] ||
52223530Sbloom 		    c == '\r' || c == '\n';
5236444Swnj 	}
5246444Swnj }
5256444Swnj 
52658470Sbostic void
52758470Sbostic #if __STDC__
52858470Sbostic echo(register char c)
52958470Sbostic #else
53023530Sbloom echo(c)
53158470Sbostic 	register char c;
53258470Sbostic #endif
53323530Sbloom {
53440858Sbostic 	register char *p;
53523530Sbloom 	char buf[8];
53623530Sbloom 
53740858Sbostic 	p = buf;
53823530Sbloom 	c &= 0177;
53945063Sbostic 	*p++ = escapechar;
54023530Sbloom 	if (c < ' ') {
54123530Sbloom 		*p++ = '^';
54223530Sbloom 		*p++ = c + '@';
54323530Sbloom 	} else if (c == 0177) {
54423530Sbloom 		*p++ = '^';
54523530Sbloom 		*p++ = '?';
54623530Sbloom 	} else
54723530Sbloom 		*p++ = c;
54823530Sbloom 	*p++ = '\r';
54923530Sbloom 	*p++ = '\n';
55045063Sbostic 	(void)write(STDOUT_FILENO, buf, p - buf);
55123530Sbloom }
55223530Sbloom 
55358470Sbostic void
55458470Sbostic #if __STDC__
55558470Sbostic stop(char cmdc)
55658470Sbostic #else
55718358Ssam stop(cmdc)
55818358Ssam 	char cmdc;
55958470Sbostic #endif
56018358Ssam {
561*67737Spendry 	struct sigaction sa;
562*67737Spendry 
56318358Ssam 	mode(0);
564*67737Spendry 	sigemptyset(&sa.sa_mask);
565*67737Spendry 	sa.sa_handler = SIG_IGN;
566*67737Spendry 	sa.sa_flags = SA_RESTART;
567*67737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
568*67737Spendry 	(void)kill(cmdc == deftt.c_cc[VSUSP] ? 0 : getpid(), SIGTSTP);
569*67737Spendry 	sa.sa_handler = catch_child;
570*67737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
57118358Ssam 	mode(1);
57258470Sbostic 	sigwinch(0);			/* check for size changes */
57318358Ssam }
57418358Ssam 
57540858Sbostic void
57658470Sbostic sigwinch(signo)
57758470Sbostic 	int signo;
57818358Ssam {
57918358Ssam 	struct winsize ws;
58018358Ssam 
58129729Smckusick 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
582*67737Spendry 	    memcmp(&ws, &winsize, sizeof(ws))) {
58318358Ssam 		winsize = ws;
58424726Smckusick 		sendwindow();
58518358Ssam 	}
58618358Ssam }
58718358Ssam 
58824726Smckusick /*
58924726Smckusick  * Send the window size to the server via the magic escape
59024726Smckusick  */
59158470Sbostic void
59224726Smckusick sendwindow()
59324726Smckusick {
59440858Sbostic 	struct winsize *wp;
59524726Smckusick 	char obuf[4 + sizeof (struct winsize)];
59624726Smckusick 
59740858Sbostic 	wp = (struct winsize *)(obuf+4);
59824726Smckusick 	obuf[0] = 0377;
59924726Smckusick 	obuf[1] = 0377;
60024726Smckusick 	obuf[2] = 's';
60124726Smckusick 	obuf[3] = 's';
60224726Smckusick 	wp->ws_row = htons(winsize.ws_row);
60324726Smckusick 	wp->ws_col = htons(winsize.ws_col);
60424726Smckusick 	wp->ws_xpixel = htons(winsize.ws_xpixel);
60524726Smckusick 	wp->ws_ypixel = htons(winsize.ws_ypixel);
60636511Skfall 
60753033Sleres #ifdef CRYPT
60853033Sleres #ifdef KERBEROS
60953033Sleres 	if(doencrypt)
61053033Sleres 		(void)des_write(rem, obuf, sizeof(obuf));
61153033Sleres 	else
61253033Sleres #endif
61353033Sleres #endif
61440858Sbostic 		(void)write(rem, obuf, sizeof(obuf));
61524726Smckusick }
61624726Smckusick 
61725424Skarels /*
61825424Skarels  * reader: read from remote: line -> 1
61925424Skarels  */
62025424Skarels #define	READING	1
62125424Skarels #define	WRITING	2
62225424Skarels 
62340858Sbostic jmp_buf rcvtop;
624*67737Spendry pid_t ppid;
625*67737Spendry int rcvcnt, rcvstate;
62640858Sbostic char rcvbuf[8 * 1024];
62725424Skarels 
62840858Sbostic void
62958470Sbostic oob(signo)
63058470Sbostic 	int signo;
6316444Swnj {
632*67737Spendry 	struct termios tt;
63340858Sbostic 	int atmark, n, out, rcvd;
6349365Ssam 	char waste[BUFSIZ], mark;
6356444Swnj 
63642230Sbostic 	out = O_RDWR;
63740858Sbostic 	rcvd = 0;
63859175Storek 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
63925424Skarels 		switch (errno) {
64025424Skarels 		case EWOULDBLOCK:
64125424Skarels 			/*
64240858Sbostic 			 * Urgent data not here yet.  It may not be possible
64340858Sbostic 			 * to send it yet if we are blocked for output and
64440858Sbostic 			 * our input buffer is full.
64525424Skarels 			 */
64625424Skarels 			if (rcvcnt < sizeof(rcvbuf)) {
64725424Skarels 				n = read(rem, rcvbuf + rcvcnt,
64840858Sbostic 				    sizeof(rcvbuf) - rcvcnt);
64925424Skarels 				if (n <= 0)
65025424Skarels 					return;
65125424Skarels 				rcvd += n;
65225424Skarels 			} else {
65325424Skarels 				n = read(rem, waste, sizeof(waste));
65425424Skarels 				if (n <= 0)
65525424Skarels 					return;
65625424Skarels 			}
65725424Skarels 			continue;
65825424Skarels 		default:
65925424Skarels 			return;
66059175Storek 		}
6616444Swnj 	}
66225424Skarels 	if (mark & TIOCPKT_WINDOW) {
66340858Sbostic 		/* Let server know about window size changes */
66440858Sbostic 		(void)kill(ppid, SIGUSR1);
66524726Smckusick 	}
66625424Skarels 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
667*67737Spendry 		tcgetattr(0, &tt);
668*67737Spendry 		tt.c_iflag &= ~(IXON|IXOFF);
669*67737Spendry 		tt.c_cc[VSTOP] = _POSIX_VDISABLE;
670*67737Spendry 		tt.c_cc[VSTART] = _POSIX_VDISABLE;
671*67737Spendry 		tcsetattr(0, TCSANOW, &tt);
6726444Swnj 	}
67325424Skarels 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
674*67737Spendry 		tcgetattr(0, &tt);
675*67737Spendry 		tt.c_iflag |= (IXON|IXOFF);
676*67737Spendry 		tt.c_cc[VSTOP] = deftt.c_cc[VSTOP];
677*67737Spendry 		tt.c_cc[VSTART] = deftt.c_cc[VSTART];
678*67737Spendry 		tcsetattr(0, TCSANOW, &tt);
6796444Swnj 	}
68025424Skarels 	if (mark & TIOCPKT_FLUSHWRITE) {
68140858Sbostic 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
68225424Skarels 		for (;;) {
68325424Skarels 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
684*67737Spendry 				warn("ioctl SIOCATMARK (ignored)");
68525424Skarels 				break;
68625424Skarels 			}
68725424Skarels 			if (atmark)
68825424Skarels 				break;
68925424Skarels 			n = read(rem, waste, sizeof (waste));
69025424Skarels 			if (n <= 0)
69125424Skarels 				break;
69225424Skarels 		}
69325424Skarels 		/*
69440858Sbostic 		 * Don't want any pending data to be output, so clear the recv
69540858Sbostic 		 * buffer.  If we were hanging on a write when interrupted,
69640858Sbostic 		 * don't want it to restart.  If we were reading, restart
69740858Sbostic 		 * anyway.
69825424Skarels 		 */
69925424Skarels 		rcvcnt = 0;
70025424Skarels 		longjmp(rcvtop, 1);
70125424Skarels 	}
70229729Smckusick 
70340858Sbostic 	/* oob does not do FLUSHREAD (alas!) */
70429729Smckusick 
70529729Smckusick 	/*
70640858Sbostic 	 * If we filled the receive buffer while a read was pending, longjmp
70740858Sbostic 	 * to the top to restart appropriately.  Don't abort a pending write,
70840858Sbostic 	 * however, or we won't know how much was written.
70925424Skarels 	 */
71025424Skarels 	if (rcvd && rcvstate == READING)
71125424Skarels 		longjmp(rcvtop, 1);
7126444Swnj }
7136444Swnj 
71440858Sbostic /* reader: read from remote: line -> 1 */
71558470Sbostic int
716*67737Spendry reader(smask)
717*67737Spendry 	sigset_t *smask;
7186444Swnj {
719*67737Spendry 	pid_t pid;
720*67737Spendry 	int n, remaining;
72159175Storek 	char *bufp;
722*67737Spendry 	struct sigaction sa;
72340858Sbostic 
72459175Storek #if BSD >= 43 || defined(SUNOS4)
72559175Storek 	pid = getpid();		/* modern systems use positives for pid */
72626981Skarels #else
72759175Storek 	pid = -getpid();	/* old broken systems use negatives */
72826981Skarels #endif
729*67737Spendry 	sigemptyset(&sa.sa_mask);
730*67737Spendry 	sa.sa_flags = SA_RESTART;
731*67737Spendry 	sa.sa_handler = SIG_IGN;
732*67737Spendry 	(void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
733*67737Spendry 	sa.sa_handler = oob;
734*67737Spendry 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
73526981Skarels 	ppid = getppid();
73640858Sbostic 	(void)fcntl(rem, F_SETOWN, pid);
73740858Sbostic 	(void)setjmp(rcvtop);
738*67737Spendry 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
73959175Storek 	bufp = rcvbuf;
7406444Swnj 	for (;;) {
74125424Skarels 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
74225424Skarels 			rcvstate = WRITING;
74345063Sbostic 			n = write(STDOUT_FILENO, bufp, remaining);
74425424Skarels 			if (n < 0) {
74525424Skarels 				if (errno != EINTR)
74658470Sbostic 					return (-1);
74725424Skarels 				continue;
74825424Skarels 			}
74925424Skarels 			bufp += n;
75025424Skarels 		}
75125424Skarels 		bufp = rcvbuf;
75225424Skarels 		rcvcnt = 0;
75325424Skarels 		rcvstate = READING;
75436511Skfall 
75553033Sleres #ifdef CRYPT
75653033Sleres #ifdef KERBEROS
75753033Sleres 		if (doencrypt)
75853033Sleres 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
75953033Sleres 		else
76053033Sleres #endif
76153033Sleres #endif
76236511Skfall 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
76325424Skarels 		if (rcvcnt == 0)
76425424Skarels 			return (0);
76525424Skarels 		if (rcvcnt < 0) {
7669365Ssam 			if (errno == EINTR)
7676444Swnj 				continue;
768*67737Spendry 			warn("read");
76958470Sbostic 			return (-1);
7706444Swnj 		}
7716444Swnj 	}
7726444Swnj }
7736444Swnj 
77458470Sbostic void
7756444Swnj mode(f)
77658470Sbostic 	int f;
7776444Swnj {
778*67737Spendry 	struct termios tt;
7799365Ssam 
780*67737Spendry 	switch (f) {
7819962Ssam 	case 0:
782*67737Spendry 		tcsetattr(0, TCSADRAIN, &deftt);
7839962Ssam 		break;
7849962Ssam 	case 1:
785*67737Spendry 		tt = deftt;
786*67737Spendry 		tt.c_oflag &= ~OPOST;
787*67737Spendry 		tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
788*67737Spendry 		tt.c_cc[VMIN] = 1;
789*67737Spendry 		tt.c_cc[VTIME] = 0;
790*67737Spendry 		if (eight) {
791*67737Spendry 			tt.c_iflag &= ~(IXON | IXOFF);
792*67737Spendry 			tt.c_cc[VSTOP] = _POSIX_VDISABLE;
793*67737Spendry 			tt.c_cc[VSTART] = _POSIX_VDISABLE;
794*67737Spendry 		}
795*67737Spendry 		/*if (litout)
796*67737Spendry 			lflags |= LLITOUT;*/
797*67737Spendry 		tcsetattr(0, TCSADRAIN, &tt);
7989962Ssam 		break;
799*67737Spendry 
8009962Ssam 	default:
8019962Ssam 		return;
8026444Swnj 	}
8036444Swnj }
8046444Swnj 
80540858Sbostic void
80658470Sbostic lostpeer(signo)
80758470Sbostic 	int signo;
8086444Swnj {
809*67737Spendry 	struct sigaction sa;
810*67737Spendry 
811*67737Spendry 	sigemptyset(&sa.sa_mask);
812*67737Spendry 	sa.sa_flags = SA_RESTART;
813*67737Spendry 	sa.sa_handler = SIG_IGN;
814*67737Spendry 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
81540858Sbostic 	msg("\007connection closed.");
81640858Sbostic 	done(1);
81740858Sbostic }
81829729Smckusick 
81940858Sbostic /* copy SIGURGs to the child process. */
82040858Sbostic void
82158470Sbostic copytochild(signo)
82258470Sbostic 	int signo;
82340858Sbostic {
824*67737Spendry 
82540858Sbostic 	(void)kill(child, SIGURG);
8266444Swnj }
8276444Swnj 
82858470Sbostic void
82940858Sbostic msg(str)
83040858Sbostic 	char *str;
8316444Swnj {
832*67737Spendry 
83340858Sbostic 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
83440858Sbostic }
83529729Smckusick 
83640858Sbostic #ifdef KERBEROS
83740858Sbostic /* VARARGS */
83858470Sbostic void
83958470Sbostic #if __STDC__
84058470Sbostic warning(const char *fmt, ...)
84158470Sbostic #else
84258470Sbostic warning(fmt, va_alist)
84358470Sbostic 	char *fmt;
84458470Sbostic 	va_dcl
84558470Sbostic #endif
84640858Sbostic {
84740858Sbostic 	va_list ap;
84840858Sbostic 
84940858Sbostic 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
85058470Sbostic #ifdef __STDC__
85158470Sbostic 	va_start(ap, fmt);
85258470Sbostic #else
85340858Sbostic 	va_start(ap);
85458470Sbostic #endif
85540858Sbostic 	vfprintf(stderr, fmt, ap);
85640858Sbostic 	va_end(ap);
85740858Sbostic 	(void)fprintf(stderr, ".\n");
8586444Swnj }
85940858Sbostic #endif
86036512Skfall 
86158470Sbostic __dead void
86240858Sbostic usage()
86336512Skfall {
86440858Sbostic 	(void)fprintf(stderr,
86540858Sbostic 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
86640858Sbostic #ifdef KERBEROS
86753033Sleres #ifdef CRYPT
86854125Sbostic 	    "8EKLx", " [-k realm] ");
86953033Sleres #else
87054125Sbostic 	    "8EKL", " [-k realm] ");
87153033Sleres #endif
87245256Smckusick #else
87345063Sbostic 	    "8EL", " ");
87440858Sbostic #endif
87540858Sbostic 	exit(1);
87636512Skfall }
87740858Sbostic 
87840858Sbostic /*
87959175Storek  * The following routine provides compatibility (such as it is) between older
88040858Sbostic  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
88140858Sbostic  */
88259175Storek #ifdef OLDSUN
88358470Sbostic int
88440858Sbostic get_window_size(fd, wp)
88540858Sbostic 	int fd;
88640858Sbostic 	struct winsize *wp;
88740858Sbostic {
88840858Sbostic 	struct ttysize ts;
88940858Sbostic 	int error;
89040858Sbostic 
89140858Sbostic 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
89258470Sbostic 		return (error);
89340858Sbostic 	wp->ws_row = ts.ts_lines;
89440858Sbostic 	wp->ws_col = ts.ts_cols;
89540858Sbostic 	wp->ws_xpixel = 0;
89640858Sbostic 	wp->ws_ypixel = 0;
89758470Sbostic 	return (0);
89840858Sbostic }
89940858Sbostic #endif
90045063Sbostic 
90158470Sbostic u_int
90245063Sbostic getescape(p)
90345063Sbostic 	register char *p;
90445063Sbostic {
90545063Sbostic 	long val;
90645063Sbostic 	int len;
90745063Sbostic 
90845063Sbostic 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
90958470Sbostic 		return ((u_int)*p);
91045063Sbostic 					/* otherwise, \nnn */
91145063Sbostic 	if (*p == '\\' && len >= 2 && len <= 4) {
91258470Sbostic 		val = strtol(++p, NULL, 8);
91345063Sbostic 		for (;;) {
91445063Sbostic 			if (!*++p)
91558470Sbostic 				return ((u_int)val);
91645063Sbostic 			if (*p < '0' || *p > '8')
91745063Sbostic 				break;
91845063Sbostic 		}
91945063Sbostic 	}
92045063Sbostic 	msg("illegal option value -- e");
92145063Sbostic 	usage();
92245063Sbostic 	/* NOTREACHED */
92345063Sbostic }
924