xref: /original-bsd/usr.bin/rlogin/rlogin.c (revision 27393bdf)
1 /*
2  * Copyright (c) 1983, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1983, 1990, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)rlogin.c	8.3 (Berkeley) 08/31/94";
16 #endif /* not lint */
17 
18 /*
19  * rlogin - remote login
20  */
21 #include <sys/param.h>
22 #include <sys/socket.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <sys/wait.h>
26 #include <sys/ioctl.h>
27 
28 #include <netinet/in.h>
29 #include <netinet/in_systm.h>
30 #include <netinet/ip.h>
31 
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <netdb.h>
35 #include <pwd.h>
36 #include <setjmp.h>
37 #include <termios.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #ifdef __STDC__
45 #include <stdarg.h>
46 #else
47 #include <varargs.h>
48 #endif
49 
50 #ifdef KERBEROS
51 #include <kerberosIV/des.h>
52 #include <kerberosIV/krb.h>
53 
54 #include "krb.h"
55 
56 CREDENTIALS cred;
57 Key_schedule schedule;
58 int use_kerberos = 1, doencrypt;
59 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
60 #endif
61 
62 #ifndef TIOCPKT_WINDOW
63 #define	TIOCPKT_WINDOW	0x80
64 #endif
65 
66 /* concession to Sun */
67 #ifndef SIGUSR1
68 #define	SIGUSR1	30
69 #endif
70 
71 int eight, litout, rem;
72 
73 int noescape;
74 u_char escapechar = '~';
75 
76 #ifdef OLDSUN
77 struct winsize {
78 	unsigned short ws_row, ws_col;
79 	unsigned short ws_xpixel, ws_ypixel;
80 };
81 #else
82 #define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
83 #endif
84 struct	winsize winsize;
85 
86 void		catch_child __P((int));
87 void		copytochild __P((int));
88 __dead void	doit __P((sigset_t *));
89 __dead void	done __P((int));
90 void		echo __P((char));
91 u_int		getescape __P((char *));
92 void		lostpeer __P((int));
93 void		mode __P((int));
94 void		msg __P((char *));
95 void		oob __P((int));
96 int		reader __P((sigset_t *));
97 void		sendwindow __P((void));
98 void		setsignal __P((int));
99 int		speed __P((int));
100 void		sigwinch __P((int));
101 void		stop __P((char));
102 __dead void	usage __P((void));
103 void		writer __P((void));
104 void		writeroob __P((int));
105 
106 #ifdef	KERBEROS
107 void		warning __P((const char *, ...));
108 #endif
109 #ifdef OLDSUN
110 int		get_window_size __P((int, struct winsize *));
111 #endif
112 
113 int
114 main(argc, argv)
115 	int argc;
116 	char *argv[];
117 {
118 	struct passwd *pw;
119 	struct servent *sp;
120 	sigset_t smask;
121 	uid_t uid;
122 	int argoff, ch, dflag, one;
123 	char *host, *p, *user, term[1024];
124 	struct sigaction sa;
125 
126 	argoff = dflag = 0;
127 	one = 1;
128 	host = user = NULL;
129 
130 	if (p = strrchr(argv[0], '/'))
131 		++p;
132 	else
133 		p = argv[0];
134 
135 	if (strcmp(p, "rlogin") != 0)
136 		host = p;
137 
138 	/* handle "rlogin host flags" */
139 	if (!host && argc > 2 && argv[1][0] != '-') {
140 		host = argv[1];
141 		argoff = 1;
142 	}
143 
144 #ifdef KERBEROS
145 #define	OPTIONS	"8EKLde:k:l:x"
146 #else
147 #define	OPTIONS	"8EKLde:l:"
148 #endif
149 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
150 		switch(ch) {
151 		case '8':
152 			eight = 1;
153 			break;
154 		case 'E':
155 			noescape = 1;
156 			break;
157 		case 'K':
158 #ifdef KERBEROS
159 			use_kerberos = 0;
160 #endif
161 			break;
162 		case 'L':
163 			litout = 1;
164 			break;
165 		case 'd':
166 			dflag = 1;
167 			break;
168 		case 'e':
169 			noescape = 0;
170 			escapechar = getescape(optarg);
171 			break;
172 #ifdef KERBEROS
173 		case 'k':
174 			dest_realm = dst_realm_buf;
175 			(void)strncpy(dest_realm, optarg, REALM_SZ);
176 			break;
177 #endif
178 		case 'l':
179 			user = optarg;
180 			break;
181 #ifdef CRYPT
182 #ifdef KERBEROS
183 		case 'x':
184 			doencrypt = 1;
185 			des_set_key(cred.session, schedule);
186 			break;
187 #endif
188 #endif
189 		case '?':
190 		default:
191 			usage();
192 		}
193 	optind += argoff;
194 	argc -= optind;
195 	argv += optind;
196 
197 	/* if haven't gotten a host yet, do so */
198 	if (!host && !(host = *argv++))
199 		usage();
200 
201 	if (*argv)
202 		usage();
203 
204 	if (!(pw = getpwuid(uid = getuid())))
205 		errx(1, "unknown user id.");
206 
207 	if (!user)
208 		user = pw->pw_name;
209 
210 	sp = NULL;
211 #ifdef KERBEROS
212 	if (use_kerberos) {
213 		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
214 		if (sp == NULL) {
215 			use_kerberos = 0;
216 			warning("can't get entry for %s/tcp service",
217 			    doencrypt ? "eklogin" : "klogin");
218 		}
219 	}
220 #endif
221 	if (sp == NULL)
222 		sp = getservbyname("login", "tcp");
223 	if (sp == NULL)
224 		errx(1, "login/tcp: unknown service.");
225 
226 	(void)snprintf(term, sizeof(term), "%s/%d",
227 			((p = getenv("TERM")) ? p : "network"),
228 			speed(0));
229 
230 	(void)get_window_size(0, &winsize);
231 
232 	sigemptyset(&sa.sa_mask);
233 	sa.sa_flags = SA_RESTART;
234 	sa.sa_handler = lostpeer;
235 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
236 	/* will use SIGUSR1 for window size hack, so hold it off */
237 	sigemptyset(&smask);
238 	sigaddset(&smask, SIGURG);
239 	sigaddset(&smask, SIGUSR1);
240 	(void)sigprocmask(SIG_SETMASK, &smask, &smask);
241 	/*
242 	 * We set SIGURG and SIGUSR1 below so that an
243 	 * incoming signal will be held pending rather than being
244 	 * discarded. Note that these routines will be ready to get
245 	 * a signal by the time that they are unblocked below.
246 	 */
247 	sa.sa_handler = copytochild;
248 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
249 	sa.sa_handler = writeroob;
250 	(void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
251 
252 #ifdef KERBEROS
253 try_connect:
254 	if (use_kerberos) {
255 		struct hostent *hp;
256 
257 		/* Fully qualify hostname (needed for krb_realmofhost). */
258 		hp = gethostbyname(host);
259 		if (hp != NULL && !(host = strdup(hp->h_name)))
260 			errx(1, "%s", strerror(ENOMEM));
261 
262 		rem = KSUCCESS;
263 		errno = 0;
264 		if (dest_realm == NULL)
265 			dest_realm = krb_realmofhost(host);
266 
267 #ifdef CRYPT
268 		if (doencrypt)
269 			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
270 			    dest_realm, &cred, schedule);
271 		else
272 #endif /* CRYPT */
273 			rem = krcmd(&host, sp->s_port, user, term, 0,
274 			    dest_realm);
275 		if (rem < 0) {
276 			use_kerberos = 0;
277 			sp = getservbyname("login", "tcp");
278 			if (sp == NULL)
279 				errx(1, "unknown service login/tcp.");
280 			if (errno == ECONNREFUSED)
281 				warning("remote host doesn't support Kerberos");
282 			if (errno == ENOENT)
283 				warning("can't provide Kerberos auth data");
284 			goto try_connect;
285 		}
286 	} else {
287 #ifdef CRYPT
288 		if (doencrypt)
289 			errx(1, "the -x flag requires Kerberos authentication.");
290 #endif /* CRYPT */
291 		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
292 	}
293 #else
294 	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
295 #endif /* KERBEROS */
296 
297 	if (rem < 0)
298 		exit(1);
299 
300 	if (dflag &&
301 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
302 		warn("setsockopt DEBUG (ignored)");
303 	one = IPTOS_LOWDELAY;
304 	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
305 		warn("setsockopt TOS (ignored)");
306 
307 	(void)setuid(uid);
308 	doit(&smask);
309 	/*NOTREACHED*/
310 }
311 
312 #if BSD >= 198810
313 int
314 speed(fd)
315 	int fd;
316 {
317 	struct termios tt;
318 
319 	(void)tcgetattr(fd, &tt);
320 
321 	return ((int) cfgetispeed(&tt));
322 }
323 #else
324 int    speeds[] = {	/* for older systems, B0 .. EXTB */
325 	0, 50, 75, 110,
326 	134, 150, 200, 300,
327 	600, 1200, 1800, 2400,
328 	4800, 9600, 19200, 38400
329 };
330 
331 int
332 speed(fd)
333 	int fd;
334 {
335 	struct termios tt;
336 
337 	(void)tcgetattr(fd, &tt);
338 
339 	return (speeds[(int)cfgetispeed(&tt)]);
340 }
341 #endif
342 
343 pid_t child;
344 struct termios deftt;
345 struct termios nott;
346 
347 void
348 doit(smask)
349 	sigset_t *smask;
350 {
351 	int i;
352 	struct sigaction sa;
353 
354 	for (i = 0; i < NCCS; i++)
355 		nott.c_cc[i] = _POSIX_VDISABLE;
356 	tcgetattr(0, &deftt);
357 	nott.c_cc[VSTART] = deftt.c_cc[VSTART];
358 	nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
359 	sigemptyset(&sa.sa_mask);
360 	sa.sa_flags = SA_RESTART;
361 	sa.sa_handler = SIG_IGN;
362 	(void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
363 	setsignal(SIGHUP);
364 	setsignal(SIGQUIT);
365 	child = fork();
366 	if (child == -1) {
367 		warn("fork");
368 		done(1);
369 	}
370 	if (child == 0) {
371 		mode(1);
372 		if (reader(smask) == 0) {
373 			msg("connection closed.");
374 			exit(0);
375 		}
376 		sleep(1);
377 		msg("\007connection closed.");
378 		exit(1);
379 	}
380 
381 	/*
382 	 * We may still own the socket, and may have a pending SIGURG (or might
383 	 * receive one soon) that we really want to send to the reader.  When
384 	 * one of these comes in, the trap copytochild simply copies such
385 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
386 	 * that were set above.
387 	 */
388 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
389 	sa.sa_handler = catch_child;
390 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
391 	writer();
392 	msg("closed connection.");
393 	done(0);
394 }
395 
396 /* trap a signal, unless it is being ignored. */
397 void
398 setsignal(sig)
399 	int sig;
400 {
401 	struct sigaction sa;
402 	sigset_t sigs;
403 
404 	sigemptyset(&sigs);
405 	sigaddset(&sigs, sig);
406 	sigprocmask(SIG_BLOCK, &sigs, &sigs);
407 
408 	sigemptyset(&sa.sa_mask);
409 	sa.sa_handler = exit;
410 	sa.sa_flags = SA_RESTART;
411 	(void)sigaction(sig, &sa, &sa);
412 	if (sa.sa_handler == SIG_IGN)
413 		(void)sigaction(sig, &sa, (struct sigaction *) 0);
414 
415 	(void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
416 }
417 
418 __dead void
419 done(status)
420 	int status;
421 {
422 	pid_t w;
423 	int wstatus;
424 	struct sigaction sa;
425 
426 	mode(0);
427 	if (child > 0) {
428 		/* make sure catch_child does not snap it up */
429 		sigemptyset(&sa.sa_mask);
430 		sa.sa_handler = SIG_DFL;
431 		sa.sa_flags = 0;
432 		(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
433 		if (kill(child, SIGKILL) >= 0)
434 			while ((w = wait(&wstatus)) > 0 && w != child)
435 				continue;
436 	}
437 	exit(status);
438 }
439 
440 int dosigwinch;
441 
442 /*
443  * This is called when the reader process gets the out-of-band (urgent)
444  * request to turn on the window-changing protocol.
445  */
446 void
447 writeroob(signo)
448 	int signo;
449 {
450 	struct sigaction sa;
451 
452 	if (dosigwinch == 0) {
453 		sendwindow();
454 		sigemptyset(&sa.sa_mask);
455 		sa.sa_handler = sigwinch;
456 		sa.sa_flags = SA_RESTART;
457 		(void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
458 	}
459 	dosigwinch = 1;
460 }
461 
462 void
463 catch_child(signo)
464 	int signo;
465 {
466 	int status;
467 	pid_t pid;
468 
469 	for (;;) {
470 		pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
471 		if (pid == 0)
472 			return;
473 		/* if the child (reader) dies, just quit */
474 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
475 			done(WEXITSTATUS(status) | WTERMSIG(status));
476 	}
477 	/* NOTREACHED */
478 }
479 
480 /*
481  * writer: write to remote: 0 -> line.
482  * ~.				terminate
483  * ~^Z				suspend rlogin process.
484  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
485  */
486 void
487 writer()
488 {
489 	register int bol, local, n;
490 	char c;
491 
492 	bol = 1;			/* beginning of line */
493 	local = 0;
494 	for (;;) {
495 		n = read(STDIN_FILENO, &c, 1);
496 		if (n <= 0) {
497 			if (n < 0 && errno == EINTR)
498 				continue;
499 			break;
500 		}
501 		/*
502 		 * If we're at the beginning of the line and recognize a
503 		 * command character, then we echo locally.  Otherwise,
504 		 * characters are echo'd remotely.  If the command character
505 		 * is doubled, this acts as a force and local echo is
506 		 * suppressed.
507 		 */
508 		if (bol) {
509 			bol = 0;
510 			if (!noescape && c == escapechar) {
511 				local = 1;
512 				continue;
513 			}
514 		} else if (local) {
515 			local = 0;
516 			if (c == '.' || c == deftt.c_cc[VEOF]) {
517 				echo(c);
518 				break;
519 			}
520 			if (c == deftt.c_cc[VSUSP] || c == deftt.c_cc[VDSUSP]) {
521 				bol = 1;
522 				echo(c);
523 				stop(c);
524 				continue;
525 			}
526 			if (c != escapechar)
527 #ifdef CRYPT
528 #ifdef KERBEROS
529 				if (doencrypt)
530 					(void)des_write(rem,
531 					    (char *)&escapechar, 1);
532 				else
533 #endif
534 #endif
535 					(void)write(rem, &escapechar, 1);
536 		}
537 
538 #ifdef CRYPT
539 #ifdef KERBEROS
540 		if (doencrypt) {
541 			if (des_write(rem, &c, 1) == 0) {
542 				msg("line gone");
543 				break;
544 			}
545 		} else
546 #endif
547 #endif
548 			if (write(rem, &c, 1) == 0) {
549 				msg("line gone");
550 				break;
551 			}
552 		bol = c == deftt.c_cc[VKILL] || c == deftt.c_cc[VEOF] ||
553 		    c == deftt.c_cc[VINTR] || c == deftt.c_cc[VSUSP] ||
554 		    c == '\r' || c == '\n';
555 	}
556 }
557 
558 void
559 #if __STDC__
560 echo(register char c)
561 #else
562 echo(c)
563 	register char c;
564 #endif
565 {
566 	register char *p;
567 	char buf[8];
568 
569 	p = buf;
570 	c &= 0177;
571 	*p++ = escapechar;
572 	if (c < ' ') {
573 		*p++ = '^';
574 		*p++ = c + '@';
575 	} else if (c == 0177) {
576 		*p++ = '^';
577 		*p++ = '?';
578 	} else
579 		*p++ = c;
580 	*p++ = '\r';
581 	*p++ = '\n';
582 	(void)write(STDOUT_FILENO, buf, p - buf);
583 }
584 
585 void
586 #if __STDC__
587 stop(char cmdc)
588 #else
589 stop(cmdc)
590 	char cmdc;
591 #endif
592 {
593 	struct sigaction sa;
594 
595 	mode(0);
596 	sigemptyset(&sa.sa_mask);
597 	sa.sa_handler = SIG_IGN;
598 	sa.sa_flags = SA_RESTART;
599 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
600 	(void)kill(cmdc == deftt.c_cc[VSUSP] ? 0 : getpid(), SIGTSTP);
601 	sa.sa_handler = catch_child;
602 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
603 	mode(1);
604 	sigwinch(0);			/* check for size changes */
605 }
606 
607 void
608 sigwinch(signo)
609 	int signo;
610 {
611 	struct winsize ws;
612 
613 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
614 	    memcmp(&ws, &winsize, sizeof(ws))) {
615 		winsize = ws;
616 		sendwindow();
617 	}
618 }
619 
620 /*
621  * Send the window size to the server via the magic escape
622  */
623 void
624 sendwindow()
625 {
626 	struct winsize *wp;
627 	char obuf[4 + sizeof (struct winsize)];
628 
629 	wp = (struct winsize *)(obuf+4);
630 	obuf[0] = 0377;
631 	obuf[1] = 0377;
632 	obuf[2] = 's';
633 	obuf[3] = 's';
634 	wp->ws_row = htons(winsize.ws_row);
635 	wp->ws_col = htons(winsize.ws_col);
636 	wp->ws_xpixel = htons(winsize.ws_xpixel);
637 	wp->ws_ypixel = htons(winsize.ws_ypixel);
638 
639 #ifdef CRYPT
640 #ifdef KERBEROS
641 	if(doencrypt)
642 		(void)des_write(rem, obuf, sizeof(obuf));
643 	else
644 #endif
645 #endif
646 		(void)write(rem, obuf, sizeof(obuf));
647 }
648 
649 /*
650  * reader: read from remote: line -> 1
651  */
652 #define	READING	1
653 #define	WRITING	2
654 
655 jmp_buf rcvtop;
656 pid_t ppid;
657 int rcvcnt, rcvstate;
658 char rcvbuf[8 * 1024];
659 
660 void
661 oob(signo)
662 	int signo;
663 {
664 	struct termios tt;
665 	int atmark, n, out, rcvd;
666 	char waste[BUFSIZ], mark;
667 
668 	out = O_RDWR;
669 	rcvd = 0;
670 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
671 		switch (errno) {
672 		case EWOULDBLOCK:
673 			/*
674 			 * Urgent data not here yet.  It may not be possible
675 			 * to send it yet if we are blocked for output and
676 			 * our input buffer is full.
677 			 */
678 			if (rcvcnt < sizeof(rcvbuf)) {
679 				n = read(rem, rcvbuf + rcvcnt,
680 				    sizeof(rcvbuf) - rcvcnt);
681 				if (n <= 0)
682 					return;
683 				rcvd += n;
684 			} else {
685 				n = read(rem, waste, sizeof(waste));
686 				if (n <= 0)
687 					return;
688 			}
689 			continue;
690 		default:
691 			return;
692 		}
693 	}
694 	if (mark & TIOCPKT_WINDOW) {
695 		/* Let server know about window size changes */
696 		(void)kill(ppid, SIGUSR1);
697 	}
698 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
699 		tcgetattr(0, &tt);
700 		tt.c_iflag &= ~(IXON | IXOFF);
701 		tt.c_cc[VSTOP] = _POSIX_VDISABLE;
702 		tt.c_cc[VSTART] = _POSIX_VDISABLE;
703 		tcsetattr(0, TCSANOW, &tt);
704 	}
705 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
706 		tcgetattr(0, &tt);
707 		tt.c_iflag |= (IXON|IXOFF);
708 		tt.c_cc[VSTOP] = deftt.c_cc[VSTOP];
709 		tt.c_cc[VSTART] = deftt.c_cc[VSTART];
710 		tcsetattr(0, TCSANOW, &tt);
711 	}
712 	if (mark & TIOCPKT_FLUSHWRITE) {
713 		(void)ioctl(1, TIOCFLUSH, (char *)&out);
714 		for (;;) {
715 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
716 				warn("ioctl SIOCATMARK (ignored)");
717 				break;
718 			}
719 			if (atmark)
720 				break;
721 			n = read(rem, waste, sizeof (waste));
722 			if (n <= 0)
723 				break;
724 		}
725 		/*
726 		 * Don't want any pending data to be output, so clear the recv
727 		 * buffer.  If we were hanging on a write when interrupted,
728 		 * don't want it to restart.  If we were reading, restart
729 		 * anyway.
730 		 */
731 		rcvcnt = 0;
732 		longjmp(rcvtop, 1);
733 	}
734 
735 	/* oob does not do FLUSHREAD (alas!) */
736 
737 	/*
738 	 * If we filled the receive buffer while a read was pending, longjmp
739 	 * to the top to restart appropriately.  Don't abort a pending write,
740 	 * however, or we won't know how much was written.
741 	 */
742 	if (rcvd && rcvstate == READING)
743 		longjmp(rcvtop, 1);
744 }
745 
746 /* reader: read from remote: line -> 1 */
747 int
748 reader(smask)
749 	sigset_t *smask;
750 {
751 	pid_t pid;
752 	int n, remaining;
753 	char *bufp;
754 	struct sigaction sa;
755 
756 #if BSD >= 43 || defined(SUNOS4)
757 	pid = getpid();		/* modern systems use positives for pid */
758 #else
759 	pid = -getpid();	/* old broken systems use negatives */
760 #endif
761 	sigemptyset(&sa.sa_mask);
762 	sa.sa_flags = SA_RESTART;
763 	sa.sa_handler = SIG_IGN;
764 	(void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
765 	sa.sa_handler = oob;
766 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
767 	ppid = getppid();
768 	(void)fcntl(rem, F_SETOWN, pid);
769 	(void)setjmp(rcvtop);
770 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
771 	bufp = rcvbuf;
772 	for (;;) {
773 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
774 			rcvstate = WRITING;
775 			n = write(STDOUT_FILENO, bufp, remaining);
776 			if (n < 0) {
777 				if (errno != EINTR)
778 					return (-1);
779 				continue;
780 			}
781 			bufp += n;
782 		}
783 		bufp = rcvbuf;
784 		rcvcnt = 0;
785 		rcvstate = READING;
786 
787 #ifdef CRYPT
788 #ifdef KERBEROS
789 		if (doencrypt)
790 			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
791 		else
792 #endif
793 #endif
794 			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
795 		if (rcvcnt == 0)
796 			return (0);
797 		if (rcvcnt < 0) {
798 			if (errno == EINTR)
799 				continue;
800 			warn("read");
801 			return (-1);
802 		}
803 	}
804 }
805 
806 void
807 mode(f)
808 	int f;
809 {
810 	struct termios tt;
811 
812 	switch (f) {
813 	case 0:
814 		tcsetattr(0, TCSADRAIN, &deftt);
815 		break;
816 	case 1:
817 		tt = deftt;
818 		tt.c_oflag &= ~(OPOST);
819 		tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
820 		tt.c_iflag &= ~(ICRNL);
821 		tt.c_cc[VMIN] = 1;
822 		tt.c_cc[VTIME] = 0;
823 		if (eight) {
824 			tt.c_iflag &= ~(IXON | IXOFF | ISTRIP);
825 			tt.c_cc[VSTOP] = _POSIX_VDISABLE;
826 			tt.c_cc[VSTART] = _POSIX_VDISABLE;
827 		}
828 		/*if (litout)
829 			lflags |= LLITOUT;*/
830 		tcsetattr(0, TCSADRAIN, &tt);
831 		break;
832 
833 	default:
834 		return;
835 	}
836 }
837 
838 void
839 lostpeer(signo)
840 	int signo;
841 {
842 	struct sigaction sa;
843 
844 	sigemptyset(&sa.sa_mask);
845 	sa.sa_flags = SA_RESTART;
846 	sa.sa_handler = SIG_IGN;
847 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
848 	msg("\007connection closed.");
849 	done(1);
850 }
851 
852 /* copy SIGURGs to the child process. */
853 void
854 copytochild(signo)
855 	int signo;
856 {
857 
858 	(void)kill(child, SIGURG);
859 }
860 
861 void
862 msg(str)
863 	char *str;
864 {
865 
866 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
867 }
868 
869 #ifdef KERBEROS
870 /* VARARGS */
871 void
872 #if __STDC__
873 warning(const char *fmt, ...)
874 #else
875 warning(fmt, va_alist)
876 	char *fmt;
877 	va_dcl
878 #endif
879 {
880 	va_list ap;
881 
882 	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
883 #ifdef __STDC__
884 	va_start(ap, fmt);
885 #else
886 	va_start(ap);
887 #endif
888 	vfprintf(stderr, fmt, ap);
889 	va_end(ap);
890 	(void)fprintf(stderr, ".\n");
891 }
892 #endif
893 
894 __dead void
895 usage()
896 {
897 	(void)fprintf(stderr,
898 	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
899 #ifdef KERBEROS
900 #ifdef CRYPT
901 	    "8EKLx", " [-k realm] ");
902 #else
903 	    "8EKL", " [-k realm] ");
904 #endif
905 #else
906 	    "8EL", " ");
907 #endif
908 	exit(1);
909 }
910 
911 /*
912  * The following routine provides compatibility (such as it is) between older
913  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
914  */
915 #ifdef OLDSUN
916 int
917 get_window_size(fd, wp)
918 	int fd;
919 	struct winsize *wp;
920 {
921 	struct ttysize ts;
922 	int error;
923 
924 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
925 		return (error);
926 	wp->ws_row = ts.ts_lines;
927 	wp->ws_col = ts.ts_cols;
928 	wp->ws_xpixel = 0;
929 	wp->ws_ypixel = 0;
930 	return (0);
931 }
932 #endif
933 
934 u_int
935 getescape(p)
936 	register char *p;
937 {
938 	long val;
939 	int len;
940 
941 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
942 		return ((u_int)*p);
943 					/* otherwise, \nnn */
944 	if (*p == '\\' && len >= 2 && len <= 4) {
945 		val = strtol(++p, NULL, 8);
946 		for (;;) {
947 			if (!*++p)
948 				return ((u_int)val);
949 			if (*p < '0' || *p > '8')
950 				break;
951 		}
952 	}
953 	msg("illegal option value -- e");
954 	usage();
955 	/* NOTREACHED */
956 }
957