xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 40683)
138728Skfall 
221595Sdist /*
338728Skfall  *	$Source: /a/staff/kfall/mit/rlogin/RCS/rlogin.c,v $
438728Skfall  *	$Header: /a/staff/kfall/mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall Exp Locker: kfall $
538728Skfall  */
638728Skfall 
738728Skfall /*
835539Sbostic  * Copyright (c) 1983 The Regents of the University of California.
935539Sbostic  * All rights reserved.
1035539Sbostic  *
1135539Sbostic  * Redistribution and use in source and binary forms are permitted
1235539Sbostic  * provided that the above copyright notice and this paragraph are
1335539Sbostic  * duplicated in all such forms and that any documentation,
1435539Sbostic  * advertising materials, and other materials related to such
1535539Sbostic  * distribution and use acknowledge that the software was developed
1635539Sbostic  * by the University of California, Berkeley.  The name of the
1735539Sbostic  * University may not be used to endorse or promote products derived
1835539Sbostic  * from this software without specific prior written permission.
1935539Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
2035539Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
2135539Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2221595Sdist  */
2321595Sdist 
246444Swnj #ifndef lint
2521595Sdist char copyright[] =
2635539Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
2721595Sdist  All rights reserved.\n";
2835539Sbostic #endif /* not lint */
296444Swnj 
3021595Sdist #ifndef lint
3136511Skfall static char sccsid[] = "@(#)rlogin.c	5.12 (Berkeley) 9/19/88";
3235539Sbostic #endif /* not lint */
3321595Sdist 
3412990Ssam /*
3512990Ssam  * rlogin - remote login
3612990Ssam  */
3726981Skarels #include <sys/param.h>
3825424Skarels #include <sys/errno.h>
3924727Smckusick #include <sys/file.h>
406444Swnj #include <sys/socket.h>
4129729Smckusick #include <sys/time.h>
4229729Smckusick #include <sys/resource.h>
4313620Ssam #include <sys/wait.h>
449365Ssam 
459207Ssam #include <netinet/in.h>
469365Ssam 
479365Ssam #include <stdio.h>
489365Ssam #include <sgtty.h>
496444Swnj #include <errno.h>
506444Swnj #include <pwd.h>
519365Ssam #include <signal.h>
5225424Skarels #include <setjmp.h>
539365Ssam #include <netdb.h>
546444Swnj 
5536511Skfall #ifdef	KERBEROS
56*40683Sbostic #include <kerberosIV/krb.h>
5736511Skfall int		encrypt = 0;
5838728Skfall char		dst_realm_buf[REALM_SZ];
5938728Skfall char		*dest_realm = NULL;
6036511Skfall CREDENTIALS	cred;
6136511Skfall Key_schedule	schedule;
6236512Skfall int		use_kerberos = 1;
6338728Skfall extern char	*krb_realmofhost();
6436511Skfall #endif	/* KERBEROS */
6536511Skfall 
6624726Smckusick # ifndef TIOCPKT_WINDOW
6724726Smckusick # define TIOCPKT_WINDOW 0x80
6824726Smckusick # endif TIOCPKT_WINDOW
6924726Smckusick 
7029729Smckusick /* concession to sun */
7129729Smckusick # ifndef SIGUSR1
7229729Smckusick # define SIGUSR1 30
7329729Smckusick # endif SIGUSR1
7429729Smckusick 
7529729Smckusick char	*index(), *rindex(), *malloc(), *getenv(), *strcat(), *strcpy();
766444Swnj struct	passwd *getpwuid();
779365Ssam char	*name;
786444Swnj int	rem;
796444Swnj char	cmdchar = '~';
806444Swnj int	eight;
8121583Sbloom int	litout;
826444Swnj char	*speeds[] =
836444Swnj     { "0", "50", "75", "110", "134", "150", "200", "300",
846444Swnj       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
8518358Ssam char	term[256] = "network";
869365Ssam extern	int errno;
879365Ssam int	lostpeer();
8824726Smckusick int	dosigwinch = 0;
8926981Skarels #ifndef sigmask
9026981Skarels #define sigmask(m)	(1 << ((m)-1))
9126981Skarels #endif
9226981Skarels #ifdef sun
9326981Skarels struct winsize {
9426981Skarels 	unsigned short ws_row, ws_col;
9526981Skarels 	unsigned short ws_xpixel, ws_ypixel;
9626981Skarels };
9729729Smckusick #endif sun
9818358Ssam struct	winsize winsize;
9924726Smckusick int	sigwinch(), oob();
1006444Swnj 
10129729Smckusick /*
10229729Smckusick  * The following routine provides compatibility (such as it is)
10329729Smckusick  * between 4.2BSD Suns and others.  Suns have only a `ttysize',
10429729Smckusick  * so we convert it to a winsize.
10529729Smckusick  */
10629729Smckusick #ifdef sun
10729729Smckusick int
10829729Smckusick get_window_size(fd, wp)
10929729Smckusick 	int fd;
11029729Smckusick 	struct winsize *wp;
11129729Smckusick {
11229729Smckusick 	struct ttysize ts;
11329729Smckusick 	int error;
11429729Smckusick 
11529729Smckusick 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
11629729Smckusick 		return (error);
11729729Smckusick 	wp->ws_row = ts.ts_lines;
11829729Smckusick 	wp->ws_col = ts.ts_cols;
11929729Smckusick 	wp->ws_xpixel = 0;
12029729Smckusick 	wp->ws_ypixel = 0;
12129729Smckusick 	return (0);
12229729Smckusick }
12329729Smckusick #else sun
12429729Smckusick #define get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
12529729Smckusick #endif sun
12629729Smckusick 
1276444Swnj main(argc, argv)
1286444Swnj 	int argc;
1296444Swnj 	char **argv;
1306444Swnj {
1319365Ssam 	char *host, *cp;
1326444Swnj 	struct sgttyb ttyb;
1336444Swnj 	struct passwd *pwd;
1349365Ssam 	struct servent *sp;
13524726Smckusick 	int uid, options = 0, oldmask;
13617449Slepreau 	int on = 1;
1376444Swnj 
1386444Swnj 	host = rindex(argv[0], '/');
1396444Swnj 	if (host)
1406444Swnj 		host++;
1416444Swnj 	else
1426444Swnj 		host = argv[0];
1436444Swnj 	argv++, --argc;
1446444Swnj 	if (!strcmp(host, "rlogin"))
1456444Swnj 		host = *argv++, --argc;
1466444Swnj another:
14710839Ssam 	if (argc > 0 && !strcmp(*argv, "-d")) {
1486444Swnj 		argv++, argc--;
14910415Ssam 		options |= SO_DEBUG;
1506444Swnj 		goto another;
1516444Swnj 	}
15210839Ssam 	if (argc > 0 && !strcmp(*argv, "-l")) {
1536444Swnj 		argv++, argc--;
1546444Swnj 		if (argc == 0)
1556444Swnj 			goto usage;
1566444Swnj 		name = *argv++; argc--;
1576444Swnj 		goto another;
1586444Swnj 	}
15910839Ssam 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
1606444Swnj 		cmdchar = argv[0][2];
1616444Swnj 		argv++, argc--;
1626444Swnj 		goto another;
1636444Swnj 	}
16410839Ssam 	if (argc > 0 && !strcmp(*argv, "-8")) {
1656444Swnj 		eight = 1;
1666444Swnj 		argv++, argc--;
1676444Swnj 		goto another;
1686444Swnj 	}
16921583Sbloom 	if (argc > 0 && !strcmp(*argv, "-L")) {
17021583Sbloom 		litout = 1;
17121583Sbloom 		argv++, argc--;
17221583Sbloom 		goto another;
17321583Sbloom 	}
17436511Skfall 
17536511Skfall #ifdef	KERBEROS
17636511Skfall 	if (argc > 0 && !strcmp(*argv, "-x")) {
17736511Skfall 		encrypt = 1;
17836511Skfall 		des_set_key(cred.session, schedule);
17936511Skfall 		argv++, argc--;
18036511Skfall 		goto another;
18136511Skfall 	}
18236524Skfall 	if (argc > 0 && !strcmp(*argv, "-k")) {
18336524Skfall 		argv++, argc--;
18436524Skfall 		if(argc <= 0 || (**argv == '-')) {
18536524Skfall 			fprintf(stderr, "-k option requires an argument\n");
18636524Skfall 			exit(1);
18736524Skfall 		}
18838728Skfall 		dest_realm = dst_realm_buf;
18938728Skfall 		strncpy(dest_realm, *argv, REALM_SZ);
19036524Skfall 		argv++, argc--;
19136524Skfall 		goto another;
19236524Skfall 	}
19336524Skfall 
19436511Skfall #endif	/* KERBEROS */
19536511Skfall 
1966444Swnj 	if (host == 0)
1976444Swnj 		goto usage;
1986444Swnj 	if (argc > 0)
1996444Swnj 		goto usage;
20038728Skfall 	uid = getuid();
20138728Skfall 	pwd = getpwuid(uid);
2026444Swnj 	if (pwd == 0) {
2036444Swnj 		fprintf(stderr, "Who are you?\n");
2046444Swnj 		exit(1);
2056444Swnj 	}
20636512Skfall #ifdef	KERBEROS
20736716Skfall 	sp = getservbyname((encrypt ? "eklogin" : "klogin"), "tcp");
20836512Skfall 	if(sp == NULL) {
20936512Skfall 		use_kerberos = 0;
21036512Skfall 		old_warning("klogin service unknown");
21136512Skfall 		sp = getservbyname("login", "tcp");
21236512Skfall 	}
21336512Skfall #else
2149365Ssam 	sp = getservbyname("login", "tcp");
21536512Skfall #endif
2169365Ssam 	if (sp == 0) {
2179365Ssam 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
2189365Ssam 		exit(2);
2199365Ssam 	}
2209241Ssam 	cp = getenv("TERM");
2219241Ssam 	if (cp)
22229729Smckusick 		(void) strcpy(term, cp);
22318358Ssam 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
22429729Smckusick 		(void) strcat(term, "/");
22529729Smckusick 		(void) strcat(term, speeds[ttyb.sg_ospeed]);
2266444Swnj 	}
22729729Smckusick 	(void) get_window_size(0, &winsize);
22829729Smckusick 	(void) signal(SIGPIPE, lostpeer);
22929729Smckusick 	/* will use SIGUSR1 for window size hack, so hold it off */
23029729Smckusick 	oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
23136511Skfall 
23236511Skfall #ifdef	KERBEROS
23336512Skfall try_connect:
23436512Skfall 	if(use_kerberos) {
23536512Skfall 		rem = KSUCCESS;
23638728Skfall 		if (dest_realm == NULL)
23738728Skfall 			dest_realm = krb_realmofhost(host);
23838728Skfall 
23938728Skfall 		errno = 0;
24038728Skfall 		if(encrypt) {
24138728Skfall 			rem = krcmd_mutual(
24238728Skfall 				&host, sp->s_port,
24338728Skfall 				name ? name : pwd->pw_name, term,
24438728Skfall 				0, dest_realm,
24538728Skfall 				&cred, schedule
24638728Skfall 			);
24736511Skfall 		} else {
24838728Skfall 			rem = krcmd(
24938728Skfall 				&host, sp->s_port,
25038728Skfall 				name ? name : pwd->pw_name, term,
25138728Skfall 				0, dest_realm
25236511Skfall 			);
25336511Skfall 		}
25438728Skfall 		if (rem < 0) {
25536512Skfall 			use_kerberos = 0;
25636628Skfall 			sp = getservbyname("login", "tcp");
25736628Skfall 			if(sp == NULL) {
25838728Skfall 				fprintf(stderr,
25938728Skfall 					"unknown service login/tcp\n");
26036628Skfall 				exit(1);
26136628Skfall 			}
26238728Skfall 			if (errno == ECONNREFUSED)
26338728Skfall 				old_warning("remote host doesn't support Kerberos");
26438728Skfall 			if (errno == ENOENT)
26538728Skfall 				old_warning("Can't provide Kerberos auth data");
26636512Skfall 			goto try_connect;
26736512Skfall 		}
26836511Skfall 	} else {
26936512Skfall 		if(encrypt) {
27038728Skfall 			fprintf(stderr, "The -x flag requires Kerberos authentication\n");
27136512Skfall 			exit(1);
27236512Skfall 		}
27336512Skfall         	rem = rcmd(&host, sp->s_port, pwd->pw_name,
27436512Skfall 	    		name ? name : pwd->pw_name, term, 0);
27536511Skfall 	}
27636512Skfall #else
27736512Skfall        	rem = rcmd(&host, sp->s_port, pwd->pw_name,
27836512Skfall     		name ? name : pwd->pw_name, term, 0);
27936512Skfall #endif
28036511Skfall 
28136511Skfall 	if(rem < 0)
28236511Skfall 		exit(1);
28336511Skfall 
28410415Ssam 	if (options & SO_DEBUG &&
28517449Slepreau 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
28610415Ssam 		perror("rlogin: setsockopt (SO_DEBUG)");
2879365Ssam 	if (setuid(uid) < 0) {
2889365Ssam 		perror("rlogin: setuid");
2899365Ssam 		exit(1);
2909365Ssam 	}
29124726Smckusick 	doit(oldmask);
2929365Ssam 	/*NOTREACHED*/
2936444Swnj usage:
2946444Swnj 	fprintf(stderr,
29536511Skfall #ifdef	KERBEROS
29636592Skfall 	    "usage: rlogin host [ -ex ] [ -l username ] [ -k realm ] [ -8 ] [ -L ] [ -x ]\n");
29736511Skfall #else
29825341Smckusick 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n");
29936511Skfall #endif
3006444Swnj 	exit(1);
3016444Swnj }
3026444Swnj 
3036444Swnj #define CRLF "\r\n"
3046444Swnj 
3059365Ssam int	child;
30611803Sedward int	catchild();
30729729Smckusick int	copytochild(), writeroob();
3089365Ssam 
30913075Ssam int	defflags, tabflag;
31021583Sbloom int	deflflags;
31113075Ssam char	deferase, defkill;
31213075Ssam struct	tchars deftc;
31313075Ssam struct	ltchars defltc;
31413075Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
31513075Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
3166444Swnj 
31724726Smckusick doit(oldmask)
3186444Swnj {
3196444Swnj 	int exit();
32013075Ssam 	struct sgttyb sb;
3216444Swnj 
32229729Smckusick 	(void) ioctl(0, TIOCGETP, (char *)&sb);
32313075Ssam 	defflags = sb.sg_flags;
32412155Ssam 	tabflag = defflags & TBDELAY;
3259962Ssam 	defflags &= ECHO | CRMOD;
32613075Ssam 	deferase = sb.sg_erase;
32713075Ssam 	defkill = sb.sg_kill;
32829729Smckusick 	(void) ioctl(0, TIOCLGET, (char *)&deflflags);
32929729Smckusick 	(void) ioctl(0, TIOCGETC, (char *)&deftc);
33013075Ssam 	notc.t_startc = deftc.t_startc;
33113075Ssam 	notc.t_stopc = deftc.t_stopc;
33229729Smckusick 	(void) ioctl(0, TIOCGLTC, (char *)&defltc);
33329729Smckusick 	(void) signal(SIGINT, SIG_IGN);
33429729Smckusick 	setsignal(SIGHUP, exit);
33529729Smckusick 	setsignal(SIGQUIT, exit);
3369365Ssam 	child = fork();
3379365Ssam 	if (child == -1) {
3389365Ssam 		perror("rlogin: fork");
33925424Skarels 		done(1);
3409365Ssam 	}
3419365Ssam 	if (child == 0) {
34224726Smckusick 		mode(1);
34329729Smckusick 		if (reader(oldmask) == 0) {
34425424Skarels 			prf("Connection closed.");
34525424Skarels 			exit(0);
34625424Skarels 		}
34712155Ssam 		sleep(1);
34812155Ssam 		prf("\007Connection closed.");
3496444Swnj 		exit(3);
3506444Swnj 	}
35129729Smckusick 
35229729Smckusick 	/*
35329729Smckusick 	 * We may still own the socket, and may have a pending SIGURG
35429729Smckusick 	 * (or might receive one soon) that we really want to send to
35529729Smckusick 	 * the reader.  Set a trap that simply copies such signals to
35629729Smckusick 	 * the child.
35729729Smckusick 	 */
35829729Smckusick 	(void) signal(SIGURG, copytochild);
35929729Smckusick 	(void) signal(SIGUSR1, writeroob);
36029729Smckusick 	(void) sigsetmask(oldmask);
36129729Smckusick 	(void) signal(SIGCHLD, catchild);
3629365Ssam 	writer();
36312155Ssam 	prf("Closed connection.");
36425424Skarels 	done(0);
3656444Swnj }
3666444Swnj 
36729729Smckusick /*
36829729Smckusick  * Trap a signal, unless it is being ignored.
36929729Smckusick  */
37029729Smckusick setsignal(sig, act)
37129729Smckusick 	int sig, (*act)();
37229729Smckusick {
37329729Smckusick 	int omask = sigblock(sigmask(sig));
37429729Smckusick 
37529729Smckusick 	if (signal(sig, act) == SIG_IGN)
37629729Smckusick 		(void) signal(sig, SIG_IGN);
37729729Smckusick 	(void) sigsetmask(omask);
37829729Smckusick }
37929729Smckusick 
38025424Skarels done(status)
38125424Skarels 	int status;
3826444Swnj {
38329729Smckusick 	int w;
3846444Swnj 
3856444Swnj 	mode(0);
38629729Smckusick 	if (child > 0) {
38729729Smckusick 		/* make sure catchild does not snap it up */
38829729Smckusick 		(void) signal(SIGCHLD, SIG_DFL);
38929729Smckusick 		if (kill(child, SIGKILL) >= 0)
39029729Smckusick 			while ((w = wait((union wait *)0)) > 0 && w != child)
39129729Smckusick 				/*void*/;
39229729Smckusick 	}
39325424Skarels 	exit(status);
3946444Swnj }
3956444Swnj 
39624726Smckusick /*
39729729Smckusick  * Copy SIGURGs to the child process.
39829729Smckusick  */
39929729Smckusick copytochild()
40029729Smckusick {
40129729Smckusick 
40229729Smckusick 	(void) kill(child, SIGURG);
40329729Smckusick }
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  */
40924726Smckusick writeroob()
41024726Smckusick {
41124726Smckusick 
41225341Smckusick 	if (dosigwinch == 0) {
41324919Smckusick 		sendwindow();
41429729Smckusick 		(void) signal(SIGWINCH, sigwinch);
41525341Smckusick 	}
41624726Smckusick 	dosigwinch = 1;
41724726Smckusick }
41824726Smckusick 
41911803Sedward catchild()
42011803Sedward {
42111803Sedward 	union wait status;
42211803Sedward 	int pid;
42311803Sedward 
42411803Sedward again:
42529729Smckusick 	pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
42611803Sedward 	if (pid == 0)
42711803Sedward 		return;
42811803Sedward 	/*
42911803Sedward 	 * if the child (reader) dies, just quit
43011803Sedward 	 */
43111803Sedward 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
43229729Smckusick 		done((int)(status.w_termsig | status.w_retcode));
43311803Sedward 	goto again;
43411803Sedward }
43511803Sedward 
4366444Swnj /*
4379365Ssam  * writer: write to remote: 0 -> line.
4389365Ssam  * ~.	terminate
4399365Ssam  * ~^Z	suspend rlogin process.
44010415Ssam  * ~^Y  suspend rlogin process, but leave reader alone.
4416444Swnj  */
4429365Ssam writer()
4436444Swnj {
44423530Sbloom 	char c;
44511803Sedward 	register n;
44623530Sbloom 	register bol = 1;               /* beginning of line */
44723530Sbloom 	register local = 0;
4486444Swnj 
44911803Sedward 	for (;;) {
45011803Sedward 		n = read(0, &c, 1);
45118358Ssam 		if (n <= 0) {
45218358Ssam 			if (n < 0 && errno == EINTR)
45318358Ssam 				continue;
45411803Sedward 			break;
45518358Ssam 		}
4569365Ssam 		/*
4579365Ssam 		 * If we're at the beginning of the line
4589365Ssam 		 * and recognize a command character, then
4599365Ssam 		 * we echo locally.  Otherwise, characters
4609365Ssam 		 * are echo'd remotely.  If the command
4619365Ssam 		 * character is doubled, this acts as a
4629365Ssam 		 * force and local echo is suppressed.
4639365Ssam 		 */
46423530Sbloom 		if (bol) {
46523530Sbloom 			bol = 0;
46623530Sbloom 			if (c == cmdchar) {
46723530Sbloom 				bol = 0;
46823530Sbloom 				local = 1;
46923530Sbloom 				continue;
4706444Swnj 			}
47123530Sbloom 		} else if (local) {
47223530Sbloom 			local = 0;
47323530Sbloom 			if (c == '.' || c == deftc.t_eofc) {
47423530Sbloom 				echo(c);
47523530Sbloom 				break;
4766444Swnj 			}
47723530Sbloom 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
47823530Sbloom 				bol = 1;
47923530Sbloom 				echo(c);
48023530Sbloom 				stop(c);
48123530Sbloom 				continue;
48223530Sbloom 			}
48336511Skfall 			if (c != cmdchar) {
48436511Skfall #ifdef	KERBEROS
48536511Skfall 				if(encrypt) {
48636511Skfall 					(void) des_write(
48736511Skfall 						rem,
48836511Skfall 						&cmdchar,
48936511Skfall 						1
49036511Skfall 					);
49136511Skfall 				} else
49236511Skfall #endif
49336511Skfall 					(void) write(
49436511Skfall 						rem,
49536511Skfall 						&cmdchar,
49636511Skfall 						1
49736511Skfall 					);
49836511Skfall 			}
4996444Swnj 		}
50036511Skfall 
50136511Skfall #ifdef	KERBEROS
50236511Skfall 		if(encrypt) {
50336511Skfall 			if (des_write(rem, &c, 1) == 0) {
50436511Skfall 				prf("line gone");
50536511Skfall 				break;
50636511Skfall 			}
50736511Skfall 		} else
50836511Skfall #endif
50936511Skfall 			if (write(rem, &c, 1) == 0) {
51036511Skfall 				prf("line gone");
51136511Skfall 				break;
51236511Skfall 			}
51323530Sbloom 		bol = c == defkill || c == deftc.t_eofc ||
51425424Skarels 		    c == deftc.t_intrc || c == defltc.t_suspc ||
51523530Sbloom 		    c == '\r' || c == '\n';
5166444Swnj 	}
5176444Swnj }
5186444Swnj 
51923530Sbloom echo(c)
52023530Sbloom register char c;
52123530Sbloom {
52223530Sbloom 	char buf[8];
52323530Sbloom 	register char *p = buf;
52423530Sbloom 
52523530Sbloom 	c &= 0177;
52623530Sbloom 	*p++ = cmdchar;
52723530Sbloom 	if (c < ' ') {
52823530Sbloom 		*p++ = '^';
52923530Sbloom 		*p++ = c + '@';
53023530Sbloom 	} else if (c == 0177) {
53123530Sbloom 		*p++ = '^';
53223530Sbloom 		*p++ = '?';
53323530Sbloom 	} else
53423530Sbloom 		*p++ = c;
53523530Sbloom 	*p++ = '\r';
53623530Sbloom 	*p++ = '\n';
53729729Smckusick 	(void) write(1, buf, p - buf);
53823530Sbloom }
53923530Sbloom 
54018358Ssam stop(cmdc)
54118358Ssam 	char cmdc;
54218358Ssam {
54318358Ssam 	mode(0);
54429729Smckusick 	(void) signal(SIGCHLD, SIG_IGN);
54529729Smckusick 	(void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
54629729Smckusick 	(void) signal(SIGCHLD, catchild);
54718358Ssam 	mode(1);
54818358Ssam 	sigwinch();			/* check for size changes */
54918358Ssam }
55018358Ssam 
55118358Ssam sigwinch()
55218358Ssam {
55318358Ssam 	struct winsize ws;
55418358Ssam 
55529729Smckusick 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
55618358Ssam 	    bcmp(&ws, &winsize, sizeof (ws))) {
55718358Ssam 		winsize = ws;
55824726Smckusick 		sendwindow();
55918358Ssam 	}
56018358Ssam }
56118358Ssam 
56224726Smckusick /*
56324726Smckusick  * Send the window size to the server via the magic escape
56424726Smckusick  */
56524726Smckusick sendwindow()
56624726Smckusick {
56724726Smckusick 	char obuf[4 + sizeof (struct winsize)];
56824726Smckusick 	struct winsize *wp = (struct winsize *)(obuf+4);
56924726Smckusick 
57024726Smckusick 	obuf[0] = 0377;
57124726Smckusick 	obuf[1] = 0377;
57224726Smckusick 	obuf[2] = 's';
57324726Smckusick 	obuf[3] = 's';
57424726Smckusick 	wp->ws_row = htons(winsize.ws_row);
57524726Smckusick 	wp->ws_col = htons(winsize.ws_col);
57624726Smckusick 	wp->ws_xpixel = htons(winsize.ws_xpixel);
57724726Smckusick 	wp->ws_ypixel = htons(winsize.ws_ypixel);
57836511Skfall 
57936511Skfall #ifdef	KERBEROS
58036511Skfall 	if(encrypt)
58136511Skfall 		(void) des_write(rem, obuf, sizeof(obuf));
58236511Skfall 	else
58336511Skfall #endif
58436511Skfall 		(void) write(rem, obuf, sizeof(obuf));
58524726Smckusick }
58624726Smckusick 
58725424Skarels /*
58825424Skarels  * reader: read from remote: line -> 1
58925424Skarels  */
59025424Skarels #define	READING	1
59125424Skarels #define	WRITING	2
59225424Skarels 
59325424Skarels char	rcvbuf[8 * 1024];
59425424Skarels int	rcvcnt;
59525424Skarels int	rcvstate;
59626981Skarels int	ppid;
59725424Skarels jmp_buf	rcvtop;
59825424Skarels 
5996444Swnj oob()
6006444Swnj {
60125424Skarels 	int out = FWRITE, atmark, n;
60225424Skarels 	int rcvd = 0;
6039365Ssam 	char waste[BUFSIZ], mark;
60424726Smckusick 	struct sgttyb sb;
6056444Swnj 
60625424Skarels 	while (recv(rem, &mark, 1, MSG_OOB) < 0)
60725424Skarels 		switch (errno) {
60825424Skarels 
60925424Skarels 		case EWOULDBLOCK:
61025424Skarels 			/*
61125424Skarels 			 * Urgent data not here yet.
61225424Skarels 			 * It may not be possible to send it yet
61325424Skarels 			 * if we are blocked for output
61425424Skarels 			 * and our input buffer is full.
61525424Skarels 			 */
61625424Skarels 			if (rcvcnt < sizeof(rcvbuf)) {
61725424Skarels 				n = read(rem, rcvbuf + rcvcnt,
61825424Skarels 					sizeof(rcvbuf) - rcvcnt);
61925424Skarels 				if (n <= 0)
62025424Skarels 					return;
62125424Skarels 				rcvd += n;
62225424Skarels 			} else {
62325424Skarels 				n = read(rem, waste, sizeof(waste));
62425424Skarels 				if (n <= 0)
62525424Skarels 					return;
62625424Skarels 			}
62725424Skarels 			continue;
62825424Skarels 
62925424Skarels 		default:
63025424Skarels 			return;
6316444Swnj 	}
63225424Skarels 	if (mark & TIOCPKT_WINDOW) {
63324726Smckusick 		/*
63424726Smckusick 		 * Let server know about window size changes
63524726Smckusick 		 */
63629729Smckusick 		(void) kill(ppid, SIGUSR1);
63724726Smckusick 	}
63825424Skarels 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
63929729Smckusick 		(void) ioctl(0, TIOCGETP, (char *)&sb);
64024726Smckusick 		sb.sg_flags &= ~CBREAK;
64124726Smckusick 		sb.sg_flags |= RAW;
64229729Smckusick 		(void) ioctl(0, TIOCSETN, (char *)&sb);
64313075Ssam 		notc.t_stopc = -1;
64413075Ssam 		notc.t_startc = -1;
64529729Smckusick 		(void) ioctl(0, TIOCSETC, (char *)&notc);
6466444Swnj 	}
64725424Skarels 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
64829729Smckusick 		(void) ioctl(0, TIOCGETP, (char *)&sb);
64924726Smckusick 		sb.sg_flags &= ~RAW;
65024726Smckusick 		sb.sg_flags |= CBREAK;
65129729Smckusick 		(void) ioctl(0, TIOCSETN, (char *)&sb);
65213075Ssam 		notc.t_stopc = deftc.t_stopc;
65313075Ssam 		notc.t_startc = deftc.t_startc;
65429729Smckusick 		(void) ioctl(0, TIOCSETC, (char *)&notc);
6556444Swnj 	}
65625424Skarels 	if (mark & TIOCPKT_FLUSHWRITE) {
65729729Smckusick 		(void) ioctl(1, TIOCFLUSH, (char *)&out);
65825424Skarels 		for (;;) {
65925424Skarels 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
66025424Skarels 				perror("ioctl");
66125424Skarels 				break;
66225424Skarels 			}
66325424Skarels 			if (atmark)
66425424Skarels 				break;
66525424Skarels 			n = read(rem, waste, sizeof (waste));
66625424Skarels 			if (n <= 0)
66725424Skarels 				break;
66825424Skarels 		}
66925424Skarels 		/*
67025424Skarels 		 * Don't want any pending data to be output,
67125424Skarels 		 * so clear the recv buffer.
67225424Skarels 		 * If we were hanging on a write when interrupted,
67325424Skarels 		 * don't want it to restart.  If we were reading,
67425424Skarels 		 * restart anyway.
67525424Skarels 		 */
67625424Skarels 		rcvcnt = 0;
67725424Skarels 		longjmp(rcvtop, 1);
67825424Skarels 	}
67929729Smckusick 
68025424Skarels 	/*
68129729Smckusick 	 * oob does not do FLUSHREAD (alas!)
68229729Smckusick 	 */
68329729Smckusick 
68429729Smckusick 	/*
68525424Skarels 	 * If we filled the receive buffer while a read was pending,
68625424Skarels 	 * longjmp to the top to restart appropriately.  Don't abort
68725424Skarels 	 * a pending write, however, or we won't know how much was written.
68825424Skarels 	 */
68925424Skarels 	if (rcvd && rcvstate == READING)
69025424Skarels 		longjmp(rcvtop, 1);
6916444Swnj }
6926444Swnj 
6939365Ssam /*
6949365Ssam  * reader: read from remote: line -> 1
6959365Ssam  */
69629729Smckusick reader(oldmask)
69729729Smckusick 	int oldmask;
6986444Swnj {
69926981Skarels #if !defined(BSD) || BSD < 43
70026981Skarels 	int pid = -getpid();
70126981Skarels #else
70225424Skarels 	int pid = getpid();
70326981Skarels #endif
70425424Skarels 	int n, remaining;
70525424Skarels 	char *bufp = rcvbuf;
7066444Swnj 
70729729Smckusick 	(void) signal(SIGTTOU, SIG_IGN);
70829729Smckusick 	(void) signal(SIGURG, oob);
70926981Skarels 	ppid = getppid();
71029729Smckusick 	(void) fcntl(rem, F_SETOWN, pid);
71125424Skarels 	(void) setjmp(rcvtop);
71229729Smckusick 	(void) sigsetmask(oldmask);
7136444Swnj 	for (;;) {
71425424Skarels 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
71525424Skarels 			rcvstate = WRITING;
71625424Skarels 			n = write(1, bufp, remaining);
71725424Skarels 			if (n < 0) {
71825424Skarels 				if (errno != EINTR)
71926189Slepreau 					return (-1);
72025424Skarels 				continue;
72125424Skarels 			}
72225424Skarels 			bufp += n;
72325424Skarels 		}
72425424Skarels 		bufp = rcvbuf;
72525424Skarels 		rcvcnt = 0;
72625424Skarels 		rcvstate = READING;
72736511Skfall 
72836511Skfall #ifdef	KERBEROS
72936511Skfall 		if(encrypt)
73036511Skfall 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
73136511Skfall 		else
73236511Skfall #endif
73336511Skfall 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
73425424Skarels 		if (rcvcnt == 0)
73525424Skarels 			return (0);
73625424Skarels 		if (rcvcnt < 0) {
7379365Ssam 			if (errno == EINTR)
7386444Swnj 				continue;
73926981Skarels 			perror("read");
74025424Skarels 			return (-1);
7416444Swnj 		}
7426444Swnj 	}
7436444Swnj }
7446444Swnj 
7456444Swnj mode(f)
7466444Swnj {
74713075Ssam 	struct tchars *tc;
74813075Ssam 	struct ltchars *ltc;
74913075Ssam 	struct sgttyb sb;
75021583Sbloom 	int	lflags;
7519365Ssam 
75229729Smckusick 	(void) ioctl(0, TIOCGETP, (char *)&sb);
75329729Smckusick 	(void) ioctl(0, TIOCLGET, (char *)&lflags);
7549962Ssam 	switch (f) {
7559962Ssam 
7569962Ssam 	case 0:
75713075Ssam 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
75813075Ssam 		sb.sg_flags |= defflags|tabflag;
7599962Ssam 		tc = &deftc;
76013075Ssam 		ltc = &defltc;
76113075Ssam 		sb.sg_kill = defkill;
76213075Ssam 		sb.sg_erase = deferase;
76321583Sbloom 		lflags = deflflags;
7649962Ssam 		break;
7659962Ssam 
7669962Ssam 	case 1:
76713075Ssam 		sb.sg_flags |= (eight ? RAW : CBREAK);
76813075Ssam 		sb.sg_flags &= ~defflags;
76912155Ssam 		/* preserve tab delays, but turn off XTABS */
77013075Ssam 		if ((sb.sg_flags & TBDELAY) == XTABS)
77113075Ssam 			sb.sg_flags &= ~TBDELAY;
7729962Ssam 		tc = &notc;
77313075Ssam 		ltc = &noltc;
77413075Ssam 		sb.sg_kill = sb.sg_erase = -1;
77521583Sbloom 		if (litout)
77621583Sbloom 			lflags |= LLITOUT;
7779962Ssam 		break;
7789962Ssam 
7799962Ssam 	default:
7809962Ssam 		return;
7816444Swnj 	}
78229729Smckusick 	(void) ioctl(0, TIOCSLTC, (char *)ltc);
78329729Smckusick 	(void) ioctl(0, TIOCSETC, (char *)tc);
78429729Smckusick 	(void) ioctl(0, TIOCSETN, (char *)&sb);
78529729Smckusick 	(void) ioctl(0, TIOCLSET, (char *)&lflags);
7866444Swnj }
7876444Swnj 
7889365Ssam /*VARARGS*/
78924726Smckusick prf(f, a1, a2, a3, a4, a5)
7909365Ssam 	char *f;
7916444Swnj {
79229729Smckusick 
79324726Smckusick 	fprintf(stderr, f, a1, a2, a3, a4, a5);
7946444Swnj 	fprintf(stderr, CRLF);
7956444Swnj }
7966444Swnj 
7979365Ssam lostpeer()
7986444Swnj {
79929729Smckusick 
80029729Smckusick 	(void) signal(SIGPIPE, SIG_IGN);
80112155Ssam 	prf("\007Connection closed.");
80225424Skarels 	done(1);
8036444Swnj }
80436512Skfall 
80536512Skfall old_warning(str)
80636512Skfall 	char	*str;
80736512Skfall {
80836512Skfall 	prf("Warning: %s, using standard rlogin", str);
80936512Skfall }
810