xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 58470)
144346Skarels /*
240858Sbostic  * Copyright (c) 1983, 1990 The Regents of the University of California.
335539Sbostic  * All rights reserved.
435539Sbostic  *
542763Sbostic  * %sccs.include.redist.c%
621595Sdist  */
721595Sdist 
86444Swnj #ifndef lint
921595Sdist char copyright[] =
1040858Sbostic "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
1121595Sdist  All rights reserved.\n";
1235539Sbostic #endif /* not lint */
136444Swnj 
1421595Sdist #ifndef lint
15*58470Sbostic static char sccsid[] = "@(#)rlogin.c	5.38 (Berkeley) 03/04/93";
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>
269365Ssam 
279207Ssam #include <netinet/in.h>
2844346Skarels #include <netinet/in_systm.h>
2944346Skarels #include <netinet/ip.h>
309365Ssam 
316444Swnj #include <errno.h>
32*58470Sbostic #include <fcntl.h>
33*58470Sbostic #include <netdb.h>
346444Swnj #include <pwd.h>
35*58470Sbostic #include <setjmp.h>
36*58470Sbostic #include <sgtty.h>
37*58470Sbostic #include <signal.h>
38*58470Sbostic #include <stdarg.h>
3940858Sbostic #include <stdio.h>
40*58470Sbostic #include <stdlib.h>
41*58470Sbostic #include <string.h>
4240858Sbostic #include <unistd.h>
436444Swnj 
4440858Sbostic #ifdef KERBEROS
4541760Skfall #include <kerberosIV/des.h>
4640683Sbostic #include <kerberosIV/krb.h>
4736511Skfall 
48*58470Sbostic #include "krb.h"
49*58470Sbostic 
5040858Sbostic CREDENTIALS cred;
5140858Sbostic Key_schedule schedule;
5246850Sbostic int use_kerberos = 1, doencrypt;
5340858Sbostic char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
5440858Sbostic #endif
5524726Smckusick 
5640858Sbostic #ifndef TIOCPKT_WINDOW
5740858Sbostic #define	TIOCPKT_WINDOW	0x80
5840858Sbostic #endif
5929729Smckusick 
6040858Sbostic /* concession to Sun */
6140858Sbostic #ifndef SIGUSR1
6240858Sbostic #define	SIGUSR1	30
6326981Skarels #endif
6440858Sbostic 
6540858Sbostic int eight, litout, rem;
6645063Sbostic 
6745063Sbostic int noescape;
6845063Sbostic u_char escapechar = '~';
6945063Sbostic 
7040858Sbostic char *speeds[] = {
7140858Sbostic 	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
7240858Sbostic 	"1800", "2400", "4800", "9600", "19200", "38400"
7340858Sbostic };
7440858Sbostic 
75*58470Sbostic #ifdef SUNOS4
7626981Skarels struct winsize {
7726981Skarels 	unsigned short ws_row, ws_col;
7826981Skarels 	unsigned short ws_xpixel, ws_ypixel;
7926981Skarels };
80*58470Sbostic #else
81*58470Sbostic #define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
8240858Sbostic #endif
8318358Ssam struct	winsize winsize;
846444Swnj 
85*58470Sbostic void		catch_child __P((int));
86*58470Sbostic void		copytochild __P((int));
87*58470Sbostic __dead void	doit __P((long));
88*58470Sbostic __dead void	done __P((int));
89*58470Sbostic void		echo __P((char));
90*58470Sbostic u_int		getescape __P((char *));
91*58470Sbostic void		lostpeer __P((int));
92*58470Sbostic void		mode __P((int));
93*58470Sbostic void		msg __P((char *));
94*58470Sbostic void		oob __P((int));
95*58470Sbostic int		reader __P((int));
96*58470Sbostic void		sendwindow __P((void));
97*58470Sbostic void		setsignal __P((int));
98*58470Sbostic void		sigwinch __P((int));
99*58470Sbostic void		stop __P((char));
100*58470Sbostic __dead void	usage __P((void));
101*58470Sbostic void		writer __P((void));
102*58470Sbostic void		writeroob __P((int));
103*58470Sbostic 
104*58470Sbostic #ifdef	KERBEROS
105*58470Sbostic void		warning __P((const char *, ...));
10640858Sbostic #endif
107*58470Sbostic #ifdef SUNOS4
108*58470Sbostic int		get_window_size __P((int, struct winsize *));
109*58470Sbostic #endif
11029729Smckusick 
111*58470Sbostic int
1126444Swnj main(argc, argv)
1136444Swnj 	int argc;
114*58470Sbostic 	char *argv[];
1156444Swnj {
11640858Sbostic 	extern char *optarg;
11740858Sbostic 	extern int optind;
11840858Sbostic 	struct passwd *pw;
11940858Sbostic 	struct servent *sp;
1206444Swnj 	struct sgttyb ttyb;
12140858Sbostic 	long omask;
12240858Sbostic 	int argoff, ch, dflag, one, uid;
12340858Sbostic 	char *host, *p, *user, term[1024];
1246444Swnj 
12540858Sbostic 	argoff = dflag = 0;
12640858Sbostic 	one = 1;
12740858Sbostic 	host = user = NULL;
12840858Sbostic 
12940858Sbostic 	if (p = rindex(argv[0], '/'))
13040858Sbostic 		++p;
1316444Swnj 	else
13240858Sbostic 		p = argv[0];
13340858Sbostic 
13440858Sbostic 	if (strcmp(p, "rlogin"))
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 
20340858Sbostic 	if (!(pw = getpwuid(uid = getuid()))) {
20440858Sbostic 		(void)fprintf(stderr, "rlogin: unknown user id.\n");
2056444Swnj 		exit(1);
2066444Swnj 	}
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");
22340858Sbostic 	if (sp == NULL) {
22440858Sbostic 		(void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
22540858Sbostic 		exit(1);
2269365Ssam 	}
22740858Sbostic 
22840858Sbostic 	(void)strcpy(term, (p = getenv("TERM")) ? p : "network");
22918358Ssam 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
23040858Sbostic 		(void)strcat(term, "/");
231*58470Sbostic 		(void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
2326444Swnj 	}
23340858Sbostic 
23440858Sbostic 	(void)get_window_size(0, &winsize);
23540858Sbostic 
23640858Sbostic 	(void)signal(SIGPIPE, lostpeer);
23729729Smckusick 	/* will use SIGUSR1 for window size hack, so hold it off */
23840858Sbostic 	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
23958261Smckusick 	/*
24058261Smckusick 	 * We set SIGURG and SIGUSR1 below so that an
24158261Smckusick 	 * incoming signal will be held pending rather than being
24258261Smckusick 	 * discarded. Note that these routines will be ready to get
24358261Smckusick 	 * a signal by the time that they are unblocked below.
24458261Smckusick 	 */
24558261Smckusick 	(void)signal(SIGURG, copytochild);
24658261Smckusick 	(void)signal(SIGUSR1, writeroob);
24736511Skfall 
24840858Sbostic #ifdef KERBEROS
24936512Skfall try_connect:
25040858Sbostic 	if (use_kerberos) {
251*58470Sbostic 		struct hostent *hp;
252*58470Sbostic 
253*58470Sbostic 		/* Fully qualify hostname (needed for krb_realmofhost). */
25456960Seric 		hp = gethostbyname(host);
25556960Seric 		if (hp != NULL && !(host = strdup(hp->h_name))) {
256*58470Sbostic 			(void)fprintf(stderr, "rlogin: %s\n",
257*58470Sbostic 			    strerror(ENOMEM));
25856960Seric 			exit(1);
25956960Seric 		}
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");
27740858Sbostic 			if (sp == NULL) {
27840858Sbostic 				(void)fprintf(stderr,
27940858Sbostic 				    "rlogin: unknown service login/tcp.\n");
28036628Skfall 				exit(1);
28136628Skfall 			}
28238728Skfall 			if (errno == ECONNREFUSED)
28340858Sbostic 				warning("remote host doesn't support Kerberos");
28438728Skfall 			if (errno == ENOENT)
28540858Sbostic 				warning("can't provide Kerberos auth data");
28636512Skfall 			goto try_connect;
28736512Skfall 		}
28836511Skfall 	} else {
28953033Sleres #ifdef CRYPT
29053033Sleres 		if (doencrypt) {
29153033Sleres 			(void)fprintf(stderr,
29253033Sleres 			    "rlogin: the -x flag requires Kerberos authentication.\n");
29353033Sleres 			exit(1);
29453033Sleres 		}
29553033Sleres #endif /* CRYPT */
29640858Sbostic 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
29736511Skfall 	}
29836512Skfall #else
29940858Sbostic 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
30045256Smckusick #endif /* KERBEROS */
30136511Skfall 
30240858Sbostic 	if (rem < 0)
30336511Skfall 		exit(1);
30436511Skfall 
30540858Sbostic 	if (dflag &&
30640858Sbostic 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
30740858Sbostic 		(void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
30840858Sbostic 		    strerror(errno));
30944346Skarels 	one = IPTOS_LOWDELAY;
31044346Skarels 	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
31144346Skarels 		perror("rlogin: setsockopt TOS (ignored)");
31240858Sbostic 
31340858Sbostic 	(void)setuid(uid);
31440858Sbostic 	doit(omask);
3159365Ssam 	/*NOTREACHED*/
3166444Swnj }
3176444Swnj 
31840858Sbostic int child, defflags, deflflags, tabflag;
31940858Sbostic char deferase, defkill;
32040858Sbostic struct tchars deftc;
32140858Sbostic struct ltchars defltc;
32240858Sbostic struct tchars notc = { -1, -1, -1, -1, -1, -1 };
32340858Sbostic struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
3246444Swnj 
325*58470Sbostic void
32640858Sbostic doit(omask)
32740858Sbostic 	long omask;
3286444Swnj {
32913075Ssam 	struct sgttyb sb;
3306444Swnj 
33140858Sbostic 	(void)ioctl(0, TIOCGETP, (char *)&sb);
33213075Ssam 	defflags = sb.sg_flags;
33312155Ssam 	tabflag = defflags & TBDELAY;
3349962Ssam 	defflags &= ECHO | CRMOD;
33513075Ssam 	deferase = sb.sg_erase;
33613075Ssam 	defkill = sb.sg_kill;
337*58470Sbostic 	(void)ioctl(0, TIOCLGET, &deflflags);
338*58470Sbostic 	(void)ioctl(0, TIOCGETC, &deftc);
33913075Ssam 	notc.t_startc = deftc.t_startc;
34013075Ssam 	notc.t_stopc = deftc.t_stopc;
341*58470Sbostic 	(void)ioctl(0, TIOCGLTC, &defltc);
34240858Sbostic 	(void)signal(SIGINT, SIG_IGN);
343*58470Sbostic 	setsignal(SIGHUP);
344*58470Sbostic 	setsignal(SIGQUIT);
3459365Ssam 	child = fork();
3469365Ssam 	if (child == -1) {
34740858Sbostic 		(void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
34825424Skarels 		done(1);
3499365Ssam 	}
3509365Ssam 	if (child == 0) {
35124726Smckusick 		mode(1);
35240858Sbostic 		if (reader(omask) == 0) {
35340858Sbostic 			msg("connection closed.");
35425424Skarels 			exit(0);
35525424Skarels 		}
35612155Ssam 		sleep(1);
35740858Sbostic 		msg("\007connection closed.");
35840858Sbostic 		exit(1);
3596444Swnj 	}
36029729Smckusick 
36129729Smckusick 	/*
36240858Sbostic 	 * We may still own the socket, and may have a pending SIGURG (or might
36358261Smckusick 	 * receive one soon) that we really want to send to the reader.  When
36458261Smckusick 	 * one of these comes in, the trap copytochild simply copies such
36558261Smckusick 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
36658261Smckusick 	 * that were set above.
36729729Smckusick 	 */
36840858Sbostic 	(void)sigsetmask(omask);
36940858Sbostic 	(void)signal(SIGCHLD, catch_child);
3709365Ssam 	writer();
37140858Sbostic 	msg("closed connection.");
37225424Skarels 	done(0);
3736444Swnj }
3746444Swnj 
37540858Sbostic /* trap a signal, unless it is being ignored. */
376*58470Sbostic void
377*58470Sbostic setsignal(sig)
37840858Sbostic 	int sig;
37929729Smckusick {
38029729Smckusick 	int omask = sigblock(sigmask(sig));
38129729Smckusick 
382*58470Sbostic 	if (signal(sig, exit) == SIG_IGN)
38340858Sbostic 		(void)signal(sig, SIG_IGN);
38440858Sbostic 	(void)sigsetmask(omask);
38529729Smckusick }
38629729Smckusick 
387*58470Sbostic __dead void
38825424Skarels done(status)
38925424Skarels 	int status;
3906444Swnj {
39146850Sbostic 	int w, wstatus;
3926444Swnj 
3936444Swnj 	mode(0);
39429729Smckusick 	if (child > 0) {
39540858Sbostic 		/* make sure catch_child does not snap it up */
39640858Sbostic 		(void)signal(SIGCHLD, SIG_DFL);
39729729Smckusick 		if (kill(child, SIGKILL) >= 0)
39846850Sbostic 			while ((w = wait(&wstatus)) > 0 && w != child);
39929729Smckusick 	}
40025424Skarels 	exit(status);
4016444Swnj }
4026444Swnj 
40340858Sbostic int dosigwinch;
40429729Smckusick 
40529729Smckusick /*
40624726Smckusick  * This is called when the reader process gets the out-of-band (urgent)
40724726Smckusick  * request to turn on the window-changing protocol.
40824726Smckusick  */
40940858Sbostic void
410*58470Sbostic writeroob(signo)
411*58470Sbostic 	int signo;
41224726Smckusick {
41325341Smckusick 	if (dosigwinch == 0) {
41424919Smckusick 		sendwindow();
41540858Sbostic 		(void)signal(SIGWINCH, sigwinch);
41625341Smckusick 	}
41724726Smckusick 	dosigwinch = 1;
41824726Smckusick }
41924726Smckusick 
42040858Sbostic void
421*58470Sbostic catch_child(signo)
422*58470Sbostic 	int signo;
42311803Sedward {
42411803Sedward 	union wait status;
42511803Sedward 	int pid;
42611803Sedward 
42740858Sbostic 	for (;;) {
42854125Sbostic 		pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
42940858Sbostic 		if (pid == 0)
43040858Sbostic 			return;
43140858Sbostic 		/* if the child (reader) dies, just quit */
432*58470Sbostic 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
43340858Sbostic 			done((int)(status.w_termsig | status.w_retcode));
43440858Sbostic 	}
43540858Sbostic 	/* NOTREACHED */
43611803Sedward }
43711803Sedward 
4386444Swnj /*
4399365Ssam  * writer: write to remote: 0 -> line.
44045063Sbostic  * ~.				terminate
44145063Sbostic  * ~^Z				suspend rlogin process.
44245063Sbostic  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
4436444Swnj  */
444*58470Sbostic void
4459365Ssam writer()
4466444Swnj {
44745063Sbostic 	register int bol, local, n;
44823530Sbloom 	char c;
4496444Swnj 
45040858Sbostic 	bol = 1;			/* beginning of line */
45140858Sbostic 	local = 0;
45211803Sedward 	for (;;) {
45340858Sbostic 		n = read(STDIN_FILENO, &c, 1);
45418358Ssam 		if (n <= 0) {
45518358Ssam 			if (n < 0 && errno == EINTR)
45618358Ssam 				continue;
45711803Sedward 			break;
45818358Ssam 		}
4599365Ssam 		/*
46040858Sbostic 		 * If we're at the beginning of the line and recognize a
46140858Sbostic 		 * command character, then we echo locally.  Otherwise,
46240858Sbostic 		 * characters are echo'd remotely.  If the command character
46340858Sbostic 		 * is doubled, this acts as a force and local echo is
46440858Sbostic 		 * suppressed.
4659365Ssam 		 */
46623530Sbloom 		if (bol) {
46723530Sbloom 			bol = 0;
46845063Sbostic 			if (!noescape && c == escapechar) {
46923530Sbloom 				local = 1;
47023530Sbloom 				continue;
4716444Swnj 			}
47223530Sbloom 		} else if (local) {
47323530Sbloom 			local = 0;
47423530Sbloom 			if (c == '.' || c == deftc.t_eofc) {
47523530Sbloom 				echo(c);
47623530Sbloom 				break;
4776444Swnj 			}
47823530Sbloom 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
47923530Sbloom 				bol = 1;
48023530Sbloom 				echo(c);
48123530Sbloom 				stop(c);
48223530Sbloom 				continue;
48323530Sbloom 			}
48445063Sbostic 			if (c != escapechar)
48553033Sleres #ifdef CRYPT
48653033Sleres #ifdef KERBEROS
48753033Sleres 				if (doencrypt)
488*58470Sbostic 					(void)des_write(rem,
489*58470Sbostic 					    (char *)&escapechar, 1);
49053033Sleres 				else
49153033Sleres #endif
49253033Sleres #endif
49345063Sbostic 					(void)write(rem, &escapechar, 1);
4946444Swnj 		}
49536511Skfall 
49653033Sleres #ifdef CRYPT
49753033Sleres #ifdef KERBEROS
49853033Sleres 		if (doencrypt) {
49953033Sleres 			if (des_write(rem, &c, 1) == 0) {
50053033Sleres 				msg("line gone");
50153033Sleres 				break;
50253033Sleres 			}
50353033Sleres 		} else
50453033Sleres #endif
50553033Sleres #endif
50636511Skfall 			if (write(rem, &c, 1) == 0) {
50740858Sbostic 				msg("line gone");
50836511Skfall 				break;
50936511Skfall 			}
51023530Sbloom 		bol = c == defkill || c == deftc.t_eofc ||
51125424Skarels 		    c == deftc.t_intrc || c == defltc.t_suspc ||
51223530Sbloom 		    c == '\r' || c == '\n';
5136444Swnj 	}
5146444Swnj }
5156444Swnj 
516*58470Sbostic void
517*58470Sbostic #if __STDC__
518*58470Sbostic echo(register char c)
519*58470Sbostic #else
52023530Sbloom echo(c)
521*58470Sbostic 	register char c;
522*58470Sbostic #endif
52323530Sbloom {
52440858Sbostic 	register char *p;
52523530Sbloom 	char buf[8];
52623530Sbloom 
52740858Sbostic 	p = buf;
52823530Sbloom 	c &= 0177;
52945063Sbostic 	*p++ = escapechar;
53023530Sbloom 	if (c < ' ') {
53123530Sbloom 		*p++ = '^';
53223530Sbloom 		*p++ = c + '@';
53323530Sbloom 	} else if (c == 0177) {
53423530Sbloom 		*p++ = '^';
53523530Sbloom 		*p++ = '?';
53623530Sbloom 	} else
53723530Sbloom 		*p++ = c;
53823530Sbloom 	*p++ = '\r';
53923530Sbloom 	*p++ = '\n';
54045063Sbostic 	(void)write(STDOUT_FILENO, buf, p - buf);
54123530Sbloom }
54223530Sbloom 
543*58470Sbostic void
544*58470Sbostic #if __STDC__
545*58470Sbostic stop(char cmdc)
546*58470Sbostic #else
54718358Ssam stop(cmdc)
54818358Ssam 	char cmdc;
549*58470Sbostic #endif
55018358Ssam {
55118358Ssam 	mode(0);
55240858Sbostic 	(void)signal(SIGCHLD, SIG_IGN);
55340858Sbostic 	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
55440858Sbostic 	(void)signal(SIGCHLD, catch_child);
55518358Ssam 	mode(1);
556*58470Sbostic 	sigwinch(0);			/* check for size changes */
55718358Ssam }
55818358Ssam 
55940858Sbostic void
560*58470Sbostic sigwinch(signo)
561*58470Sbostic 	int signo;
56218358Ssam {
56318358Ssam 	struct winsize ws;
56418358Ssam 
56529729Smckusick 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
56640858Sbostic 	    bcmp(&ws, &winsize, sizeof(ws))) {
56718358Ssam 		winsize = ws;
56824726Smckusick 		sendwindow();
56918358Ssam 	}
57018358Ssam }
57118358Ssam 
57224726Smckusick /*
57324726Smckusick  * Send the window size to the server via the magic escape
57424726Smckusick  */
575*58470Sbostic void
57624726Smckusick sendwindow()
57724726Smckusick {
57840858Sbostic 	struct winsize *wp;
57924726Smckusick 	char obuf[4 + sizeof (struct winsize)];
58024726Smckusick 
58140858Sbostic 	wp = (struct winsize *)(obuf+4);
58224726Smckusick 	obuf[0] = 0377;
58324726Smckusick 	obuf[1] = 0377;
58424726Smckusick 	obuf[2] = 's';
58524726Smckusick 	obuf[3] = 's';
58624726Smckusick 	wp->ws_row = htons(winsize.ws_row);
58724726Smckusick 	wp->ws_col = htons(winsize.ws_col);
58824726Smckusick 	wp->ws_xpixel = htons(winsize.ws_xpixel);
58924726Smckusick 	wp->ws_ypixel = htons(winsize.ws_ypixel);
59036511Skfall 
59153033Sleres #ifdef CRYPT
59253033Sleres #ifdef KERBEROS
59353033Sleres 	if(doencrypt)
59453033Sleres 		(void)des_write(rem, obuf, sizeof(obuf));
59553033Sleres 	else
59653033Sleres #endif
59753033Sleres #endif
59840858Sbostic 		(void)write(rem, obuf, sizeof(obuf));
59924726Smckusick }
60024726Smckusick 
60125424Skarels /*
60225424Skarels  * reader: read from remote: line -> 1
60325424Skarels  */
60425424Skarels #define	READING	1
60525424Skarels #define	WRITING	2
60625424Skarels 
60740858Sbostic jmp_buf rcvtop;
60840858Sbostic int ppid, rcvcnt, rcvstate;
60940858Sbostic char rcvbuf[8 * 1024];
61025424Skarels 
61140858Sbostic void
612*58470Sbostic oob(signo)
613*58470Sbostic 	int signo;
6146444Swnj {
61540858Sbostic 	struct sgttyb sb;
61640858Sbostic 	int atmark, n, out, rcvd;
6179365Ssam 	char waste[BUFSIZ], mark;
6186444Swnj 
61942230Sbostic 	out = O_RDWR;
62040858Sbostic 	rcvd = 0;
62125424Skarels 	while (recv(rem, &mark, 1, MSG_OOB) < 0)
62225424Skarels 		switch (errno) {
62325424Skarels 		case EWOULDBLOCK:
62425424Skarels 			/*
62540858Sbostic 			 * Urgent data not here yet.  It may not be possible
62640858Sbostic 			 * to send it yet if we are blocked for output and
62740858Sbostic 			 * our input buffer is full.
62825424Skarels 			 */
62925424Skarels 			if (rcvcnt < sizeof(rcvbuf)) {
63025424Skarels 				n = read(rem, rcvbuf + rcvcnt,
63140858Sbostic 				    sizeof(rcvbuf) - rcvcnt);
63225424Skarels 				if (n <= 0)
63325424Skarels 					return;
63425424Skarels 				rcvd += n;
63525424Skarels 			} else {
63625424Skarels 				n = read(rem, waste, sizeof(waste));
63725424Skarels 				if (n <= 0)
63825424Skarels 					return;
63925424Skarels 			}
64025424Skarels 			continue;
64125424Skarels 		default:
64225424Skarels 			return;
6436444Swnj 	}
64425424Skarels 	if (mark & TIOCPKT_WINDOW) {
64540858Sbostic 		/* Let server know about window size changes */
64640858Sbostic 		(void)kill(ppid, SIGUSR1);
64724726Smckusick 	}
64825424Skarels 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
64940858Sbostic 		(void)ioctl(0, TIOCGETP, (char *)&sb);
65024726Smckusick 		sb.sg_flags &= ~CBREAK;
65124726Smckusick 		sb.sg_flags |= RAW;
65240858Sbostic 		(void)ioctl(0, TIOCSETN, (char *)&sb);
65313075Ssam 		notc.t_stopc = -1;
65413075Ssam 		notc.t_startc = -1;
65540858Sbostic 		(void)ioctl(0, TIOCSETC, (char *)&notc);
6566444Swnj 	}
65725424Skarels 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
65840858Sbostic 		(void)ioctl(0, TIOCGETP, (char *)&sb);
65924726Smckusick 		sb.sg_flags &= ~RAW;
66024726Smckusick 		sb.sg_flags |= CBREAK;
66140858Sbostic 		(void)ioctl(0, TIOCSETN, (char *)&sb);
66213075Ssam 		notc.t_stopc = deftc.t_stopc;
66313075Ssam 		notc.t_startc = deftc.t_startc;
66440858Sbostic 		(void)ioctl(0, TIOCSETC, (char *)&notc);
6656444Swnj 	}
66625424Skarels 	if (mark & TIOCPKT_FLUSHWRITE) {
66740858Sbostic 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
66825424Skarels 		for (;;) {
66925424Skarels 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
67040858Sbostic 				(void)fprintf(stderr, "rlogin: ioctl: %s.\n",
67140858Sbostic 				    strerror(errno));
67225424Skarels 				break;
67325424Skarels 			}
67425424Skarels 			if (atmark)
67525424Skarels 				break;
67625424Skarels 			n = read(rem, waste, sizeof (waste));
67725424Skarels 			if (n <= 0)
67825424Skarels 				break;
67925424Skarels 		}
68025424Skarels 		/*
68140858Sbostic 		 * Don't want any pending data to be output, so clear the recv
68240858Sbostic 		 * buffer.  If we were hanging on a write when interrupted,
68340858Sbostic 		 * don't want it to restart.  If we were reading, restart
68440858Sbostic 		 * anyway.
68525424Skarels 		 */
68625424Skarels 		rcvcnt = 0;
68725424Skarels 		longjmp(rcvtop, 1);
68825424Skarels 	}
68929729Smckusick 
69040858Sbostic 	/* oob does not do FLUSHREAD (alas!) */
69129729Smckusick 
69229729Smckusick 	/*
69340858Sbostic 	 * If we filled the receive buffer while a read was pending, longjmp
69440858Sbostic 	 * to the top to restart appropriately.  Don't abort a pending write,
69540858Sbostic 	 * however, or we won't know how much was written.
69625424Skarels 	 */
69725424Skarels 	if (rcvd && rcvstate == READING)
69825424Skarels 		longjmp(rcvtop, 1);
6996444Swnj }
7006444Swnj 
70140858Sbostic /* reader: read from remote: line -> 1 */
702*58470Sbostic int
70340858Sbostic reader(omask)
70440858Sbostic 	int omask;
7056444Swnj {
70640858Sbostic 
70726981Skarels #if !defined(BSD) || BSD < 43
70826981Skarels 	int pid = -getpid();
70926981Skarels #else
71025424Skarels 	int pid = getpid();
71126981Skarels #endif
71225424Skarels 	int n, remaining;
71325424Skarels 	char *bufp = rcvbuf;
7146444Swnj 
71540858Sbostic 	(void)signal(SIGTTOU, SIG_IGN);
71640858Sbostic 	(void)signal(SIGURG, oob);
71726981Skarels 	ppid = getppid();
71840858Sbostic 	(void)fcntl(rem, F_SETOWN, pid);
71940858Sbostic 	(void)setjmp(rcvtop);
72040858Sbostic 	(void)sigsetmask(omask);
7216444Swnj 	for (;;) {
72225424Skarels 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
72325424Skarels 			rcvstate = WRITING;
72445063Sbostic 			n = write(STDOUT_FILENO, bufp, remaining);
72525424Skarels 			if (n < 0) {
72625424Skarels 				if (errno != EINTR)
727*58470Sbostic 					return (-1);
72825424Skarels 				continue;
72925424Skarels 			}
73025424Skarels 			bufp += n;
73125424Skarels 		}
73225424Skarels 		bufp = rcvbuf;
73325424Skarels 		rcvcnt = 0;
73425424Skarels 		rcvstate = READING;
73536511Skfall 
73653033Sleres #ifdef CRYPT
73753033Sleres #ifdef KERBEROS
73853033Sleres 		if (doencrypt)
73953033Sleres 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
74053033Sleres 		else
74153033Sleres #endif
74253033Sleres #endif
74336511Skfall 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
74425424Skarels 		if (rcvcnt == 0)
74525424Skarels 			return (0);
74625424Skarels 		if (rcvcnt < 0) {
7479365Ssam 			if (errno == EINTR)
7486444Swnj 				continue;
74940858Sbostic 			(void)fprintf(stderr, "rlogin: read: %s.\n",
75040858Sbostic 			    strerror(errno));
751*58470Sbostic 			return (-1);
7526444Swnj 		}
7536444Swnj 	}
7546444Swnj }
7556444Swnj 
756*58470Sbostic void
7576444Swnj mode(f)
758*58470Sbostic 	int f;
7596444Swnj {
76013075Ssam 	struct ltchars *ltc;
76113075Ssam 	struct sgttyb sb;
76240858Sbostic 	struct tchars *tc;
76340858Sbostic 	int lflags;
7649365Ssam 
76540858Sbostic 	(void)ioctl(0, TIOCGETP, (char *)&sb);
76640858Sbostic 	(void)ioctl(0, TIOCLGET, (char *)&lflags);
76740858Sbostic 	switch(f) {
7689962Ssam 	case 0:
76913075Ssam 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
77013075Ssam 		sb.sg_flags |= defflags|tabflag;
7719962Ssam 		tc = &deftc;
77213075Ssam 		ltc = &defltc;
77313075Ssam 		sb.sg_kill = defkill;
77413075Ssam 		sb.sg_erase = deferase;
77521583Sbloom 		lflags = deflflags;
7769962Ssam 		break;
7779962Ssam 	case 1:
77813075Ssam 		sb.sg_flags |= (eight ? RAW : CBREAK);
77913075Ssam 		sb.sg_flags &= ~defflags;
78012155Ssam 		/* preserve tab delays, but turn off XTABS */
78113075Ssam 		if ((sb.sg_flags & TBDELAY) == XTABS)
78213075Ssam 			sb.sg_flags &= ~TBDELAY;
7839962Ssam 		tc = &notc;
78413075Ssam 		ltc = &noltc;
78513075Ssam 		sb.sg_kill = sb.sg_erase = -1;
78621583Sbloom 		if (litout)
78721583Sbloom 			lflags |= LLITOUT;
7889962Ssam 		break;
7899962Ssam 	default:
7909962Ssam 		return;
7916444Swnj 	}
79240858Sbostic 	(void)ioctl(0, TIOCSLTC, (char *)ltc);
79340858Sbostic 	(void)ioctl(0, TIOCSETC, (char *)tc);
79440858Sbostic 	(void)ioctl(0, TIOCSETN, (char *)&sb);
79540858Sbostic 	(void)ioctl(0, TIOCLSET, (char *)&lflags);
7966444Swnj }
7976444Swnj 
79840858Sbostic void
799*58470Sbostic lostpeer(signo)
800*58470Sbostic 	int signo;
8016444Swnj {
80240858Sbostic 	(void)signal(SIGPIPE, SIG_IGN);
80340858Sbostic 	msg("\007connection closed.");
80440858Sbostic 	done(1);
80540858Sbostic }
80629729Smckusick 
80740858Sbostic /* copy SIGURGs to the child process. */
80840858Sbostic void
809*58470Sbostic copytochild(signo)
810*58470Sbostic 	int signo;
81140858Sbostic {
81240858Sbostic 	(void)kill(child, SIGURG);
8136444Swnj }
8146444Swnj 
815*58470Sbostic void
81640858Sbostic msg(str)
81740858Sbostic 	char *str;
8186444Swnj {
81940858Sbostic 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
82040858Sbostic }
82129729Smckusick 
82240858Sbostic #ifdef KERBEROS
82340858Sbostic /* VARARGS */
824*58470Sbostic void
825*58470Sbostic #if __STDC__
826*58470Sbostic warning(const char *fmt, ...)
827*58470Sbostic #else
828*58470Sbostic warning(fmt, va_alist)
829*58470Sbostic 	char *fmt;
830*58470Sbostic 	va_dcl
831*58470Sbostic #endif
83240858Sbostic {
83340858Sbostic 	va_list ap;
83440858Sbostic 
83540858Sbostic 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
836*58470Sbostic #ifdef __STDC__
837*58470Sbostic 	va_start(ap, fmt);
838*58470Sbostic #else
83940858Sbostic 	va_start(ap);
840*58470Sbostic #endif
84140858Sbostic 	fmt = va_arg(ap, char *);
84240858Sbostic 	vfprintf(stderr, fmt, ap);
84340858Sbostic 	va_end(ap);
84440858Sbostic 	(void)fprintf(stderr, ".\n");
8456444Swnj }
84640858Sbostic #endif
84736512Skfall 
848*58470Sbostic __dead void
84940858Sbostic usage()
85036512Skfall {
85140858Sbostic 	(void)fprintf(stderr,
85240858Sbostic 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
85340858Sbostic #ifdef KERBEROS
85453033Sleres #ifdef CRYPT
85554125Sbostic 	    "8EKLx", " [-k realm] ");
85653033Sleres #else
85754125Sbostic 	    "8EKL", " [-k realm] ");
85853033Sleres #endif
85945256Smckusick #else
86045063Sbostic 	    "8EL", " ");
86140858Sbostic #endif
86240858Sbostic 	exit(1);
86336512Skfall }
86440858Sbostic 
86540858Sbostic /*
86640858Sbostic  * The following routine provides compatibility (such as it is) between 4.2BSD
86740858Sbostic  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
86840858Sbostic  */
869*58470Sbostic #ifdef SUNOS4
870*58470Sbostic int
87140858Sbostic get_window_size(fd, wp)
87240858Sbostic 	int fd;
87340858Sbostic 	struct winsize *wp;
87440858Sbostic {
87540858Sbostic 	struct ttysize ts;
87640858Sbostic 	int error;
87740858Sbostic 
87840858Sbostic 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
879*58470Sbostic 		return (error);
88040858Sbostic 	wp->ws_row = ts.ts_lines;
88140858Sbostic 	wp->ws_col = ts.ts_cols;
88240858Sbostic 	wp->ws_xpixel = 0;
88340858Sbostic 	wp->ws_ypixel = 0;
884*58470Sbostic 	return (0);
88540858Sbostic }
88640858Sbostic #endif
88745063Sbostic 
888*58470Sbostic u_int
88945063Sbostic getescape(p)
89045063Sbostic 	register char *p;
89145063Sbostic {
89245063Sbostic 	long val;
89345063Sbostic 	int len;
89445063Sbostic 
89545063Sbostic 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
896*58470Sbostic 		return ((u_int)*p);
89745063Sbostic 					/* otherwise, \nnn */
89845063Sbostic 	if (*p == '\\' && len >= 2 && len <= 4) {
899*58470Sbostic 		val = strtol(++p, NULL, 8);
90045063Sbostic 		for (;;) {
90145063Sbostic 			if (!*++p)
902*58470Sbostic 				return ((u_int)val);
90345063Sbostic 			if (*p < '0' || *p > '8')
90445063Sbostic 				break;
90545063Sbostic 		}
90645063Sbostic 	}
90745063Sbostic 	msg("illegal option value -- e");
90845063Sbostic 	usage();
90945063Sbostic 	/* NOTREACHED */
91045063Sbostic }
911