xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 24727)
121595Sdist /*
221595Sdist  * Copyright (c) 1983 Regents of the University of California.
321595Sdist  * All rights reserved.  The Berkeley software License Agreement
421595Sdist  * specifies the terms and conditions for redistribution.
521595Sdist  */
621595Sdist 
76444Swnj #ifndef lint
821595Sdist char copyright[] =
921595Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021595Sdist  All rights reserved.\n";
1121595Sdist #endif not lint
126444Swnj 
1321595Sdist #ifndef lint
14*24727Smckusick static char sccsid[] = "@(#)rlogin.c	5.5 (Berkeley) 09/12/85";
1521595Sdist #endif not lint
1621595Sdist 
1712990Ssam /*
1812990Ssam  * rlogin - remote login
1912990Ssam  */
206444Swnj #include <sys/types.h>
21*24727Smckusick #include <sys/file.h>
226444Swnj #include <sys/socket.h>
2313620Ssam #include <sys/wait.h>
249365Ssam 
259207Ssam #include <netinet/in.h>
269365Ssam 
279365Ssam #include <stdio.h>
289365Ssam #include <sgtty.h>
296444Swnj #include <errno.h>
306444Swnj #include <pwd.h>
319365Ssam #include <signal.h>
329365Ssam #include <netdb.h>
336444Swnj 
3424726Smckusick # ifndef TIOCPKT_WINDOW
3524726Smckusick # define TIOCPKT_WINDOW 0x80
3624726Smckusick # endif TIOCPKT_WINDOW
3724726Smckusick 
386444Swnj char	*index(), *rindex(), *malloc(), *getenv();
396444Swnj struct	passwd *getpwuid();
409365Ssam char	*name;
416444Swnj int	rem;
426444Swnj char	cmdchar = '~';
436444Swnj int	eight;
4421583Sbloom int	litout;
456444Swnj char	*speeds[] =
466444Swnj     { "0", "50", "75", "110", "134", "150", "200", "300",
476444Swnj       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
4818358Ssam char	term[256] = "network";
499365Ssam extern	int errno;
509365Ssam int	lostpeer();
5124726Smckusick int	dosigwinch = 0;
5224726Smckusick int	nosigwin = 0;
5318358Ssam struct	winsize winsize;
5424726Smckusick int	sigwinch(), oob();
556444Swnj 
566444Swnj main(argc, argv)
576444Swnj 	int argc;
586444Swnj 	char **argv;
596444Swnj {
609365Ssam 	char *host, *cp;
616444Swnj 	struct sgttyb ttyb;
626444Swnj 	struct passwd *pwd;
639365Ssam 	struct servent *sp;
6424726Smckusick 	int uid, options = 0, oldmask;
6517449Slepreau 	int on = 1;
666444Swnj 
676444Swnj 	host = rindex(argv[0], '/');
686444Swnj 	if (host)
696444Swnj 		host++;
706444Swnj 	else
716444Swnj 		host = argv[0];
726444Swnj 	argv++, --argc;
736444Swnj 	if (!strcmp(host, "rlogin"))
746444Swnj 		host = *argv++, --argc;
756444Swnj another:
7610839Ssam 	if (argc > 0 && !strcmp(*argv, "-d")) {
776444Swnj 		argv++, argc--;
7810415Ssam 		options |= SO_DEBUG;
796444Swnj 		goto another;
806444Swnj 	}
8110839Ssam 	if (argc > 0 && !strcmp(*argv, "-l")) {
826444Swnj 		argv++, argc--;
836444Swnj 		if (argc == 0)
846444Swnj 			goto usage;
856444Swnj 		name = *argv++; argc--;
866444Swnj 		goto another;
876444Swnj 	}
8810839Ssam 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
896444Swnj 		cmdchar = argv[0][2];
906444Swnj 		argv++, argc--;
916444Swnj 		goto another;
926444Swnj 	}
9310839Ssam 	if (argc > 0 && !strcmp(*argv, "-8")) {
946444Swnj 		eight = 1;
956444Swnj 		argv++, argc--;
966444Swnj 		goto another;
976444Swnj 	}
9821583Sbloom 	if (argc > 0 && !strcmp(*argv, "-L")) {
9921583Sbloom 		litout = 1;
10021583Sbloom 		argv++, argc--;
10121583Sbloom 		goto another;
10221583Sbloom 	}
10318358Ssam 	if (argc > 0 && !strcmp(*argv, "-w")) {
10418358Ssam 		nosigwin++;
10518358Ssam 		argv++, argc--;
10618358Ssam 		goto another;
10718358Ssam 	}
1086444Swnj 	if (host == 0)
1096444Swnj 		goto usage;
1106444Swnj 	if (argc > 0)
1116444Swnj 		goto usage;
1126444Swnj 	pwd = getpwuid(getuid());
1136444Swnj 	if (pwd == 0) {
1146444Swnj 		fprintf(stderr, "Who are you?\n");
1156444Swnj 		exit(1);
1166444Swnj 	}
1179365Ssam 	sp = getservbyname("login", "tcp");
1189365Ssam 	if (sp == 0) {
1199365Ssam 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
1209365Ssam 		exit(2);
1219365Ssam 	}
1229241Ssam 	cp = getenv("TERM");
1239241Ssam 	if (cp)
1249241Ssam 		strcpy(term, cp);
12518358Ssam 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
1266444Swnj 		strcat(term, "/");
1276444Swnj 		strcat(term, speeds[ttyb.sg_ospeed]);
1286444Swnj 	}
12924726Smckusick 	(void) ioctl(0, TIOCGWINSZ, &winsize);
13012990Ssam 	signal(SIGPIPE, lostpeer);
13124726Smckusick 	signal(SIGURG, oob);
13224726Smckusick 	oldmask = sigblock(sigmask(SIGURG));
1339365Ssam         rem = rcmd(&host, sp->s_port, pwd->pw_name,
1346444Swnj 	    name ? name : pwd->pw_name, term, 0);
1356444Swnj         if (rem < 0)
1366444Swnj                 exit(1);
13710415Ssam 	if (options & SO_DEBUG &&
13817449Slepreau 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
13910415Ssam 		perror("rlogin: setsockopt (SO_DEBUG)");
1409365Ssam 	uid = getuid();
1419365Ssam 	if (setuid(uid) < 0) {
1429365Ssam 		perror("rlogin: setuid");
1439365Ssam 		exit(1);
1449365Ssam 	}
14524726Smckusick 	doit(oldmask);
1469365Ssam 	/*NOTREACHED*/
1476444Swnj usage:
1486444Swnj 	fprintf(stderr,
14921583Sbloom 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ] [ -w ]\n");
1506444Swnj 	exit(1);
1516444Swnj }
1526444Swnj 
1536444Swnj #define CRLF "\r\n"
1546444Swnj 
1559365Ssam int	child;
15611803Sedward int	catchild();
15724726Smckusick int	writeroob();
1589365Ssam 
15913075Ssam int	defflags, tabflag;
16021583Sbloom int	deflflags;
16113075Ssam char	deferase, defkill;
16213075Ssam struct	tchars deftc;
16313075Ssam struct	ltchars defltc;
16413075Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
16513075Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
1666444Swnj 
16724726Smckusick doit(oldmask)
1686444Swnj {
1696444Swnj 	int exit();
17013075Ssam 	struct sgttyb sb;
1716444Swnj 
17213075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
17313075Ssam 	defflags = sb.sg_flags;
17412155Ssam 	tabflag = defflags & TBDELAY;
1759962Ssam 	defflags &= ECHO | CRMOD;
17613075Ssam 	deferase = sb.sg_erase;
17713075Ssam 	defkill = sb.sg_kill;
17821583Sbloom 	ioctl(0, TIOCLGET, (char *)&deflflags);
17913075Ssam 	ioctl(0, TIOCGETC, (char *)&deftc);
18013075Ssam 	notc.t_startc = deftc.t_startc;
18113075Ssam 	notc.t_stopc = deftc.t_stopc;
18213075Ssam 	ioctl(0, TIOCGLTC, (char *)&defltc);
18324726Smckusick 	signal(SIGINT, SIG_IGN);
18412990Ssam 	signal(SIGHUP, exit);
18512990Ssam 	signal(SIGQUIT, exit);
1869365Ssam 	child = fork();
1879365Ssam 	if (child == -1) {
1889365Ssam 		perror("rlogin: fork");
1899365Ssam 		done();
1909365Ssam 	}
1919365Ssam 	if (child == 0) {
19224726Smckusick 		mode(1);
19324726Smckusick 		sigsetmask(oldmask);
1949365Ssam 		reader();
19512155Ssam 		sleep(1);
19612155Ssam 		prf("\007Connection closed.");
1976444Swnj 		exit(3);
1986444Swnj 	}
19924726Smckusick 	signal(SIGURG, writeroob);
20024726Smckusick 	sigsetmask(oldmask);
20112990Ssam 	signal(SIGCHLD, catchild);
20218358Ssam 	if (!nosigwin)
20318358Ssam 		signal(SIGWINCH, sigwinch);
2049365Ssam 	writer();
20512155Ssam 	prf("Closed connection.");
2066444Swnj 	done();
2076444Swnj }
2086444Swnj 
2096444Swnj done()
2106444Swnj {
2116444Swnj 
2126444Swnj 	mode(0);
2139365Ssam 	if (child > 0 && kill(child, SIGKILL) >= 0)
2149365Ssam 		wait((int *)0);
2156444Swnj 	exit(0);
2166444Swnj }
2176444Swnj 
21824726Smckusick /*
21924726Smckusick  * This is called when the reader process gets the out-of-band (urgent)
22024726Smckusick  * request to turn on the window-changing protocol.
22124726Smckusick  */
22224726Smckusick writeroob()
22324726Smckusick {
22424726Smckusick 
22524726Smckusick 	dosigwinch = 1;
22624726Smckusick 	sendwindow();
22724726Smckusick }
22824726Smckusick 
22911803Sedward catchild()
23011803Sedward {
23111803Sedward 	union wait status;
23211803Sedward 	int pid;
23311803Sedward 
23411803Sedward again:
23511803Sedward 	pid = wait3(&status, WNOHANG|WUNTRACED, 0);
23611803Sedward 	if (pid == 0)
23711803Sedward 		return;
23811803Sedward 	/*
23911803Sedward 	 * if the child (reader) dies, just quit
24011803Sedward 	 */
24111803Sedward 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
24211803Sedward 		done();
24311803Sedward 	goto again;
24411803Sedward }
24511803Sedward 
2466444Swnj /*
2479365Ssam  * writer: write to remote: 0 -> line.
2489365Ssam  * ~.	terminate
2499365Ssam  * ~^Z	suspend rlogin process.
25010415Ssam  * ~^Y  suspend rlogin process, but leave reader alone.
2516444Swnj  */
2529365Ssam writer()
2536444Swnj {
25423530Sbloom 	char c;
25511803Sedward 	register n;
25623530Sbloom 	register bol = 1;               /* beginning of line */
25723530Sbloom 	register local = 0;
2586444Swnj 
25911803Sedward 	for (;;) {
26011803Sedward 		n = read(0, &c, 1);
26118358Ssam 		if (n <= 0) {
26218358Ssam 			if (n < 0 && errno == EINTR)
26318358Ssam 				continue;
26411803Sedward 			break;
26518358Ssam 		}
2669365Ssam 		/*
2679365Ssam 		 * If we're at the beginning of the line
2689365Ssam 		 * and recognize a command character, then
2699365Ssam 		 * we echo locally.  Otherwise, characters
2709365Ssam 		 * are echo'd remotely.  If the command
2719365Ssam 		 * character is doubled, this acts as a
2729365Ssam 		 * force and local echo is suppressed.
2739365Ssam 		 */
27423530Sbloom 		if (bol) {
27523530Sbloom 			bol = 0;
27623530Sbloom 			if (c == cmdchar) {
27723530Sbloom 				bol = 0;
27823530Sbloom 				local = 1;
27923530Sbloom 				continue;
2806444Swnj 			}
28123530Sbloom 		} else if (local) {
28223530Sbloom 			local = 0;
28323530Sbloom 			if (c == '.' || c == deftc.t_eofc) {
28423530Sbloom 				echo(c);
28523530Sbloom 				break;
2866444Swnj 			}
28723530Sbloom 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
28823530Sbloom 				bol = 1;
28923530Sbloom 				echo(c);
29023530Sbloom 				stop(c);
29123530Sbloom 				continue;
29223530Sbloom 			}
29323530Sbloom 			if (c != cmdchar)
29423530Sbloom 				write(rem, &cmdchar, 1);
2956444Swnj 		}
29623530Sbloom 		if (write(rem, &c, 1) == 0) {
29723530Sbloom 			prf("line gone");
29823530Sbloom 			break;
2996444Swnj 		}
30023530Sbloom 		bol = c == defkill || c == deftc.t_eofc ||
30123530Sbloom 		    c == '\r' || c == '\n';
3026444Swnj 	}
3036444Swnj }
3046444Swnj 
30523530Sbloom echo(c)
30623530Sbloom register char c;
30723530Sbloom {
30823530Sbloom 	char buf[8];
30923530Sbloom 	register char *p = buf;
31023530Sbloom 
31123530Sbloom 	c &= 0177;
31223530Sbloom 	*p++ = cmdchar;
31323530Sbloom 	if (c < ' ') {
31423530Sbloom 		*p++ = '^';
31523530Sbloom 		*p++ = c + '@';
31623530Sbloom 	} else if (c == 0177) {
31723530Sbloom 		*p++ = '^';
31823530Sbloom 		*p++ = '?';
31923530Sbloom 	} else
32023530Sbloom 		*p++ = c;
32123530Sbloom 	*p++ = '\r';
32223530Sbloom 	*p++ = '\n';
32323530Sbloom 	write(1, buf, p - buf);
32423530Sbloom }
32523530Sbloom 
32618358Ssam stop(cmdc)
32718358Ssam 	char cmdc;
32818358Ssam {
32918358Ssam 	mode(0);
33018358Ssam 	signal(SIGCHLD, SIG_IGN);
33118358Ssam 	kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
33218358Ssam 	signal(SIGCHLD, catchild);
33318358Ssam 	mode(1);
33418358Ssam 	sigwinch();			/* check for size changes */
33518358Ssam }
33618358Ssam 
33718358Ssam sigwinch()
33818358Ssam {
33918358Ssam 	struct winsize ws;
34018358Ssam 
34124726Smckusick 	if (dosigwinch && !nosigwin && ioctl(0, TIOCGWINSZ, &ws) == 0 &&
34218358Ssam 	    bcmp(&ws, &winsize, sizeof (ws))) {
34318358Ssam 		winsize = ws;
34424726Smckusick 		sendwindow();
34518358Ssam 	}
34618358Ssam }
34718358Ssam 
34824726Smckusick /*
34924726Smckusick  * Send the window size to the server via the magic escape
35024726Smckusick  */
35124726Smckusick sendwindow()
35224726Smckusick {
35324726Smckusick 	char obuf[4 + sizeof (struct winsize)];
35424726Smckusick 	struct winsize *wp = (struct winsize *)(obuf+4);
35524726Smckusick 
35624726Smckusick 	obuf[0] = 0377;
35724726Smckusick 	obuf[1] = 0377;
35824726Smckusick 	obuf[2] = 's';
35924726Smckusick 	obuf[3] = 's';
36024726Smckusick 	wp->ws_row = htons(winsize.ws_row);
36124726Smckusick 	wp->ws_col = htons(winsize.ws_col);
36224726Smckusick 	wp->ws_xpixel = htons(winsize.ws_xpixel);
36324726Smckusick 	wp->ws_ypixel = htons(winsize.ws_ypixel);
36424726Smckusick 	(void) write(rem, obuf, sizeof(obuf));
36524726Smckusick }
36624726Smckusick 
3676444Swnj oob()
3686444Swnj {
36910839Ssam 	int out = 1+1, atmark;
3709365Ssam 	char waste[BUFSIZ], mark;
37124726Smckusick 	struct sgttyb sb;
3726444Swnj 
3739365Ssam 	ioctl(1, TIOCFLUSH, (char *)&out);
3746444Swnj 	for (;;) {
37524726Smckusick 		int rv;
37610839Ssam 		if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
3776444Swnj 			perror("ioctl");
3786444Swnj 			break;
3796444Swnj 		}
38010839Ssam 		if (atmark)
3816444Swnj 			break;
38224726Smckusick 		rv = read(rem, waste, sizeof (waste));
38324726Smckusick 		if (rv <= 0)
38423230Sbloom 			break;
3856444Swnj 	}
38612990Ssam 	recv(rem, &mark, 1, MSG_OOB);
38724726Smckusick 	if (mark & TIOCPKT_WINDOW) {
38824726Smckusick 		/*
38924726Smckusick 		 * Let server know about window size changes
39024726Smckusick 		 */
39124726Smckusick 		kill(getppid(), SIGURG);
39224726Smckusick 	}
39324726Smckusick 	if (eight)
39424726Smckusick 		return;
3956444Swnj 	if (mark & TIOCPKT_NOSTOP) {
39624726Smckusick 		ioctl(0, TIOCGETP, (char *)&sb);
39724726Smckusick 		sb.sg_flags &= ~CBREAK;
39824726Smckusick 		sb.sg_flags |= RAW;
39924726Smckusick 		ioctl(0, TIOCSETN, (char *)&sb);
40013075Ssam 		notc.t_stopc = -1;
40113075Ssam 		notc.t_startc = -1;
40213075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
4036444Swnj 	}
4046444Swnj 	if (mark & TIOCPKT_DOSTOP) {
40524726Smckusick 		ioctl(0, TIOCGETP, (char *)&sb);
40624726Smckusick 		sb.sg_flags &= ~RAW;
40724726Smckusick 		sb.sg_flags |= CBREAK;
40824726Smckusick 		ioctl(0, TIOCSETN, (char *)&sb);
40913075Ssam 		notc.t_stopc = deftc.t_stopc;
41013075Ssam 		notc.t_startc = deftc.t_startc;
41113075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
4126444Swnj 	}
4136444Swnj }
4146444Swnj 
4159365Ssam /*
4169365Ssam  * reader: read from remote: line -> 1
4179365Ssam  */
4189365Ssam reader()
4196444Swnj {
4209365Ssam 	char rb[BUFSIZ];
4219365Ssam 	register int cnt;
4226444Swnj 
42323530Sbloom 	signal(SIGTTOU, SIG_IGN);
424*24727Smckusick 	{ int pid = getpid();
425*24727Smckusick 	  fcntl(rem, F_SETOWN, pid); }
4266444Swnj 	for (;;) {
4279365Ssam 		cnt = read(rem, rb, sizeof (rb));
42811396Ssam 		if (cnt == 0)
42911396Ssam 			break;
43011396Ssam 		if (cnt < 0) {
4319365Ssam 			if (errno == EINTR)
4326444Swnj 				continue;
4336444Swnj 			break;
4346444Swnj 		}
4359365Ssam 		write(1, rb, cnt);
4366444Swnj 	}
4376444Swnj }
4386444Swnj 
4396444Swnj mode(f)
4406444Swnj {
44113075Ssam 	struct tchars *tc;
44213075Ssam 	struct ltchars *ltc;
44313075Ssam 	struct sgttyb sb;
44421583Sbloom 	int	lflags;
4459365Ssam 
44613075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
44721583Sbloom 	ioctl(0, TIOCLGET, (char *)&lflags);
4489962Ssam 	switch (f) {
4499962Ssam 
4509962Ssam 	case 0:
45113075Ssam 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
45213075Ssam 		sb.sg_flags |= defflags|tabflag;
4539962Ssam 		tc = &deftc;
45413075Ssam 		ltc = &defltc;
45513075Ssam 		sb.sg_kill = defkill;
45613075Ssam 		sb.sg_erase = deferase;
45721583Sbloom 		lflags = deflflags;
4589962Ssam 		break;
4599962Ssam 
4609962Ssam 	case 1:
46113075Ssam 		sb.sg_flags |= (eight ? RAW : CBREAK);
46213075Ssam 		sb.sg_flags &= ~defflags;
46312155Ssam 		/* preserve tab delays, but turn off XTABS */
46413075Ssam 		if ((sb.sg_flags & TBDELAY) == XTABS)
46513075Ssam 			sb.sg_flags &= ~TBDELAY;
4669962Ssam 		tc = &notc;
46713075Ssam 		ltc = &noltc;
46813075Ssam 		sb.sg_kill = sb.sg_erase = -1;
46921583Sbloom 		if (litout)
47021583Sbloom 			lflags |= LLITOUT;
4719962Ssam 		break;
4729962Ssam 
4739962Ssam 	default:
4749962Ssam 		return;
4756444Swnj 	}
47613075Ssam 	ioctl(0, TIOCSLTC, (char *)ltc);
47713075Ssam 	ioctl(0, TIOCSETC, (char *)tc);
47813075Ssam 	ioctl(0, TIOCSETN, (char *)&sb);
47921583Sbloom 	ioctl(0, TIOCLSET, (char *)&lflags);
4806444Swnj }
4816444Swnj 
4829365Ssam /*VARARGS*/
48324726Smckusick prf(f, a1, a2, a3, a4, a5)
4849365Ssam 	char *f;
4856444Swnj {
48624726Smckusick 	fprintf(stderr, f, a1, a2, a3, a4, a5);
4876444Swnj 	fprintf(stderr, CRLF);
4886444Swnj }
4896444Swnj 
4909365Ssam lostpeer()
4916444Swnj {
49212990Ssam 	signal(SIGPIPE, SIG_IGN);
49312155Ssam 	prf("\007Connection closed.");
4949365Ssam 	done();
4956444Swnj }
496