xref: /csrg-svn/usr.bin/rlogin/rlogin.c (revision 21583)
16444Swnj #ifndef lint
2*21583Sbloom static char sccsid[] = "@(#)rlogin.c	4.18 (Berkeley) 85/05/31";
36444Swnj #endif
46444Swnj 
512990Ssam /*
612990Ssam  * rlogin - remote login
712990Ssam  */
86444Swnj #include <sys/types.h>
96444Swnj #include <sys/socket.h>
1013620Ssam #include <sys/wait.h>
119365Ssam 
129207Ssam #include <netinet/in.h>
139365Ssam 
149365Ssam #include <stdio.h>
159365Ssam #include <sgtty.h>
166444Swnj #include <errno.h>
176444Swnj #include <pwd.h>
189365Ssam #include <signal.h>
199365Ssam #include <netdb.h>
2018358Ssam #include <setjmp.h>
216444Swnj 
226444Swnj char	*index(), *rindex(), *malloc(), *getenv();
236444Swnj struct	passwd *getpwuid();
249365Ssam char	*name;
256444Swnj int	rem;
266444Swnj char	cmdchar = '~';
276444Swnj int	eight;
28*21583Sbloom int	litout;
296444Swnj char	*speeds[] =
306444Swnj     { "0", "50", "75", "110", "134", "150", "200", "300",
316444Swnj       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
3218358Ssam char	term[256] = "network";
339365Ssam extern	int errno;
349365Ssam int	lostpeer();
3518358Ssam int	nosigwin;
3618358Ssam jmp_buf	winsizechanged;
3718358Ssam struct	winsize winsize;
3818358Ssam int	sigwinch();
396444Swnj 
406444Swnj main(argc, argv)
416444Swnj 	int argc;
426444Swnj 	char **argv;
436444Swnj {
449365Ssam 	char *host, *cp;
456444Swnj 	struct sgttyb ttyb;
466444Swnj 	struct passwd *pwd;
479365Ssam 	struct servent *sp;
4810415Ssam 	int uid, options = 0;
4917449Slepreau 	int on = 1;
506444Swnj 
516444Swnj 	host = rindex(argv[0], '/');
526444Swnj 	if (host)
536444Swnj 		host++;
546444Swnj 	else
556444Swnj 		host = argv[0];
566444Swnj 	argv++, --argc;
576444Swnj 	if (!strcmp(host, "rlogin"))
586444Swnj 		host = *argv++, --argc;
596444Swnj another:
6010839Ssam 	if (argc > 0 && !strcmp(*argv, "-d")) {
616444Swnj 		argv++, argc--;
6210415Ssam 		options |= SO_DEBUG;
636444Swnj 		goto another;
646444Swnj 	}
6510839Ssam 	if (argc > 0 && !strcmp(*argv, "-l")) {
666444Swnj 		argv++, argc--;
676444Swnj 		if (argc == 0)
686444Swnj 			goto usage;
696444Swnj 		name = *argv++; argc--;
706444Swnj 		goto another;
716444Swnj 	}
7210839Ssam 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
736444Swnj 		cmdchar = argv[0][2];
746444Swnj 		argv++, argc--;
756444Swnj 		goto another;
766444Swnj 	}
7710839Ssam 	if (argc > 0 && !strcmp(*argv, "-8")) {
786444Swnj 		eight = 1;
796444Swnj 		argv++, argc--;
806444Swnj 		goto another;
816444Swnj 	}
82*21583Sbloom 	if (argc > 0 && !strcmp(*argv, "-L")) {
83*21583Sbloom 		litout = 1;
84*21583Sbloom 		argv++, argc--;
85*21583Sbloom 		goto another;
86*21583Sbloom 	}
8718358Ssam 	if (argc > 0 && !strcmp(*argv, "-w")) {
8818358Ssam 		nosigwin++;
8918358Ssam 		argv++, argc--;
9018358Ssam 		goto another;
9118358Ssam 	}
926444Swnj 	if (host == 0)
936444Swnj 		goto usage;
946444Swnj 	if (argc > 0)
956444Swnj 		goto usage;
966444Swnj 	pwd = getpwuid(getuid());
976444Swnj 	if (pwd == 0) {
986444Swnj 		fprintf(stderr, "Who are you?\n");
996444Swnj 		exit(1);
1006444Swnj 	}
1019365Ssam 	sp = getservbyname("login", "tcp");
1029365Ssam 	if (sp == 0) {
1039365Ssam 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
1049365Ssam 		exit(2);
1059365Ssam 	}
1069241Ssam 	cp = getenv("TERM");
1079241Ssam 	if (cp)
1089241Ssam 		strcpy(term, cp);
10918358Ssam 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
1106444Swnj 		strcat(term, "/");
1116444Swnj 		strcat(term, speeds[ttyb.sg_ospeed]);
1126444Swnj 	}
11318358Ssam 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &winsize) == 0) {
11418358Ssam 		cp = index(term, '\0');
11518358Ssam 		sprintf(cp, "/%u,%u,%u,%u", winsize.ws_row, winsize.ws_col,
11618358Ssam 		    winsize.ws_xpixel, winsize.ws_ypixel);
11718358Ssam 	}
11812990Ssam 	signal(SIGPIPE, lostpeer);
1199365Ssam         rem = rcmd(&host, sp->s_port, pwd->pw_name,
1206444Swnj 	    name ? name : pwd->pw_name, term, 0);
1216444Swnj         if (rem < 0)
1226444Swnj                 exit(1);
12310415Ssam 	if (options & SO_DEBUG &&
12417449Slepreau 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
12510415Ssam 		perror("rlogin: setsockopt (SO_DEBUG)");
1269365Ssam 	uid = getuid();
1279365Ssam 	if (setuid(uid) < 0) {
1289365Ssam 		perror("rlogin: setuid");
1299365Ssam 		exit(1);
1309365Ssam 	}
1319365Ssam 	doit();
1329365Ssam 	/*NOTREACHED*/
1336444Swnj usage:
1346444Swnj 	fprintf(stderr,
135*21583Sbloom 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ] [ -w ]\n");
1366444Swnj 	exit(1);
1376444Swnj }
1386444Swnj 
1396444Swnj #define CRLF "\r\n"
1406444Swnj 
1419365Ssam int	child;
14211803Sedward int	catchild();
1439365Ssam 
14413075Ssam int	defflags, tabflag;
145*21583Sbloom int	deflflags;
14613075Ssam char	deferase, defkill;
14713075Ssam struct	tchars deftc;
14813075Ssam struct	ltchars defltc;
14913075Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
15013075Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
1516444Swnj 
1529365Ssam doit()
1536444Swnj {
1546444Swnj 	int exit();
15513075Ssam 	struct sgttyb sb;
1566444Swnj 
15713075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
15813075Ssam 	defflags = sb.sg_flags;
15912155Ssam 	tabflag = defflags & TBDELAY;
1609962Ssam 	defflags &= ECHO | CRMOD;
16113075Ssam 	deferase = sb.sg_erase;
16213075Ssam 	defkill = sb.sg_kill;
163*21583Sbloom 	ioctl(0, TIOCLGET, (char *)&deflflags);
16413075Ssam 	ioctl(0, TIOCGETC, (char *)&deftc);
16513075Ssam 	notc.t_startc = deftc.t_startc;
16613075Ssam 	notc.t_stopc = deftc.t_stopc;
16713075Ssam 	ioctl(0, TIOCGLTC, (char *)&defltc);
16812990Ssam 	signal(SIGINT, exit);
16912990Ssam 	signal(SIGHUP, exit);
17012990Ssam 	signal(SIGQUIT, exit);
1719365Ssam 	child = fork();
1729365Ssam 	if (child == -1) {
1739365Ssam 		perror("rlogin: fork");
1749365Ssam 		done();
1759365Ssam 	}
17612990Ssam 	signal(SIGINT, SIG_IGN);
17711803Sedward 	mode(1);
1789365Ssam 	if (child == 0) {
1799365Ssam 		reader();
18012155Ssam 		sleep(1);
18112155Ssam 		prf("\007Connection closed.");
1826444Swnj 		exit(3);
1836444Swnj 	}
18412990Ssam 	signal(SIGCHLD, catchild);
18518358Ssam 	if (!nosigwin)
18618358Ssam 		signal(SIGWINCH, sigwinch);
1879365Ssam 	writer();
18812155Ssam 	prf("Closed connection.");
1896444Swnj 	done();
1906444Swnj }
1916444Swnj 
1926444Swnj done()
1936444Swnj {
1946444Swnj 
1956444Swnj 	mode(0);
1969365Ssam 	if (child > 0 && kill(child, SIGKILL) >= 0)
1979365Ssam 		wait((int *)0);
1986444Swnj 	exit(0);
1996444Swnj }
2006444Swnj 
20111803Sedward catchild()
20211803Sedward {
20311803Sedward 	union wait status;
20411803Sedward 	int pid;
20511803Sedward 
20611803Sedward again:
20711803Sedward 	pid = wait3(&status, WNOHANG|WUNTRACED, 0);
20811803Sedward 	if (pid == 0)
20911803Sedward 		return;
21011803Sedward 	/*
21111803Sedward 	 * if the child (reader) dies, just quit
21211803Sedward 	 */
21311803Sedward 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
21411803Sedward 		done();
21511803Sedward 	goto again;
21611803Sedward }
21711803Sedward 
2186444Swnj /*
2199365Ssam  * writer: write to remote: 0 -> line.
2209365Ssam  * ~.	terminate
2219365Ssam  * ~^Z	suspend rlogin process.
22210415Ssam  * ~^Y  suspend rlogin process, but leave reader alone.
2236444Swnj  */
2249365Ssam writer()
2256444Swnj {
22618358Ssam 	char obuf[600], c;
22718358Ssam 	register char *op;
22811803Sedward 	register n;
2296444Swnj 
23018358Ssam 	/*
23118358Ssam 	 * Handle SIGWINCH's with in-band signaling of new
23218358Ssam 	 * window size.  It seems reasonable that we flush
23318358Ssam 	 * pending input and not force out of band signal
23418358Ssam 	 * as this most likely will occur from an input device
23518358Ssam 	 * other than the keyboard (e.g. a mouse).
23618358Ssam 	 *
23718358Ssam 	 * The hack of using 0377 to signal an in-band signal
23818358Ssam 	 * is pretty bad, but otherwise we'd be forced to
23918358Ssam 	 * either get complicated (use MSG_OOB) or go to a
24018358Ssam 	 * serious (telnet-style) protocol.
24118358Ssam 	 */
24218358Ssam 	if (setjmp(winsizechanged)) {
24318358Ssam 		struct winsize *wp = (struct winsize *)(obuf+4);
24418358Ssam 
24518358Ssam 		obuf[0] = 0377;			/* XXX */
24618358Ssam 		obuf[1] = 0377;			/* XXX */
24718358Ssam 		obuf[2] = 's';			/* XXX */
24818358Ssam 		obuf[3] = 's';			/* XXX */
24918358Ssam 		wp->ws_row = htons(winsize.ws_row);
25018358Ssam 		wp->ws_col = htons(winsize.ws_col);
25118358Ssam 		wp->ws_xpixel = htons(winsize.ws_xpixel);
25218358Ssam 		wp->ws_ypixel = htons(winsize.ws_ypixel);
25318358Ssam 		(void) write(rem, obuf, 4+sizeof (*wp));
25418358Ssam 	}
2559365Ssam top:
25618358Ssam 	op = obuf;
25711803Sedward 	for (;;) {
2589365Ssam 		int local;
2599365Ssam 
26011803Sedward 		n = read(0, &c, 1);
26118358Ssam 		if (n <= 0) {
26218358Ssam 			if (n < 0 && errno == EINTR)
26318358Ssam 				continue;
26411803Sedward 			break;
26518358Ssam 		}
26618358Ssam 		if (!eight)
2679365Ssam 			c &= 0177;
2689365Ssam 		/*
2699365Ssam 		 * If we're at the beginning of the line
2709365Ssam 		 * and recognize a command character, then
2719365Ssam 		 * we echo locally.  Otherwise, characters
2729365Ssam 		 * are echo'd remotely.  If the command
2739365Ssam 		 * character is doubled, this acts as a
2749365Ssam 		 * force and local echo is suppressed.
2759365Ssam 		 */
27618358Ssam 		if (op == obuf)
2779365Ssam 			local = (c == cmdchar);
27818358Ssam 		if (op == obuf + 1 && *obuf == cmdchar)
2799365Ssam 			local = (c != cmdchar);
2809365Ssam 		if (!local) {
2819365Ssam 			if (write(rem, &c, 1) == 0) {
2829365Ssam 				prf("line gone");
2839365Ssam 				return;
2846444Swnj 			}
28518358Ssam 			if (!eight)
2869365Ssam 				c &= 0177;
2879365Ssam 		} else {
2889365Ssam 			if (c == '\r' || c == '\n') {
28918358Ssam 				char cmdc = obuf[1];
2906444Swnj 
29113075Ssam 				if (cmdc == '.' || cmdc == deftc.t_eofc) {
2929365Ssam 					write(0, CRLF, sizeof(CRLF));
2939365Ssam 					return;
2949962Ssam 				}
29513075Ssam 				if (cmdc == defltc.t_suspc ||
29613075Ssam 				    cmdc == defltc.t_dsuspc) {
29718358Ssam 					stop(cmdc);
2989365Ssam 					goto top;
2996444Swnj 				}
30018358Ssam 				*op++ = c;
30118358Ssam 				write(rem, obuf, op - obuf);
3029365Ssam 				goto top;
3036444Swnj 			}
3049365Ssam 			write(1, &c, 1);
3056444Swnj 		}
30618358Ssam 		*op++ = c;
30713075Ssam 		if (c == deferase) {
30818358Ssam 			op -= 2;
30918358Ssam 			if (op < obuf)
3109365Ssam 				goto top;
3116444Swnj 		}
31213075Ssam 		if (c == defkill || c == deftc.t_eofc ||
3139365Ssam 		    c == '\r' || c == '\n')
3149365Ssam 			goto top;
31518358Ssam 		if (op >= &obuf[sizeof (obuf)])
31618358Ssam 			op--;
3176444Swnj 	}
3186444Swnj }
3196444Swnj 
32018358Ssam stop(cmdc)
32118358Ssam 	char cmdc;
32218358Ssam {
32318358Ssam 	struct winsize ws;
32418358Ssam 
32518358Ssam 	write(0, CRLF, sizeof(CRLF));
32618358Ssam 	mode(0);
32718358Ssam 	signal(SIGCHLD, SIG_IGN);
32818358Ssam 	kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
32918358Ssam 	signal(SIGCHLD, catchild);
33018358Ssam 	mode(1);
33118358Ssam 	sigwinch();			/* check for size changes */
33218358Ssam }
33318358Ssam 
33418358Ssam sigwinch()
33518358Ssam {
33618358Ssam 	struct winsize ws;
33718358Ssam 
33818358Ssam 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &ws) == 0 &&
33918358Ssam 	    bcmp(&ws, &winsize, sizeof (ws))) {
34018358Ssam 		winsize = ws;
34118358Ssam 		longjmp(winsizechanged, 1);
34218358Ssam 	}
34318358Ssam }
34418358Ssam 
3456444Swnj oob()
3466444Swnj {
34710839Ssam 	int out = 1+1, atmark;
3489365Ssam 	char waste[BUFSIZ], mark;
3496444Swnj 
3509365Ssam 	ioctl(1, TIOCFLUSH, (char *)&out);
3516444Swnj 	for (;;) {
35210839Ssam 		if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
3536444Swnj 			perror("ioctl");
3546444Swnj 			break;
3556444Swnj 		}
35610839Ssam 		if (atmark)
3576444Swnj 			break;
3589365Ssam 		(void) read(rem, waste, sizeof (waste));
3596444Swnj 	}
36012990Ssam 	recv(rem, &mark, 1, MSG_OOB);
3616444Swnj 	if (mark & TIOCPKT_NOSTOP) {
36213075Ssam 		notc.t_stopc = -1;
36313075Ssam 		notc.t_startc = -1;
36413075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
3656444Swnj 	}
3666444Swnj 	if (mark & TIOCPKT_DOSTOP) {
36713075Ssam 		notc.t_stopc = deftc.t_stopc;
36813075Ssam 		notc.t_startc = deftc.t_startc;
36913075Ssam 		ioctl(0, TIOCSETC, (char *)&notc);
3706444Swnj 	}
3716444Swnj }
3726444Swnj 
3739365Ssam /*
3749365Ssam  * reader: read from remote: line -> 1
3759365Ssam  */
3769365Ssam reader()
3776444Swnj {
3789365Ssam 	char rb[BUFSIZ];
3799365Ssam 	register int cnt;
3806444Swnj 
38112990Ssam 	signal(SIGURG, oob);
3826444Swnj 	{ int pid = -getpid();
3839365Ssam 	  ioctl(rem, SIOCSPGRP, (char *)&pid); }
3846444Swnj 	for (;;) {
3859365Ssam 		cnt = read(rem, rb, sizeof (rb));
38611396Ssam 		if (cnt == 0)
38711396Ssam 			break;
38811396Ssam 		if (cnt < 0) {
3899365Ssam 			if (errno == EINTR)
3906444Swnj 				continue;
3916444Swnj 			break;
3926444Swnj 		}
3939365Ssam 		write(1, rb, cnt);
3946444Swnj 	}
3956444Swnj }
3966444Swnj 
3976444Swnj mode(f)
3986444Swnj {
39913075Ssam 	struct tchars *tc;
40013075Ssam 	struct ltchars *ltc;
40113075Ssam 	struct sgttyb sb;
402*21583Sbloom 	int	lflags;
4039365Ssam 
40413075Ssam 	ioctl(0, TIOCGETP, (char *)&sb);
405*21583Sbloom 	ioctl(0, TIOCLGET, (char *)&lflags);
4069962Ssam 	switch (f) {
4079962Ssam 
4089962Ssam 	case 0:
40913075Ssam 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
41013075Ssam 		sb.sg_flags |= defflags|tabflag;
4119962Ssam 		tc = &deftc;
41213075Ssam 		ltc = &defltc;
41313075Ssam 		sb.sg_kill = defkill;
41413075Ssam 		sb.sg_erase = deferase;
415*21583Sbloom 		lflags = deflflags;
4169962Ssam 		break;
4179962Ssam 
4189962Ssam 	case 1:
41913075Ssam 		sb.sg_flags |= (eight ? RAW : CBREAK);
42013075Ssam 		sb.sg_flags &= ~defflags;
42112155Ssam 		/* preserve tab delays, but turn off XTABS */
42213075Ssam 		if ((sb.sg_flags & TBDELAY) == XTABS)
42313075Ssam 			sb.sg_flags &= ~TBDELAY;
4249962Ssam 		tc = &notc;
42513075Ssam 		ltc = &noltc;
42613075Ssam 		sb.sg_kill = sb.sg_erase = -1;
427*21583Sbloom 		if (litout)
428*21583Sbloom 			lflags |= LLITOUT;
4299962Ssam 		break;
4309962Ssam 
4319962Ssam 	default:
4329962Ssam 		return;
4336444Swnj 	}
43413075Ssam 	ioctl(0, TIOCSLTC, (char *)ltc);
43513075Ssam 	ioctl(0, TIOCSETC, (char *)tc);
43613075Ssam 	ioctl(0, TIOCSETN, (char *)&sb);
437*21583Sbloom 	ioctl(0, TIOCLSET, (char *)&lflags);
4386444Swnj }
4396444Swnj 
4409365Ssam /*VARARGS*/
4416444Swnj prf(f, a1, a2, a3)
4429365Ssam 	char *f;
4436444Swnj {
4446444Swnj 	fprintf(stderr, f, a1, a2, a3);
4456444Swnj 	fprintf(stderr, CRLF);
4466444Swnj }
4476444Swnj 
4489365Ssam lostpeer()
4496444Swnj {
45012990Ssam 	signal(SIGPIPE, SIG_IGN);
45112155Ssam 	prf("\007Connection closed.");
4529365Ssam 	done();
4536444Swnj }
454