xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 24725)
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*24725Smckusick static char sccsid[] = "@(#)rlogin.c	5.3.1.1 (Berkeley) 09/12/85";
1521595Sdist #endif not lint
1621595Sdist 
1712990Ssam /*
1812990Ssam  * rlogin - remote login
1912990Ssam  */
206444Swnj #include <sys/types.h>
216444Swnj #include <sys/socket.h>
2213620Ssam #include <sys/wait.h>
239365Ssam 
249207Ssam #include <netinet/in.h>
259365Ssam 
269365Ssam #include <stdio.h>
279365Ssam #include <sgtty.h>
286444Swnj #include <errno.h>
296444Swnj #include <pwd.h>
309365Ssam #include <signal.h>
319365Ssam #include <netdb.h>
3218358Ssam #include <setjmp.h>
336444Swnj 
346444Swnj char	*index(), *rindex(), *malloc(), *getenv();
356444Swnj struct	passwd *getpwuid();
369365Ssam char	*name;
376444Swnj int	rem;
386444Swnj char	cmdchar = '~';
396444Swnj int	eight;
4021583Sbloom int	litout;
416444Swnj char	*speeds[] =
426444Swnj     { "0", "50", "75", "110", "134", "150", "200", "300",
436444Swnj       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
4418358Ssam char	term[256] = "network";
459365Ssam extern	int errno;
469365Ssam int	lostpeer();
4718358Ssam int	nosigwin;
4818358Ssam jmp_buf	winsizechanged;
4918358Ssam struct	winsize winsize;
5018358Ssam int	sigwinch();
516444Swnj 
526444Swnj main(argc, argv)
536444Swnj 	int argc;
546444Swnj 	char **argv;
556444Swnj {
569365Ssam 	char *host, *cp;
576444Swnj 	struct sgttyb ttyb;
586444Swnj 	struct passwd *pwd;
599365Ssam 	struct servent *sp;
6010415Ssam 	int uid, options = 0;
6117449Slepreau 	int on = 1;
626444Swnj 
636444Swnj 	host = rindex(argv[0], '/');
646444Swnj 	if (host)
656444Swnj 		host++;
666444Swnj 	else
676444Swnj 		host = argv[0];
686444Swnj 	argv++, --argc;
696444Swnj 	if (!strcmp(host, "rlogin"))
706444Swnj 		host = *argv++, --argc;
716444Swnj another:
7210839Ssam 	if (argc > 0 && !strcmp(*argv, "-d")) {
736444Swnj 		argv++, argc--;
7410415Ssam 		options |= SO_DEBUG;
756444Swnj 		goto another;
766444Swnj 	}
7710839Ssam 	if (argc > 0 && !strcmp(*argv, "-l")) {
786444Swnj 		argv++, argc--;
796444Swnj 		if (argc == 0)
806444Swnj 			goto usage;
816444Swnj 		name = *argv++; argc--;
826444Swnj 		goto another;
836444Swnj 	}
8410839Ssam 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
856444Swnj 		cmdchar = argv[0][2];
866444Swnj 		argv++, argc--;
876444Swnj 		goto another;
886444Swnj 	}
8910839Ssam 	if (argc > 0 && !strcmp(*argv, "-8")) {
906444Swnj 		eight = 1;
916444Swnj 		argv++, argc--;
926444Swnj 		goto another;
936444Swnj 	}
9421583Sbloom 	if (argc > 0 && !strcmp(*argv, "-L")) {
9521583Sbloom 		litout = 1;
9621583Sbloom 		argv++, argc--;
9721583Sbloom 		goto another;
9821583Sbloom 	}
9918358Ssam 	if (argc > 0 && !strcmp(*argv, "-w")) {
10018358Ssam 		nosigwin++;
10118358Ssam 		argv++, argc--;
10218358Ssam 		goto another;
10318358Ssam 	}
1046444Swnj 	if (host == 0)
1056444Swnj 		goto usage;
1066444Swnj 	if (argc > 0)
1076444Swnj 		goto usage;
1086444Swnj 	pwd = getpwuid(getuid());
1096444Swnj 	if (pwd == 0) {
1106444Swnj 		fprintf(stderr, "Who are you?\n");
1116444Swnj 		exit(1);
1126444Swnj 	}
1139365Ssam 	sp = getservbyname("login", "tcp");
1149365Ssam 	if (sp == 0) {
1159365Ssam 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
1169365Ssam 		exit(2);
1179365Ssam 	}
1189241Ssam 	cp = getenv("TERM");
1199241Ssam 	if (cp)
1209241Ssam 		strcpy(term, cp);
12118358Ssam 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
1226444Swnj 		strcat(term, "/");
1236444Swnj 		strcat(term, speeds[ttyb.sg_ospeed]);
1246444Swnj 	}
12518358Ssam 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &winsize) == 0) {
12618358Ssam 		cp = index(term, '\0');
12718358Ssam 		sprintf(cp, "/%u,%u,%u,%u", winsize.ws_row, winsize.ws_col,
12818358Ssam 		    winsize.ws_xpixel, winsize.ws_ypixel);
12918358Ssam 	}
13012990Ssam 	signal(SIGPIPE, lostpeer);
1319365Ssam         rem = rcmd(&host, sp->s_port, pwd->pw_name,
1326444Swnj 	    name ? name : pwd->pw_name, term, 0);
1336444Swnj         if (rem < 0)
1346444Swnj                 exit(1);
13510415Ssam 	if (options & SO_DEBUG &&
13617449Slepreau 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
13710415Ssam 		perror("rlogin: setsockopt (SO_DEBUG)");
1389365Ssam 	uid = getuid();
1399365Ssam 	if (setuid(uid) < 0) {
1409365Ssam 		perror("rlogin: setuid");
1419365Ssam 		exit(1);
1429365Ssam 	}
1439365Ssam 	doit();
1449365Ssam 	/*NOTREACHED*/
1456444Swnj usage:
1466444Swnj 	fprintf(stderr,
14721583Sbloom 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ] [ -w ]\n");
1486444Swnj 	exit(1);
1496444Swnj }
1506444Swnj 
1516444Swnj #define CRLF "\r\n"
1526444Swnj 
1539365Ssam int	child;
15411803Sedward int	catchild();
1559365Ssam 
15613075Ssam int	defflags, tabflag;
15721583Sbloom int	deflflags;
15813075Ssam char	deferase, defkill;
15913075Ssam struct	tchars deftc;
16013075Ssam struct	ltchars defltc;
16113075Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
16213075Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
1636444Swnj 
1649365Ssam doit()
1656444Swnj {
1666444Swnj 	int exit();
16713075Ssam 	struct sgttyb sb;
1686444Swnj 
16913075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
17013075Ssam 	defflags = sb.sg_flags;
17112155Ssam 	tabflag = defflags & TBDELAY;
1729962Ssam 	defflags &= ECHO | CRMOD;
17313075Ssam 	deferase = sb.sg_erase;
17413075Ssam 	defkill = sb.sg_kill;
17521583Sbloom 	ioctl(0, TIOCLGET, (char *)&deflflags);
17613075Ssam 	ioctl(0, TIOCGETC, (char *)&deftc);
17713075Ssam 	notc.t_startc = deftc.t_startc;
17813075Ssam 	notc.t_stopc = deftc.t_stopc;
17913075Ssam 	ioctl(0, TIOCGLTC, (char *)&defltc);
18012990Ssam 	signal(SIGINT, exit);
18112990Ssam 	signal(SIGHUP, exit);
18212990Ssam 	signal(SIGQUIT, exit);
1839365Ssam 	child = fork();
1849365Ssam 	if (child == -1) {
1859365Ssam 		perror("rlogin: fork");
1869365Ssam 		done();
1879365Ssam 	}
18812990Ssam 	signal(SIGINT, SIG_IGN);
18911803Sedward 	mode(1);
1909365Ssam 	if (child == 0) {
1919365Ssam 		reader();
19212155Ssam 		sleep(1);
19312155Ssam 		prf("\007Connection closed.");
1946444Swnj 		exit(3);
1956444Swnj 	}
19612990Ssam 	signal(SIGCHLD, catchild);
19718358Ssam 	if (!nosigwin)
19818358Ssam 		signal(SIGWINCH, sigwinch);
1999365Ssam 	writer();
20012155Ssam 	prf("Closed connection.");
2016444Swnj 	done();
2026444Swnj }
2036444Swnj 
2046444Swnj done()
2056444Swnj {
2066444Swnj 
2076444Swnj 	mode(0);
2089365Ssam 	if (child > 0 && kill(child, SIGKILL) >= 0)
2099365Ssam 		wait((int *)0);
2106444Swnj 	exit(0);
2116444Swnj }
2126444Swnj 
21311803Sedward catchild()
21411803Sedward {
21511803Sedward 	union wait status;
21611803Sedward 	int pid;
21711803Sedward 
21811803Sedward again:
21911803Sedward 	pid = wait3(&status, WNOHANG|WUNTRACED, 0);
22011803Sedward 	if (pid == 0)
22111803Sedward 		return;
22211803Sedward 	/*
22311803Sedward 	 * if the child (reader) dies, just quit
22411803Sedward 	 */
22511803Sedward 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
22611803Sedward 		done();
22711803Sedward 	goto again;
22811803Sedward }
22911803Sedward 
2306444Swnj /*
2319365Ssam  * writer: write to remote: 0 -> line.
2329365Ssam  * ~.	terminate
2339365Ssam  * ~^Z	suspend rlogin process.
23410415Ssam  * ~^Y  suspend rlogin process, but leave reader alone.
2356444Swnj  */
2369365Ssam writer()
2376444Swnj {
23823530Sbloom 	char c;
23911803Sedward 	register n;
24023530Sbloom 	register bol = 1;               /* beginning of line */
24123530Sbloom 	register local = 0;
2426444Swnj 
24318358Ssam 	/*
24418358Ssam 	 * Handle SIGWINCH's with in-band signaling of new
24518358Ssam 	 * window size.  It seems reasonable that we flush
24618358Ssam 	 * pending input and not force out of band signal
24718358Ssam 	 * as this most likely will occur from an input device
24818358Ssam 	 * other than the keyboard (e.g. a mouse).
24918358Ssam 	 *
25018358Ssam 	 * The hack of using 0377 to signal an in-band signal
25118358Ssam 	 * is pretty bad, but otherwise we'd be forced to
25218358Ssam 	 * either get complicated (use MSG_OOB) or go to a
25318358Ssam 	 * serious (telnet-style) protocol.
25418358Ssam 	 */
25518358Ssam 	if (setjmp(winsizechanged)) {
25623530Sbloom 		char obuf[4 + sizeof (struct winsize)];
25718358Ssam 		struct winsize *wp = (struct winsize *)(obuf+4);
25818358Ssam 
25918358Ssam 		obuf[0] = 0377;			/* XXX */
26018358Ssam 		obuf[1] = 0377;			/* XXX */
26118358Ssam 		obuf[2] = 's';			/* XXX */
26218358Ssam 		obuf[3] = 's';			/* XXX */
26318358Ssam 		wp->ws_row = htons(winsize.ws_row);
26418358Ssam 		wp->ws_col = htons(winsize.ws_col);
26518358Ssam 		wp->ws_xpixel = htons(winsize.ws_xpixel);
26618358Ssam 		wp->ws_ypixel = htons(winsize.ws_ypixel);
26718358Ssam 		(void) write(rem, obuf, 4+sizeof (*wp));
26818358Ssam 	}
26923530Sbloom 
27011803Sedward 	for (;;) {
27111803Sedward 		n = read(0, &c, 1);
27218358Ssam 		if (n <= 0) {
27318358Ssam 			if (n < 0 && errno == EINTR)
27418358Ssam 				continue;
27511803Sedward 			break;
27618358Ssam 		}
27718358Ssam 		if (!eight)
2789365Ssam 			c &= 0177;
2799365Ssam 		/*
2809365Ssam 		 * If we're at the beginning of the line
2819365Ssam 		 * and recognize a command character, then
2829365Ssam 		 * we echo locally.  Otherwise, characters
2839365Ssam 		 * are echo'd remotely.  If the command
2849365Ssam 		 * character is doubled, this acts as a
2859365Ssam 		 * force and local echo is suppressed.
2869365Ssam 		 */
28723530Sbloom 		if (bol) {
28823530Sbloom 			bol = 0;
28923530Sbloom 			if (c == cmdchar) {
29023530Sbloom 				bol = 0;
29123530Sbloom 				local = 1;
29223530Sbloom 				continue;
2936444Swnj 			}
29423530Sbloom 		} else if (local) {
29523530Sbloom 			local = 0;
29623530Sbloom 			if (c == '.' || c == deftc.t_eofc) {
29723530Sbloom 				echo(c);
29823530Sbloom 				break;
2996444Swnj 			}
30023530Sbloom 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
30123530Sbloom 				bol = 1;
30223530Sbloom 				echo(c);
30323530Sbloom 				stop(c);
30423530Sbloom 				continue;
30523530Sbloom 			}
30623530Sbloom 			if (c != cmdchar)
30723530Sbloom 				write(rem, &cmdchar, 1);
3086444Swnj 		}
30923530Sbloom 		if (write(rem, &c, 1) == 0) {
31023530Sbloom 			prf("line gone");
31123530Sbloom 			break;
3126444Swnj 		}
31323530Sbloom 		bol = c == defkill || c == deftc.t_eofc ||
31423530Sbloom 		    c == '\r' || c == '\n';
3156444Swnj 	}
3166444Swnj }
3176444Swnj 
31823530Sbloom echo(c)
31923530Sbloom register char c;
32023530Sbloom {
32123530Sbloom 	char buf[8];
32223530Sbloom 	register char *p = buf;
32323530Sbloom 
32423530Sbloom 	c &= 0177;
32523530Sbloom 	*p++ = cmdchar;
32623530Sbloom 	if (c < ' ') {
32723530Sbloom 		*p++ = '^';
32823530Sbloom 		*p++ = c + '@';
32923530Sbloom 	} else if (c == 0177) {
33023530Sbloom 		*p++ = '^';
33123530Sbloom 		*p++ = '?';
33223530Sbloom 	} else
33323530Sbloom 		*p++ = c;
33423530Sbloom 	*p++ = '\r';
33523530Sbloom 	*p++ = '\n';
33623530Sbloom 	write(1, buf, p - buf);
33723530Sbloom }
33823530Sbloom 
33918358Ssam stop(cmdc)
34018358Ssam 	char cmdc;
34118358Ssam {
34218358Ssam 	struct winsize ws;
34318358Ssam 
34418358Ssam 	mode(0);
34518358Ssam 	signal(SIGCHLD, SIG_IGN);
34618358Ssam 	kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
34718358Ssam 	signal(SIGCHLD, catchild);
34818358Ssam 	mode(1);
34918358Ssam 	sigwinch();			/* check for size changes */
35018358Ssam }
35118358Ssam 
35218358Ssam sigwinch()
35318358Ssam {
35418358Ssam 	struct winsize ws;
35518358Ssam 
35618358Ssam 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &ws) == 0 &&
35718358Ssam 	    bcmp(&ws, &winsize, sizeof (ws))) {
35818358Ssam 		winsize = ws;
35918358Ssam 		longjmp(winsizechanged, 1);
36018358Ssam 	}
36118358Ssam }
36218358Ssam 
3636444Swnj oob()
3646444Swnj {
36510839Ssam 	int out = 1+1, atmark;
3669365Ssam 	char waste[BUFSIZ], mark;
3676444Swnj 
3689365Ssam 	ioctl(1, TIOCFLUSH, (char *)&out);
3696444Swnj 	for (;;) {
37010839Ssam 		if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
3716444Swnj 			perror("ioctl");
3726444Swnj 			break;
3736444Swnj 		}
37410839Ssam 		if (atmark)
3756444Swnj 			break;
37623230Sbloom 		if (read(rem, waste, sizeof (waste)) <= 0)
37723230Sbloom 			break;
3786444Swnj 	}
37912990Ssam 	recv(rem, &mark, 1, MSG_OOB);
3806444Swnj 	if (mark & TIOCPKT_NOSTOP) {
38113075Ssam 		notc.t_stopc = -1;
38213075Ssam 		notc.t_startc = -1;
38313075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
3846444Swnj 	}
3856444Swnj 	if (mark & TIOCPKT_DOSTOP) {
38613075Ssam 		notc.t_stopc = deftc.t_stopc;
38713075Ssam 		notc.t_startc = deftc.t_startc;
38813075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
3896444Swnj 	}
3906444Swnj }
3916444Swnj 
3929365Ssam /*
3939365Ssam  * reader: read from remote: line -> 1
3949365Ssam  */
3959365Ssam reader()
3966444Swnj {
3979365Ssam 	char rb[BUFSIZ];
3989365Ssam 	register int cnt;
3996444Swnj 
40012990Ssam 	signal(SIGURG, oob);
40123530Sbloom 	signal(SIGTTOU, SIG_IGN);
402*24725Smckusick 	{ int pid = -getpid();
403*24725Smckusick 	  ioctl(rem, SIOCSPGRP, (char *)&pid); }
4046444Swnj 	for (;;) {
4059365Ssam 		cnt = read(rem, rb, sizeof (rb));
40611396Ssam 		if (cnt == 0)
40711396Ssam 			break;
40811396Ssam 		if (cnt < 0) {
4099365Ssam 			if (errno == EINTR)
4106444Swnj 				continue;
4116444Swnj 			break;
4126444Swnj 		}
4139365Ssam 		write(1, rb, cnt);
4146444Swnj 	}
4156444Swnj }
4166444Swnj 
4176444Swnj mode(f)
4186444Swnj {
41913075Ssam 	struct tchars *tc;
42013075Ssam 	struct ltchars *ltc;
42113075Ssam 	struct sgttyb sb;
42221583Sbloom 	int	lflags;
4239365Ssam 
42413075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
42521583Sbloom 	ioctl(0, TIOCLGET, (char *)&lflags);
4269962Ssam 	switch (f) {
4279962Ssam 
4289962Ssam 	case 0:
42913075Ssam 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
43013075Ssam 		sb.sg_flags |= defflags|tabflag;
4319962Ssam 		tc = &deftc;
43213075Ssam 		ltc = &defltc;
43313075Ssam 		sb.sg_kill = defkill;
43413075Ssam 		sb.sg_erase = deferase;
43521583Sbloom 		lflags = deflflags;
4369962Ssam 		break;
4379962Ssam 
4389962Ssam 	case 1:
43913075Ssam 		sb.sg_flags |= (eight ? RAW : CBREAK);
44013075Ssam 		sb.sg_flags &= ~defflags;
44112155Ssam 		/* preserve tab delays, but turn off XTABS */
44213075Ssam 		if ((sb.sg_flags & TBDELAY) == XTABS)
44313075Ssam 			sb.sg_flags &= ~TBDELAY;
4449962Ssam 		tc = &notc;
44513075Ssam 		ltc = &noltc;
44613075Ssam 		sb.sg_kill = sb.sg_erase = -1;
44721583Sbloom 		if (litout)
44821583Sbloom 			lflags |= LLITOUT;
4499962Ssam 		break;
4509962Ssam 
4519962Ssam 	default:
4529962Ssam 		return;
4536444Swnj 	}
45413075Ssam 	ioctl(0, TIOCSLTC, (char *)ltc);
45513075Ssam 	ioctl(0, TIOCSETC, (char *)tc);
45613075Ssam 	ioctl(0, TIOCSETN, (char *)&sb);
45721583Sbloom 	ioctl(0, TIOCLSET, (char *)&lflags);
4586444Swnj }
4596444Swnj 
4609365Ssam /*VARARGS*/
4616444Swnj prf(f, a1, a2, a3)
4629365Ssam 	char *f;
4636444Swnj {
4646444Swnj 	fprintf(stderr, f, a1, a2, a3);
4656444Swnj 	fprintf(stderr, CRLF);
4666444Swnj }
4676444Swnj 
4689365Ssam lostpeer()
4696444Swnj {
47012990Ssam 	signal(SIGPIPE, SIG_IGN);
47112155Ssam 	prf("\007Connection closed.");
4729365Ssam 	done();
4736444Swnj }
474