xref: /original-bsd/usr.bin/rlogin/rlogin.c (revision 8f26171a)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)rlogin.c	5.11 (Berkeley) 08/07/86";
15 #endif not lint
16 
17 /*
18  * rlogin - remote login
19  */
20 #include <sys/param.h>
21 #include <sys/errno.h>
22 #include <sys/file.h>
23 #include <sys/socket.h>
24 #include <sys/time.h>
25 #include <sys/resource.h>
26 #include <sys/wait.h>
27 
28 #include <netinet/in.h>
29 
30 #include <stdio.h>
31 #include <sgtty.h>
32 #include <errno.h>
33 #include <pwd.h>
34 #include <signal.h>
35 #include <setjmp.h>
36 #include <netdb.h>
37 
38 # ifndef TIOCPKT_WINDOW
39 # define TIOCPKT_WINDOW 0x80
40 # endif TIOCPKT_WINDOW
41 
42 /* concession to sun */
43 # ifndef SIGUSR1
44 # define SIGUSR1 30
45 # endif SIGUSR1
46 
47 char	*index(), *rindex(), *malloc(), *getenv(), *strcat(), *strcpy();
48 struct	passwd *getpwuid();
49 char	*name;
50 int	rem;
51 char	cmdchar = '~';
52 int	eight;
53 int	litout;
54 char	*speeds[] =
55     { "0", "50", "75", "110", "134", "150", "200", "300",
56       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
57 char	term[256] = "network";
58 extern	int errno;
59 int	lostpeer();
60 int	dosigwinch = 0;
61 #ifndef sigmask
62 #define sigmask(m)	(1 << ((m)-1))
63 #endif
64 #ifdef sun
65 struct winsize {
66 	unsigned short ws_row, ws_col;
67 	unsigned short ws_xpixel, ws_ypixel;
68 };
69 #endif sun
70 struct	winsize winsize;
71 int	sigwinch(), oob();
72 
73 /*
74  * The following routine provides compatibility (such as it is)
75  * between 4.2BSD Suns and others.  Suns have only a `ttysize',
76  * so we convert it to a winsize.
77  */
78 #ifdef sun
79 int
80 get_window_size(fd, wp)
81 	int fd;
82 	struct winsize *wp;
83 {
84 	struct ttysize ts;
85 	int error;
86 
87 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
88 		return (error);
89 	wp->ws_row = ts.ts_lines;
90 	wp->ws_col = ts.ts_cols;
91 	wp->ws_xpixel = 0;
92 	wp->ws_ypixel = 0;
93 	return (0);
94 }
95 #else sun
96 #define get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
97 #endif sun
98 
99 main(argc, argv)
100 	int argc;
101 	char **argv;
102 {
103 	char *host, *cp;
104 	struct sgttyb ttyb;
105 	struct passwd *pwd;
106 	struct servent *sp;
107 	int uid, options = 0, oldmask;
108 	int on = 1;
109 
110 	host = rindex(argv[0], '/');
111 	if (host)
112 		host++;
113 	else
114 		host = argv[0];
115 	argv++, --argc;
116 	if (!strcmp(host, "rlogin"))
117 		host = *argv++, --argc;
118 another:
119 	if (argc > 0 && !strcmp(*argv, "-d")) {
120 		argv++, argc--;
121 		options |= SO_DEBUG;
122 		goto another;
123 	}
124 	if (argc > 0 && !strcmp(*argv, "-l")) {
125 		argv++, argc--;
126 		if (argc == 0)
127 			goto usage;
128 		name = *argv++; argc--;
129 		goto another;
130 	}
131 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
132 		cmdchar = argv[0][2];
133 		argv++, argc--;
134 		goto another;
135 	}
136 	if (argc > 0 && !strcmp(*argv, "-8")) {
137 		eight = 1;
138 		argv++, argc--;
139 		goto another;
140 	}
141 	if (argc > 0 && !strcmp(*argv, "-L")) {
142 		litout = 1;
143 		argv++, argc--;
144 		goto another;
145 	}
146 	if (host == 0)
147 		goto usage;
148 	if (argc > 0)
149 		goto usage;
150 	pwd = getpwuid(getuid());
151 	if (pwd == 0) {
152 		fprintf(stderr, "Who are you?\n");
153 		exit(1);
154 	}
155 	sp = getservbyname("login", "tcp");
156 	if (sp == 0) {
157 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
158 		exit(2);
159 	}
160 	cp = getenv("TERM");
161 	if (cp)
162 		(void) strcpy(term, cp);
163 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
164 		(void) strcat(term, "/");
165 		(void) strcat(term, speeds[ttyb.sg_ospeed]);
166 	}
167 	(void) get_window_size(0, &winsize);
168 	(void) signal(SIGPIPE, lostpeer);
169 	/* will use SIGUSR1 for window size hack, so hold it off */
170 	oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
171         rem = rcmd(&host, sp->s_port, pwd->pw_name,
172 	    name ? name : pwd->pw_name, term, 0);
173         if (rem < 0)
174                 exit(1);
175 	if (options & SO_DEBUG &&
176 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
177 		perror("rlogin: setsockopt (SO_DEBUG)");
178 	uid = getuid();
179 	if (setuid(uid) < 0) {
180 		perror("rlogin: setuid");
181 		exit(1);
182 	}
183 	doit(oldmask);
184 	/*NOTREACHED*/
185 usage:
186 	fprintf(stderr,
187 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n");
188 	exit(1);
189 }
190 
191 #define CRLF "\r\n"
192 
193 int	child;
194 int	catchild();
195 int	copytochild(), writeroob();
196 
197 int	defflags, tabflag;
198 int	deflflags;
199 char	deferase, defkill;
200 struct	tchars deftc;
201 struct	ltchars defltc;
202 struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
203 struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
204 
205 doit(oldmask)
206 {
207 	int exit();
208 	struct sgttyb sb;
209 
210 	(void) ioctl(0, TIOCGETP, (char *)&sb);
211 	defflags = sb.sg_flags;
212 	tabflag = defflags & TBDELAY;
213 	defflags &= ECHO | CRMOD;
214 	deferase = sb.sg_erase;
215 	defkill = sb.sg_kill;
216 	(void) ioctl(0, TIOCLGET, (char *)&deflflags);
217 	(void) ioctl(0, TIOCGETC, (char *)&deftc);
218 	notc.t_startc = deftc.t_startc;
219 	notc.t_stopc = deftc.t_stopc;
220 	(void) ioctl(0, TIOCGLTC, (char *)&defltc);
221 	(void) signal(SIGINT, SIG_IGN);
222 	setsignal(SIGHUP, exit);
223 	setsignal(SIGQUIT, exit);
224 	child = fork();
225 	if (child == -1) {
226 		perror("rlogin: fork");
227 		done(1);
228 	}
229 	if (child == 0) {
230 		mode(1);
231 		if (reader(oldmask) == 0) {
232 			prf("Connection closed.");
233 			exit(0);
234 		}
235 		sleep(1);
236 		prf("\007Connection closed.");
237 		exit(3);
238 	}
239 
240 	/*
241 	 * We may still own the socket, and may have a pending SIGURG
242 	 * (or might receive one soon) that we really want to send to
243 	 * the reader.  Set a trap that simply copies such signals to
244 	 * the child.
245 	 */
246 	(void) signal(SIGURG, copytochild);
247 	(void) signal(SIGUSR1, writeroob);
248 	(void) sigsetmask(oldmask);
249 	(void) signal(SIGCHLD, catchild);
250 	writer();
251 	prf("Closed connection.");
252 	done(0);
253 }
254 
255 /*
256  * Trap a signal, unless it is being ignored.
257  */
258 setsignal(sig, act)
259 	int sig, (*act)();
260 {
261 	int omask = sigblock(sigmask(sig));
262 
263 	if (signal(sig, act) == SIG_IGN)
264 		(void) signal(sig, SIG_IGN);
265 	(void) sigsetmask(omask);
266 }
267 
268 done(status)
269 	int status;
270 {
271 	int w;
272 
273 	mode(0);
274 	if (child > 0) {
275 		/* make sure catchild does not snap it up */
276 		(void) signal(SIGCHLD, SIG_DFL);
277 		if (kill(child, SIGKILL) >= 0)
278 			while ((w = wait((union wait *)0)) > 0 && w != child)
279 				/*void*/;
280 	}
281 	exit(status);
282 }
283 
284 /*
285  * Copy SIGURGs to the child process.
286  */
287 copytochild()
288 {
289 
290 	(void) kill(child, SIGURG);
291 }
292 
293 /*
294  * This is called when the reader process gets the out-of-band (urgent)
295  * request to turn on the window-changing protocol.
296  */
297 writeroob()
298 {
299 
300 	if (dosigwinch == 0) {
301 		sendwindow();
302 		(void) signal(SIGWINCH, sigwinch);
303 	}
304 	dosigwinch = 1;
305 }
306 
307 catchild()
308 {
309 	union wait status;
310 	int pid;
311 
312 again:
313 	pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
314 	if (pid == 0)
315 		return;
316 	/*
317 	 * if the child (reader) dies, just quit
318 	 */
319 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
320 		done((int)(status.w_termsig | status.w_retcode));
321 	goto again;
322 }
323 
324 /*
325  * writer: write to remote: 0 -> line.
326  * ~.	terminate
327  * ~^Z	suspend rlogin process.
328  * ~^Y  suspend rlogin process, but leave reader alone.
329  */
330 writer()
331 {
332 	char c;
333 	register n;
334 	register bol = 1;               /* beginning of line */
335 	register local = 0;
336 
337 	for (;;) {
338 		n = read(0, &c, 1);
339 		if (n <= 0) {
340 			if (n < 0 && errno == EINTR)
341 				continue;
342 			break;
343 		}
344 		/*
345 		 * If we're at the beginning of the line
346 		 * and recognize a command character, then
347 		 * we echo locally.  Otherwise, characters
348 		 * are echo'd remotely.  If the command
349 		 * character is doubled, this acts as a
350 		 * force and local echo is suppressed.
351 		 */
352 		if (bol) {
353 			bol = 0;
354 			if (c == cmdchar) {
355 				bol = 0;
356 				local = 1;
357 				continue;
358 			}
359 		} else if (local) {
360 			local = 0;
361 			if (c == '.' || c == deftc.t_eofc) {
362 				echo(c);
363 				break;
364 			}
365 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
366 				bol = 1;
367 				echo(c);
368 				stop(c);
369 				continue;
370 			}
371 			if (c != cmdchar)
372 				(void) write(rem, &cmdchar, 1);
373 		}
374 		if (write(rem, &c, 1) == 0) {
375 			prf("line gone");
376 			break;
377 		}
378 		bol = c == defkill || c == deftc.t_eofc ||
379 		    c == deftc.t_intrc || c == defltc.t_suspc ||
380 		    c == '\r' || c == '\n';
381 	}
382 }
383 
384 echo(c)
385 register char c;
386 {
387 	char buf[8];
388 	register char *p = buf;
389 
390 	c &= 0177;
391 	*p++ = cmdchar;
392 	if (c < ' ') {
393 		*p++ = '^';
394 		*p++ = c + '@';
395 	} else if (c == 0177) {
396 		*p++ = '^';
397 		*p++ = '?';
398 	} else
399 		*p++ = c;
400 	*p++ = '\r';
401 	*p++ = '\n';
402 	(void) write(1, buf, p - buf);
403 }
404 
405 stop(cmdc)
406 	char cmdc;
407 {
408 	mode(0);
409 	(void) signal(SIGCHLD, SIG_IGN);
410 	(void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
411 	(void) signal(SIGCHLD, catchild);
412 	mode(1);
413 	sigwinch();			/* check for size changes */
414 }
415 
416 sigwinch()
417 {
418 	struct winsize ws;
419 
420 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
421 	    bcmp(&ws, &winsize, sizeof (ws))) {
422 		winsize = ws;
423 		sendwindow();
424 	}
425 }
426 
427 /*
428  * Send the window size to the server via the magic escape
429  */
430 sendwindow()
431 {
432 	char obuf[4 + sizeof (struct winsize)];
433 	struct winsize *wp = (struct winsize *)(obuf+4);
434 
435 	obuf[0] = 0377;
436 	obuf[1] = 0377;
437 	obuf[2] = 's';
438 	obuf[3] = 's';
439 	wp->ws_row = htons(winsize.ws_row);
440 	wp->ws_col = htons(winsize.ws_col);
441 	wp->ws_xpixel = htons(winsize.ws_xpixel);
442 	wp->ws_ypixel = htons(winsize.ws_ypixel);
443 	(void) write(rem, obuf, sizeof(obuf));
444 }
445 
446 /*
447  * reader: read from remote: line -> 1
448  */
449 #define	READING	1
450 #define	WRITING	2
451 
452 char	rcvbuf[8 * 1024];
453 int	rcvcnt;
454 int	rcvstate;
455 int	ppid;
456 jmp_buf	rcvtop;
457 
458 oob()
459 {
460 	int out = FWRITE, atmark, n;
461 	int rcvd = 0;
462 	char waste[BUFSIZ], mark;
463 	struct sgttyb sb;
464 
465 	while (recv(rem, &mark, 1, MSG_OOB) < 0)
466 		switch (errno) {
467 
468 		case EWOULDBLOCK:
469 			/*
470 			 * Urgent data not here yet.
471 			 * It may not be possible to send it yet
472 			 * if we are blocked for output
473 			 * and our input buffer is full.
474 			 */
475 			if (rcvcnt < sizeof(rcvbuf)) {
476 				n = read(rem, rcvbuf + rcvcnt,
477 					sizeof(rcvbuf) - rcvcnt);
478 				if (n <= 0)
479 					return;
480 				rcvd += n;
481 			} else {
482 				n = read(rem, waste, sizeof(waste));
483 				if (n <= 0)
484 					return;
485 			}
486 			continue;
487 
488 		default:
489 			return;
490 	}
491 	if (mark & TIOCPKT_WINDOW) {
492 		/*
493 		 * Let server know about window size changes
494 		 */
495 		(void) kill(ppid, SIGUSR1);
496 	}
497 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
498 		(void) ioctl(0, TIOCGETP, (char *)&sb);
499 		sb.sg_flags &= ~CBREAK;
500 		sb.sg_flags |= RAW;
501 		(void) ioctl(0, TIOCSETN, (char *)&sb);
502 		notc.t_stopc = -1;
503 		notc.t_startc = -1;
504 		(void) ioctl(0, TIOCSETC, (char *)&notc);
505 	}
506 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
507 		(void) ioctl(0, TIOCGETP, (char *)&sb);
508 		sb.sg_flags &= ~RAW;
509 		sb.sg_flags |= CBREAK;
510 		(void) ioctl(0, TIOCSETN, (char *)&sb);
511 		notc.t_stopc = deftc.t_stopc;
512 		notc.t_startc = deftc.t_startc;
513 		(void) ioctl(0, TIOCSETC, (char *)&notc);
514 	}
515 	if (mark & TIOCPKT_FLUSHWRITE) {
516 		(void) ioctl(1, TIOCFLUSH, (char *)&out);
517 		for (;;) {
518 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
519 				perror("ioctl");
520 				break;
521 			}
522 			if (atmark)
523 				break;
524 			n = read(rem, waste, sizeof (waste));
525 			if (n <= 0)
526 				break;
527 		}
528 		/*
529 		 * Don't want any pending data to be output,
530 		 * so clear the recv buffer.
531 		 * If we were hanging on a write when interrupted,
532 		 * don't want it to restart.  If we were reading,
533 		 * restart anyway.
534 		 */
535 		rcvcnt = 0;
536 		longjmp(rcvtop, 1);
537 	}
538 
539 	/*
540 	 * oob does not do FLUSHREAD (alas!)
541 	 */
542 
543 	/*
544 	 * If we filled the receive buffer while a read was pending,
545 	 * longjmp to the top to restart appropriately.  Don't abort
546 	 * a pending write, however, or we won't know how much was written.
547 	 */
548 	if (rcvd && rcvstate == READING)
549 		longjmp(rcvtop, 1);
550 }
551 
552 /*
553  * reader: read from remote: line -> 1
554  */
555 reader(oldmask)
556 	int oldmask;
557 {
558 #if !defined(BSD) || BSD < 43
559 	int pid = -getpid();
560 #else
561 	int pid = getpid();
562 #endif
563 	int n, remaining;
564 	char *bufp = rcvbuf;
565 
566 	(void) signal(SIGTTOU, SIG_IGN);
567 	(void) signal(SIGURG, oob);
568 	ppid = getppid();
569 	(void) fcntl(rem, F_SETOWN, pid);
570 	(void) setjmp(rcvtop);
571 	(void) sigsetmask(oldmask);
572 	for (;;) {
573 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
574 			rcvstate = WRITING;
575 			n = write(1, bufp, remaining);
576 			if (n < 0) {
577 				if (errno != EINTR)
578 					return (-1);
579 				continue;
580 			}
581 			bufp += n;
582 		}
583 		bufp = rcvbuf;
584 		rcvcnt = 0;
585 		rcvstate = READING;
586 		rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
587 		if (rcvcnt == 0)
588 			return (0);
589 		if (rcvcnt < 0) {
590 			if (errno == EINTR)
591 				continue;
592 			perror("read");
593 			return (-1);
594 		}
595 	}
596 }
597 
598 mode(f)
599 {
600 	struct tchars *tc;
601 	struct ltchars *ltc;
602 	struct sgttyb sb;
603 	int	lflags;
604 
605 	(void) ioctl(0, TIOCGETP, (char *)&sb);
606 	(void) ioctl(0, TIOCLGET, (char *)&lflags);
607 	switch (f) {
608 
609 	case 0:
610 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
611 		sb.sg_flags |= defflags|tabflag;
612 		tc = &deftc;
613 		ltc = &defltc;
614 		sb.sg_kill = defkill;
615 		sb.sg_erase = deferase;
616 		lflags = deflflags;
617 		break;
618 
619 	case 1:
620 		sb.sg_flags |= (eight ? RAW : CBREAK);
621 		sb.sg_flags &= ~defflags;
622 		/* preserve tab delays, but turn off XTABS */
623 		if ((sb.sg_flags & TBDELAY) == XTABS)
624 			sb.sg_flags &= ~TBDELAY;
625 		tc = &notc;
626 		ltc = &noltc;
627 		sb.sg_kill = sb.sg_erase = -1;
628 		if (litout)
629 			lflags |= LLITOUT;
630 		break;
631 
632 	default:
633 		return;
634 	}
635 	(void) ioctl(0, TIOCSLTC, (char *)ltc);
636 	(void) ioctl(0, TIOCSETC, (char *)tc);
637 	(void) ioctl(0, TIOCSETN, (char *)&sb);
638 	(void) ioctl(0, TIOCLSET, (char *)&lflags);
639 }
640 
641 /*VARARGS*/
642 prf(f, a1, a2, a3, a4, a5)
643 	char *f;
644 {
645 
646 	fprintf(stderr, f, a1, a2, a3, a4, a5);
647 	fprintf(stderr, CRLF);
648 }
649 
650 lostpeer()
651 {
652 
653 	(void) signal(SIGPIPE, SIG_IGN);
654 	prf("\007Connection closed.");
655 	done(1);
656 }
657 
658