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