xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 67749)
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*67749Spendry static char sccsid[] = "@(#)rlogin.c	8.3 (Berkeley) 08/31/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>
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));
99*67749Spendry 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
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.");
20667737Spendry 
20740858Sbostic 	if (!user)
20840858Sbostic 		user = pw->pw_name;
20940858Sbostic 
21040868Sbostic 	sp = NULL;
21140858Sbostic #ifdef KERBEROS
21240868Sbostic 	if (use_kerberos) {
21346850Sbostic 		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
21440868Sbostic 		if (sp == NULL) {
21540868Sbostic 			use_kerberos = 0;
21640868Sbostic 			warning("can't get entry for %s/tcp service",
21746850Sbostic 			    doencrypt ? "eklogin" : "klogin");
21840868Sbostic 		}
21936512Skfall 	}
22036512Skfall #endif
22140868Sbostic 	if (sp == NULL)
22240868Sbostic 		sp = getservbyname("login", "tcp");
22367737Spendry 	if (sp == NULL)
22467737Spendry 		errx(1, "login/tcp: unknown service.");
22540858Sbostic 
22667737Spendry 	(void)snprintf(term, sizeof(term), "%s/%d",
22767737Spendry 			((p = getenv("TERM")) ? p : "network"),
228*67749Spendry 			speed(0));
22940858Sbostic 
23040858Sbostic 	(void)get_window_size(0, &winsize);
23140858Sbostic 
23267737Spendry 	sigemptyset(&sa.sa_mask);
23367737Spendry 	sa.sa_flags = SA_RESTART;
23467737Spendry 	sa.sa_handler = lostpeer;
23567737Spendry 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
23629729Smckusick 	/* will use SIGUSR1 for window size hack, so hold it off */
23767737Spendry 	sigemptyset(&smask);
23867737Spendry 	sigaddset(&smask, SIGURG);
23967737Spendry 	sigaddset(&smask, SIGUSR1);
24067737Spendry 	(void)sigprocmask(SIG_SETMASK, &smask, &smask);
24158261Smckusick 	/*
24258261Smckusick 	 * We set SIGURG and SIGUSR1 below so that an
24358261Smckusick 	 * incoming signal will be held pending rather than being
24458261Smckusick 	 * discarded. Note that these routines will be ready to get
24558261Smckusick 	 * a signal by the time that they are unblocked below.
24658261Smckusick 	 */
24767737Spendry 	sa.sa_handler = copytochild;
24867737Spendry 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
24967737Spendry 	sa.sa_handler = writeroob;
25067737Spendry 	(void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
25136511Skfall 
25240858Sbostic #ifdef KERBEROS
25336512Skfall try_connect:
25440858Sbostic 	if (use_kerberos) {
25558470Sbostic 		struct hostent *hp;
25658470Sbostic 
25758470Sbostic 		/* Fully qualify hostname (needed for krb_realmofhost). */
25856960Seric 		hp = gethostbyname(host);
25967737Spendry 		if (hp != NULL && !(host = strdup(hp->h_name)))
26067737Spendry 			errx(1, "%s", strerror(ENOMEM));
26156960Seric 
26236512Skfall 		rem = KSUCCESS;
26340858Sbostic 		errno = 0;
26438728Skfall 		if (dest_realm == NULL)
26538728Skfall 			dest_realm = krb_realmofhost(host);
26638728Skfall 
26753033Sleres #ifdef CRYPT
26853033Sleres 		if (doencrypt)
26953033Sleres 			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
27053033Sleres 			    dest_realm, &cred, schedule);
27153033Sleres 		else
27253033Sleres #endif /* CRYPT */
27340858Sbostic 			rem = krcmd(&host, sp->s_port, user, term, 0,
27440858Sbostic 			    dest_realm);
27538728Skfall 		if (rem < 0) {
27636512Skfall 			use_kerberos = 0;
27736628Skfall 			sp = getservbyname("login", "tcp");
27867737Spendry 			if (sp == NULL)
27967737Spendry 				errx(1, "unknown service login/tcp.");
28038728Skfall 			if (errno == ECONNREFUSED)
28140858Sbostic 				warning("remote host doesn't support Kerberos");
28238728Skfall 			if (errno == ENOENT)
28340858Sbostic 				warning("can't provide Kerberos auth data");
28436512Skfall 			goto try_connect;
28536512Skfall 		}
28636511Skfall 	} else {
28753033Sleres #ifdef CRYPT
28867737Spendry 		if (doencrypt)
28967737Spendry 			errx(1, "the -x flag requires Kerberos authentication.");
29053033Sleres #endif /* CRYPT */
29140858Sbostic 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
29236511Skfall 	}
29336512Skfall #else
29440858Sbostic 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
29545256Smckusick #endif /* KERBEROS */
29636511Skfall 
29740858Sbostic 	if (rem < 0)
29836511Skfall 		exit(1);
29936511Skfall 
30040858Sbostic 	if (dflag &&
30140858Sbostic 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
30267737Spendry 		warn("setsockopt DEBUG (ignored)");
30344346Skarels 	one = IPTOS_LOWDELAY;
30444346Skarels 	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
30567737Spendry 		warn("setsockopt TOS (ignored)");
30640858Sbostic 
30740858Sbostic 	(void)setuid(uid);
30867737Spendry 	doit(&smask);
3099365Ssam 	/*NOTREACHED*/
3106444Swnj }
3116444Swnj 
312*67749Spendry #if BSD >= 198810
313*67749Spendry int
314*67749Spendry speed(fd)
315*67749Spendry 	int fd;
316*67749Spendry {
317*67749Spendry 	struct termios tt;
318*67749Spendry 
319*67749Spendry 	(void)tcgetattr(fd, &tt);
320*67749Spendry 
321*67749Spendry 	return ((int) cfgetispeed(&tt));
322*67749Spendry }
323*67749Spendry #else
324*67749Spendry int    speeds[] = {	/* for older systems, B0 .. EXTB */
325*67749Spendry 	0, 50, 75, 110,
326*67749Spendry 	134, 150, 200, 300,
327*67749Spendry 	600, 1200, 1800, 2400,
328*67749Spendry 	4800, 9600, 19200, 38400
329*67749Spendry };
330*67749Spendry 
331*67749Spendry int
332*67749Spendry speed(fd)
333*67749Spendry 	int fd;
334*67749Spendry {
335*67749Spendry 	struct termios tt;
336*67749Spendry 
337*67749Spendry 	(void)tcgetattr(fd, &tt);
338*67749Spendry 
339*67749Spendry 	return (speeds[(int)cfgetispeed(&tt)]);
340*67749Spendry }
341*67749Spendry #endif
342*67749Spendry 
34367737Spendry pid_t child;
34467737Spendry struct termios deftt;
34567737Spendry struct termios nott;
3466444Swnj 
34758470Sbostic void
34867737Spendry doit(smask)
34967737Spendry 	sigset_t *smask;
3506444Swnj {
35167737Spendry 	int i;
35267737Spendry 	struct sigaction sa;
3536444Swnj 
35467737Spendry 	for (i = 0; i < NCCS; i++)
35567737Spendry 		nott.c_cc[i] = _POSIX_VDISABLE;
35667737Spendry 	tcgetattr(0, &deftt);
35767737Spendry 	nott.c_cc[VSTART] = deftt.c_cc[VSTART];
35867737Spendry 	nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
35967737Spendry 	sigemptyset(&sa.sa_mask);
36067737Spendry 	sa.sa_flags = SA_RESTART;
36167737Spendry 	sa.sa_handler = SIG_IGN;
36267737Spendry 	(void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
36358470Sbostic 	setsignal(SIGHUP);
36458470Sbostic 	setsignal(SIGQUIT);
3659365Ssam 	child = fork();
3669365Ssam 	if (child == -1) {
36767737Spendry 		warn("fork");
36825424Skarels 		done(1);
3699365Ssam 	}
3709365Ssam 	if (child == 0) {
37124726Smckusick 		mode(1);
37267737Spendry 		if (reader(smask) == 0) {
37340858Sbostic 			msg("connection closed.");
37425424Skarels 			exit(0);
37525424Skarels 		}
37612155Ssam 		sleep(1);
37740858Sbostic 		msg("\007connection closed.");
37840858Sbostic 		exit(1);
3796444Swnj 	}
38029729Smckusick 
38129729Smckusick 	/*
38240858Sbostic 	 * We may still own the socket, and may have a pending SIGURG (or might
38358261Smckusick 	 * receive one soon) that we really want to send to the reader.  When
38458261Smckusick 	 * one of these comes in, the trap copytochild simply copies such
38558261Smckusick 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
38658261Smckusick 	 * that were set above.
38729729Smckusick 	 */
38867737Spendry 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
38967737Spendry 	sa.sa_handler = catch_child;
39067737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
3919365Ssam 	writer();
39240858Sbostic 	msg("closed connection.");
39325424Skarels 	done(0);
3946444Swnj }
3956444Swnj 
39640858Sbostic /* trap a signal, unless it is being ignored. */
39758470Sbostic void
39858470Sbostic setsignal(sig)
39940858Sbostic 	int sig;
40029729Smckusick {
40167737Spendry 	struct sigaction sa;
40267737Spendry 	sigset_t sigs;
40329729Smckusick 
40467737Spendry 	sigemptyset(&sigs);
40567737Spendry 	sigaddset(&sigs, sig);
40667737Spendry 	sigprocmask(SIG_BLOCK, &sigs, &sigs);
40767737Spendry 
40867737Spendry 	sigemptyset(&sa.sa_mask);
40967737Spendry 	sa.sa_handler = exit;
41067737Spendry 	sa.sa_flags = SA_RESTART;
41167737Spendry 	(void)sigaction(sig, &sa, &sa);
41267737Spendry 	if (sa.sa_handler == SIG_IGN)
41367737Spendry 		(void)sigaction(sig, &sa, (struct sigaction *) 0);
41467737Spendry 
41567737Spendry 	(void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
41629729Smckusick }
41729729Smckusick 
41858470Sbostic __dead void
41925424Skarels done(status)
42025424Skarels 	int status;
4216444Swnj {
42267737Spendry 	pid_t w;
42367737Spendry 	int wstatus;
42467737Spendry 	struct sigaction sa;
4256444Swnj 
4266444Swnj 	mode(0);
42729729Smckusick 	if (child > 0) {
42840858Sbostic 		/* make sure catch_child does not snap it up */
42967737Spendry 		sigemptyset(&sa.sa_mask);
43067737Spendry 		sa.sa_handler = SIG_DFL;
43167737Spendry 		sa.sa_flags = 0;
43267737Spendry 		(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
43329729Smckusick 		if (kill(child, SIGKILL) >= 0)
43467737Spendry 			while ((w = wait(&wstatus)) > 0 && w != child)
43567737Spendry 				continue;
43629729Smckusick 	}
43725424Skarels 	exit(status);
4386444Swnj }
4396444Swnj 
44040858Sbostic int dosigwinch;
44129729Smckusick 
44229729Smckusick /*
44324726Smckusick  * This is called when the reader process gets the out-of-band (urgent)
44424726Smckusick  * request to turn on the window-changing protocol.
44524726Smckusick  */
44640858Sbostic void
44758470Sbostic writeroob(signo)
44858470Sbostic 	int signo;
44924726Smckusick {
45067737Spendry 	struct sigaction sa;
45167737Spendry 
45225341Smckusick 	if (dosigwinch == 0) {
45324919Smckusick 		sendwindow();
45467737Spendry 		sigemptyset(&sa.sa_mask);
45567737Spendry 		sa.sa_handler = sigwinch;
45667737Spendry 		sa.sa_flags = SA_RESTART;
45767737Spendry 		(void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
45825341Smckusick 	}
45924726Smckusick 	dosigwinch = 1;
46024726Smckusick }
46124726Smckusick 
46240858Sbostic void
46358470Sbostic catch_child(signo)
46458470Sbostic 	int signo;
46511803Sedward {
46667737Spendry 	int status;
46767737Spendry 	pid_t pid;
46811803Sedward 
46940858Sbostic 	for (;;) {
47067737Spendry 		pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
47140858Sbostic 		if (pid == 0)
47240858Sbostic 			return;
47340858Sbostic 		/* if the child (reader) dies, just quit */
47458470Sbostic 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
47567737Spendry 			done(WEXITSTATUS(status) | WTERMSIG(status));
47640858Sbostic 	}
47740858Sbostic 	/* NOTREACHED */
47811803Sedward }
47911803Sedward 
4806444Swnj /*
4819365Ssam  * writer: write to remote: 0 -> line.
48245063Sbostic  * ~.				terminate
48345063Sbostic  * ~^Z				suspend rlogin process.
48445063Sbostic  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
4856444Swnj  */
48658470Sbostic void
4879365Ssam writer()
4886444Swnj {
48945063Sbostic 	register int bol, local, n;
49023530Sbloom 	char c;
4916444Swnj 
49240858Sbostic 	bol = 1;			/* beginning of line */
49340858Sbostic 	local = 0;
49411803Sedward 	for (;;) {
49540858Sbostic 		n = read(STDIN_FILENO, &c, 1);
49618358Ssam 		if (n <= 0) {
49718358Ssam 			if (n < 0 && errno == EINTR)
49818358Ssam 				continue;
49911803Sedward 			break;
50018358Ssam 		}
5019365Ssam 		/*
50240858Sbostic 		 * If we're at the beginning of the line and recognize a
50340858Sbostic 		 * command character, then we echo locally.  Otherwise,
50440858Sbostic 		 * characters are echo'd remotely.  If the command character
50540858Sbostic 		 * is doubled, this acts as a force and local echo is
50640858Sbostic 		 * suppressed.
5079365Ssam 		 */
50823530Sbloom 		if (bol) {
50923530Sbloom 			bol = 0;
51045063Sbostic 			if (!noescape && c == escapechar) {
51123530Sbloom 				local = 1;
51223530Sbloom 				continue;
5136444Swnj 			}
51423530Sbloom 		} else if (local) {
51523530Sbloom 			local = 0;
51667737Spendry 			if (c == '.' || c == deftt.c_cc[VEOF]) {
51723530Sbloom 				echo(c);
51823530Sbloom 				break;
5196444Swnj 			}
52067737Spendry 			if (c == deftt.c_cc[VSUSP] || c == deftt.c_cc[VDSUSP]) {
52123530Sbloom 				bol = 1;
52223530Sbloom 				echo(c);
52323530Sbloom 				stop(c);
52423530Sbloom 				continue;
52523530Sbloom 			}
52645063Sbostic 			if (c != escapechar)
52753033Sleres #ifdef CRYPT
52853033Sleres #ifdef KERBEROS
52953033Sleres 				if (doencrypt)
53058470Sbostic 					(void)des_write(rem,
53158470Sbostic 					    (char *)&escapechar, 1);
53253033Sleres 				else
53353033Sleres #endif
53453033Sleres #endif
53545063Sbostic 					(void)write(rem, &escapechar, 1);
5366444Swnj 		}
53736511Skfall 
53853033Sleres #ifdef CRYPT
53953033Sleres #ifdef KERBEROS
54053033Sleres 		if (doencrypt) {
54153033Sleres 			if (des_write(rem, &c, 1) == 0) {
54253033Sleres 				msg("line gone");
54353033Sleres 				break;
54453033Sleres 			}
54553033Sleres 		} else
54653033Sleres #endif
54753033Sleres #endif
54836511Skfall 			if (write(rem, &c, 1) == 0) {
54940858Sbostic 				msg("line gone");
55036511Skfall 				break;
55136511Skfall 			}
55267737Spendry 		bol = c == deftt.c_cc[VKILL] || c == deftt.c_cc[VEOF] ||
55367737Spendry 		    c == deftt.c_cc[VINTR] || c == deftt.c_cc[VSUSP] ||
55423530Sbloom 		    c == '\r' || c == '\n';
5556444Swnj 	}
5566444Swnj }
5576444Swnj 
55858470Sbostic void
55958470Sbostic #if __STDC__
56058470Sbostic echo(register char c)
56158470Sbostic #else
56223530Sbloom echo(c)
56358470Sbostic 	register char c;
56458470Sbostic #endif
56523530Sbloom {
56640858Sbostic 	register char *p;
56723530Sbloom 	char buf[8];
56823530Sbloom 
56940858Sbostic 	p = buf;
57023530Sbloom 	c &= 0177;
57145063Sbostic 	*p++ = escapechar;
57223530Sbloom 	if (c < ' ') {
57323530Sbloom 		*p++ = '^';
57423530Sbloom 		*p++ = c + '@';
57523530Sbloom 	} else if (c == 0177) {
57623530Sbloom 		*p++ = '^';
57723530Sbloom 		*p++ = '?';
57823530Sbloom 	} else
57923530Sbloom 		*p++ = c;
58023530Sbloom 	*p++ = '\r';
58123530Sbloom 	*p++ = '\n';
58245063Sbostic 	(void)write(STDOUT_FILENO, buf, p - buf);
58323530Sbloom }
58423530Sbloom 
58558470Sbostic void
58658470Sbostic #if __STDC__
58758470Sbostic stop(char cmdc)
58858470Sbostic #else
58918358Ssam stop(cmdc)
59018358Ssam 	char cmdc;
59158470Sbostic #endif
59218358Ssam {
59367737Spendry 	struct sigaction sa;
59467737Spendry 
59518358Ssam 	mode(0);
59667737Spendry 	sigemptyset(&sa.sa_mask);
59767737Spendry 	sa.sa_handler = SIG_IGN;
59867737Spendry 	sa.sa_flags = SA_RESTART;
59967737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
60067737Spendry 	(void)kill(cmdc == deftt.c_cc[VSUSP] ? 0 : getpid(), SIGTSTP);
60167737Spendry 	sa.sa_handler = catch_child;
60267737Spendry 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
60318358Ssam 	mode(1);
60458470Sbostic 	sigwinch(0);			/* check for size changes */
60518358Ssam }
60618358Ssam 
60740858Sbostic void
60858470Sbostic sigwinch(signo)
60958470Sbostic 	int signo;
61018358Ssam {
61118358Ssam 	struct winsize ws;
61218358Ssam 
61329729Smckusick 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
61467737Spendry 	    memcmp(&ws, &winsize, sizeof(ws))) {
61518358Ssam 		winsize = ws;
61624726Smckusick 		sendwindow();
61718358Ssam 	}
61818358Ssam }
61918358Ssam 
62024726Smckusick /*
62124726Smckusick  * Send the window size to the server via the magic escape
62224726Smckusick  */
62358470Sbostic void
62424726Smckusick sendwindow()
62524726Smckusick {
62640858Sbostic 	struct winsize *wp;
62724726Smckusick 	char obuf[4 + sizeof (struct winsize)];
62824726Smckusick 
62940858Sbostic 	wp = (struct winsize *)(obuf+4);
63024726Smckusick 	obuf[0] = 0377;
63124726Smckusick 	obuf[1] = 0377;
63224726Smckusick 	obuf[2] = 's';
63324726Smckusick 	obuf[3] = 's';
63424726Smckusick 	wp->ws_row = htons(winsize.ws_row);
63524726Smckusick 	wp->ws_col = htons(winsize.ws_col);
63624726Smckusick 	wp->ws_xpixel = htons(winsize.ws_xpixel);
63724726Smckusick 	wp->ws_ypixel = htons(winsize.ws_ypixel);
63836511Skfall 
63953033Sleres #ifdef CRYPT
64053033Sleres #ifdef KERBEROS
64153033Sleres 	if(doencrypt)
64253033Sleres 		(void)des_write(rem, obuf, sizeof(obuf));
64353033Sleres 	else
64453033Sleres #endif
64553033Sleres #endif
64640858Sbostic 		(void)write(rem, obuf, sizeof(obuf));
64724726Smckusick }
64824726Smckusick 
64925424Skarels /*
65025424Skarels  * reader: read from remote: line -> 1
65125424Skarels  */
65225424Skarels #define	READING	1
65325424Skarels #define	WRITING	2
65425424Skarels 
65540858Sbostic jmp_buf rcvtop;
65667737Spendry pid_t ppid;
65767737Spendry int rcvcnt, rcvstate;
65840858Sbostic char rcvbuf[8 * 1024];
65925424Skarels 
66040858Sbostic void
66158470Sbostic oob(signo)
66258470Sbostic 	int signo;
6636444Swnj {
66467737Spendry 	struct termios tt;
66540858Sbostic 	int atmark, n, out, rcvd;
6669365Ssam 	char waste[BUFSIZ], mark;
6676444Swnj 
66842230Sbostic 	out = O_RDWR;
66940858Sbostic 	rcvd = 0;
67059175Storek 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
67125424Skarels 		switch (errno) {
67225424Skarels 		case EWOULDBLOCK:
67325424Skarels 			/*
67440858Sbostic 			 * Urgent data not here yet.  It may not be possible
67540858Sbostic 			 * to send it yet if we are blocked for output and
67640858Sbostic 			 * our input buffer is full.
67725424Skarels 			 */
67825424Skarels 			if (rcvcnt < sizeof(rcvbuf)) {
67925424Skarels 				n = read(rem, rcvbuf + rcvcnt,
68040858Sbostic 				    sizeof(rcvbuf) - rcvcnt);
68125424Skarels 				if (n <= 0)
68225424Skarels 					return;
68325424Skarels 				rcvd += n;
68425424Skarels 			} else {
68525424Skarels 				n = read(rem, waste, sizeof(waste));
68625424Skarels 				if (n <= 0)
68725424Skarels 					return;
68825424Skarels 			}
68925424Skarels 			continue;
69025424Skarels 		default:
69125424Skarels 			return;
69259175Storek 		}
6936444Swnj 	}
69425424Skarels 	if (mark & TIOCPKT_WINDOW) {
69540858Sbostic 		/* Let server know about window size changes */
69640858Sbostic 		(void)kill(ppid, SIGUSR1);
69724726Smckusick 	}
69825424Skarels 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
69967737Spendry 		tcgetattr(0, &tt);
700*67749Spendry 		tt.c_iflag &= ~(IXON | IXOFF);
70167737Spendry 		tt.c_cc[VSTOP] = _POSIX_VDISABLE;
70267737Spendry 		tt.c_cc[VSTART] = _POSIX_VDISABLE;
70367737Spendry 		tcsetattr(0, TCSANOW, &tt);
7046444Swnj 	}
70525424Skarels 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
70667737Spendry 		tcgetattr(0, &tt);
70767737Spendry 		tt.c_iflag |= (IXON|IXOFF);
70867737Spendry 		tt.c_cc[VSTOP] = deftt.c_cc[VSTOP];
70967737Spendry 		tt.c_cc[VSTART] = deftt.c_cc[VSTART];
71067737Spendry 		tcsetattr(0, TCSANOW, &tt);
7116444Swnj 	}
71225424Skarels 	if (mark & TIOCPKT_FLUSHWRITE) {
71340858Sbostic 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
71425424Skarels 		for (;;) {
71525424Skarels 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
71667737Spendry 				warn("ioctl SIOCATMARK (ignored)");
71725424Skarels 				break;
71825424Skarels 			}
71925424Skarels 			if (atmark)
72025424Skarels 				break;
72125424Skarels 			n = read(rem, waste, sizeof (waste));
72225424Skarels 			if (n <= 0)
72325424Skarels 				break;
72425424Skarels 		}
72525424Skarels 		/*
72640858Sbostic 		 * Don't want any pending data to be output, so clear the recv
72740858Sbostic 		 * buffer.  If we were hanging on a write when interrupted,
72840858Sbostic 		 * don't want it to restart.  If we were reading, restart
72940858Sbostic 		 * anyway.
73025424Skarels 		 */
73125424Skarels 		rcvcnt = 0;
73225424Skarels 		longjmp(rcvtop, 1);
73325424Skarels 	}
73429729Smckusick 
73540858Sbostic 	/* oob does not do FLUSHREAD (alas!) */
73629729Smckusick 
73729729Smckusick 	/*
73840858Sbostic 	 * If we filled the receive buffer while a read was pending, longjmp
73940858Sbostic 	 * to the top to restart appropriately.  Don't abort a pending write,
74040858Sbostic 	 * however, or we won't know how much was written.
74125424Skarels 	 */
74225424Skarels 	if (rcvd && rcvstate == READING)
74325424Skarels 		longjmp(rcvtop, 1);
7446444Swnj }
7456444Swnj 
74640858Sbostic /* reader: read from remote: line -> 1 */
74758470Sbostic int
74867737Spendry reader(smask)
74967737Spendry 	sigset_t *smask;
7506444Swnj {
75167737Spendry 	pid_t pid;
75267737Spendry 	int n, remaining;
75359175Storek 	char *bufp;
75467737Spendry 	struct sigaction sa;
75540858Sbostic 
75659175Storek #if BSD >= 43 || defined(SUNOS4)
75759175Storek 	pid = getpid();		/* modern systems use positives for pid */
75826981Skarels #else
75959175Storek 	pid = -getpid();	/* old broken systems use negatives */
76026981Skarels #endif
76167737Spendry 	sigemptyset(&sa.sa_mask);
76267737Spendry 	sa.sa_flags = SA_RESTART;
76367737Spendry 	sa.sa_handler = SIG_IGN;
76467737Spendry 	(void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
76567737Spendry 	sa.sa_handler = oob;
76667737Spendry 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
76726981Skarels 	ppid = getppid();
76840858Sbostic 	(void)fcntl(rem, F_SETOWN, pid);
76940858Sbostic 	(void)setjmp(rcvtop);
77067737Spendry 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
77159175Storek 	bufp = rcvbuf;
7726444Swnj 	for (;;) {
77325424Skarels 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
77425424Skarels 			rcvstate = WRITING;
77545063Sbostic 			n = write(STDOUT_FILENO, bufp, remaining);
77625424Skarels 			if (n < 0) {
77725424Skarels 				if (errno != EINTR)
77858470Sbostic 					return (-1);
77925424Skarels 				continue;
78025424Skarels 			}
78125424Skarels 			bufp += n;
78225424Skarels 		}
78325424Skarels 		bufp = rcvbuf;
78425424Skarels 		rcvcnt = 0;
78525424Skarels 		rcvstate = READING;
78636511Skfall 
78753033Sleres #ifdef CRYPT
78853033Sleres #ifdef KERBEROS
78953033Sleres 		if (doencrypt)
79053033Sleres 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
79153033Sleres 		else
79253033Sleres #endif
79353033Sleres #endif
79436511Skfall 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
79525424Skarels 		if (rcvcnt == 0)
79625424Skarels 			return (0);
79725424Skarels 		if (rcvcnt < 0) {
7989365Ssam 			if (errno == EINTR)
7996444Swnj 				continue;
80067737Spendry 			warn("read");
80158470Sbostic 			return (-1);
8026444Swnj 		}
8036444Swnj 	}
8046444Swnj }
8056444Swnj 
80658470Sbostic void
8076444Swnj mode(f)
80858470Sbostic 	int f;
8096444Swnj {
81067737Spendry 	struct termios tt;
8119365Ssam 
81267737Spendry 	switch (f) {
8139962Ssam 	case 0:
81467737Spendry 		tcsetattr(0, TCSADRAIN, &deftt);
8159962Ssam 		break;
8169962Ssam 	case 1:
81767737Spendry 		tt = deftt;
818*67749Spendry 		tt.c_oflag &= ~(OPOST);
81967737Spendry 		tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
820*67749Spendry 		tt.c_iflag &= ~(ICRNL);
82167737Spendry 		tt.c_cc[VMIN] = 1;
82267737Spendry 		tt.c_cc[VTIME] = 0;
82367737Spendry 		if (eight) {
824*67749Spendry 			tt.c_iflag &= ~(IXON | IXOFF | ISTRIP);
82567737Spendry 			tt.c_cc[VSTOP] = _POSIX_VDISABLE;
82667737Spendry 			tt.c_cc[VSTART] = _POSIX_VDISABLE;
82767737Spendry 		}
82867737Spendry 		/*if (litout)
82967737Spendry 			lflags |= LLITOUT;*/
83067737Spendry 		tcsetattr(0, TCSADRAIN, &tt);
8319962Ssam 		break;
83267737Spendry 
8339962Ssam 	default:
8349962Ssam 		return;
8356444Swnj 	}
8366444Swnj }
8376444Swnj 
83840858Sbostic void
83958470Sbostic lostpeer(signo)
84058470Sbostic 	int signo;
8416444Swnj {
84267737Spendry 	struct sigaction sa;
84367737Spendry 
84467737Spendry 	sigemptyset(&sa.sa_mask);
84567737Spendry 	sa.sa_flags = SA_RESTART;
84667737Spendry 	sa.sa_handler = SIG_IGN;
84767737Spendry 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
84840858Sbostic 	msg("\007connection closed.");
84940858Sbostic 	done(1);
85040858Sbostic }
85129729Smckusick 
85240858Sbostic /* copy SIGURGs to the child process. */
85340858Sbostic void
85458470Sbostic copytochild(signo)
85558470Sbostic 	int signo;
85640858Sbostic {
85767737Spendry 
85840858Sbostic 	(void)kill(child, SIGURG);
8596444Swnj }
8606444Swnj 
86158470Sbostic void
86240858Sbostic msg(str)
86340858Sbostic 	char *str;
8646444Swnj {
86567737Spendry 
86640858Sbostic 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
86740858Sbostic }
86829729Smckusick 
86940858Sbostic #ifdef KERBEROS
87040858Sbostic /* VARARGS */
87158470Sbostic void
87258470Sbostic #if __STDC__
87358470Sbostic warning(const char *fmt, ...)
87458470Sbostic #else
87558470Sbostic warning(fmt, va_alist)
87658470Sbostic 	char *fmt;
87758470Sbostic 	va_dcl
87858470Sbostic #endif
87940858Sbostic {
88040858Sbostic 	va_list ap;
88140858Sbostic 
88240858Sbostic 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
88358470Sbostic #ifdef __STDC__
88458470Sbostic 	va_start(ap, fmt);
88558470Sbostic #else
88640858Sbostic 	va_start(ap);
88758470Sbostic #endif
88840858Sbostic 	vfprintf(stderr, fmt, ap);
88940858Sbostic 	va_end(ap);
89040858Sbostic 	(void)fprintf(stderr, ".\n");
8916444Swnj }
89240858Sbostic #endif
89336512Skfall 
89458470Sbostic __dead void
89540858Sbostic usage()
89636512Skfall {
89740858Sbostic 	(void)fprintf(stderr,
89840858Sbostic 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
89940858Sbostic #ifdef KERBEROS
90053033Sleres #ifdef CRYPT
90154125Sbostic 	    "8EKLx", " [-k realm] ");
90253033Sleres #else
90354125Sbostic 	    "8EKL", " [-k realm] ");
90453033Sleres #endif
90545256Smckusick #else
90645063Sbostic 	    "8EL", " ");
90740858Sbostic #endif
90840858Sbostic 	exit(1);
90936512Skfall }
91040858Sbostic 
91140858Sbostic /*
91259175Storek  * The following routine provides compatibility (such as it is) between older
91340858Sbostic  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
91440858Sbostic  */
91559175Storek #ifdef OLDSUN
91658470Sbostic int
91740858Sbostic get_window_size(fd, wp)
91840858Sbostic 	int fd;
91940858Sbostic 	struct winsize *wp;
92040858Sbostic {
92140858Sbostic 	struct ttysize ts;
92240858Sbostic 	int error;
92340858Sbostic 
92440858Sbostic 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
92558470Sbostic 		return (error);
92640858Sbostic 	wp->ws_row = ts.ts_lines;
92740858Sbostic 	wp->ws_col = ts.ts_cols;
92840858Sbostic 	wp->ws_xpixel = 0;
92940858Sbostic 	wp->ws_ypixel = 0;
93058470Sbostic 	return (0);
93140858Sbostic }
93240858Sbostic #endif
93345063Sbostic 
93458470Sbostic u_int
93545063Sbostic getescape(p)
93645063Sbostic 	register char *p;
93745063Sbostic {
93845063Sbostic 	long val;
93945063Sbostic 	int len;
94045063Sbostic 
94145063Sbostic 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
94258470Sbostic 		return ((u_int)*p);
94345063Sbostic 					/* otherwise, \nnn */
94445063Sbostic 	if (*p == '\\' && len >= 2 && len <= 4) {
94558470Sbostic 		val = strtol(++p, NULL, 8);
94645063Sbostic 		for (;;) {
94745063Sbostic 			if (!*++p)
94858470Sbostic 				return ((u_int)val);
94945063Sbostic 			if (*p < '0' || *p > '8')
95045063Sbostic 				break;
95145063Sbostic 		}
95245063Sbostic 	}
95345063Sbostic 	msg("illegal option value -- e");
95445063Sbostic 	usage();
95545063Sbostic 	/* NOTREACHED */
95645063Sbostic }
957