rlogin.c revision 152397
1292932Sdim/*
2292932Sdim * Copyright (c) 1983, 1990, 1993
3353358Sdim *	The Regents of the University of California.  All rights reserved.
4353358Sdim * Copyright (c) 2002 Networks Associates Technology, Inc.
5353358Sdim * All rights reserved.
6292932Sdim *
7292932Sdim * Portions of this software were developed for the FreeBSD Project by
8292932Sdim * ThinkSec AS and NAI Labs, the Security Research Division of Network
9292932Sdim * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10292932Sdim * ("CBOSS"), as part of the DARPA CHATS research program.
11292932Sdim *
12292932Sdim * Redistribution and use in source and binary forms, with or without
13292932Sdim * modification, are permitted provided that the following conditions
14292932Sdim * are met:
15292932Sdim * 1. Redistributions of source code must retain the above copyright
16292932Sdim *    notice, this list of conditions and the following disclaimer.
17292932Sdim * 2. Redistributions in binary form must reproduce the above copyright
18292932Sdim *    notice, this list of conditions and the following disclaimer in the
19292932Sdim *    documentation and/or other materials provided with the distribution.
20314564Sdim * 3. All advertising materials mentioning features or use of this software
21292932Sdim *    must display the following acknowledgement:
22314564Sdim *	This product includes software developed by the University of
23314564Sdim *	California, Berkeley and its contributors.
24292932Sdim * 4. Neither the name of the University nor the names of its contributors
25292932Sdim *    may be used to endorse or promote products derived from this software
26292932Sdim *    without specific prior written permission.
27309124Sdim *
28292932Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29292932Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30292932Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31309124Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32292932Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33321369Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34321369Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35321369Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36321369Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37344779Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38321369Sdim * SUCH DAMAGE.
39292932Sdim */
40292932Sdim
41292932Sdim#ifndef lint
42292932Sdimstatic const char copyright[] =
43292932Sdim"@(#) Copyright (c) 1983, 1990, 1993\n\
44292932Sdim	The Regents of the University of California.  All rights reserved.\n";
45292932Sdim#endif /* not lint */
46360784Sdim
47360784Sdim#if 0
48314564Sdim#ifndef lint
49314564Sdimstatic const char sccsid[] = "@(#)rlogin.c	8.1 (Berkeley) 6/6/93";
50292932Sdim#endif /* not lint */
51314564Sdim#endif
52292932Sdim
53292932Sdim#include <sys/cdefs.h>
54314564Sdim__FBSDID("$FreeBSD: head/usr.bin/rlogin/rlogin.c 152397 2005-11-13 21:03:56Z dwmalone $");
55314564Sdim
56314564Sdim/*
57314564Sdim * rlogin - remote login
58314564Sdim */
59314564Sdim
60314564Sdim#include <sys/param.h>
61292932Sdim#include <sys/ioctl.h>
62292932Sdim#include <sys/socket.h>
63314564Sdim#include <sys/time.h>
64314564Sdim#include <sys/resource.h>
65353358Sdim#include <sys/wait.h>
66292932Sdim
67314564Sdim#include <netinet/in.h>
68292932Sdim#include <netinet/in_systm.h>
69292932Sdim#include <netinet/ip.h>
70314564Sdim#include <netinet/tcp.h>
71314564Sdim
72314564Sdim#include <err.h>
73314564Sdim#include <errno.h>
74314564Sdim#include <fcntl.h>
75314564Sdim#include <libutil.h>
76353358Sdim#include <netdb.h>
77353358Sdim#include <paths.h>
78353358Sdim#include <pwd.h>
79353358Sdim#include <setjmp.h>
80353358Sdim#include <termios.h>
81353358Sdim#include <signal.h>
82314564Sdim#include <stdio.h>
83292932Sdim#include <stdlib.h>
84292932Sdim#include <string.h>
85292932Sdim#include <unistd.h>
86314564Sdim
87314564Sdim#ifndef TIOCPKT_WINDOW
88314564Sdim#define	TIOCPKT_WINDOW	0x80
89292932Sdim#endif
90314564Sdim
91292932Sdim/* concession to Sun */
92292932Sdim#ifndef SIGUSR1
93314564Sdim#define	SIGUSR1	30
94314564Sdim#endif
95314564Sdim
96292932Sdimint eight, rem;
97292932Sdimstruct termios deftty;
98314564Sdim
99314564Sdimint family = PF_UNSPEC;
100314564Sdim
101314564Sdimint noescape;
102314564Sdimu_char escapechar = '~';
103314564Sdim
104314564Sdim#define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
105314564Sdimstruct	winsize winsize;
106292932Sdim
107292932Sdimvoid		catch_child(int);
108314564Sdimvoid		copytochild(int);
109292932Sdimvoid		doit(long) __dead2;
110314564Sdimvoid		done(int) __dead2;
111314564Sdimvoid		echo(char);
112314564Sdimu_int		getescape(const char *);
113314564Sdimvoid		lostpeer(int);
114292932Sdimvoid		mode(int);
115292932Sdimvoid		msg(const char *);
116314564Sdimvoid		oob(int);
117314564Sdimint		reader(int);
118314564Sdimvoid		sendwindow(void);
119314564Sdimvoid		setsignal(int);
120292932Sdimvoid		sigwinch(int);
121314564Sdimvoid		stop(char);
122353358Sdimvoid		usage(void) __dead2;
123292932Sdimvoid		writer(void);
124314564Sdimvoid		writeroob(int);
125314564Sdim
126292932Sdimint
127314564Sdimmain(int argc, char *argv[])
128314564Sdim{
129314564Sdim	struct passwd *pw;
130314564Sdim	struct servent *sp;
131353358Sdim	struct termios tty;
132292932Sdim	long omask;
133314564Sdim	int argoff, ch, dflag, Dflag, one;
134314564Sdim	uid_t uid;
135292932Sdim	char *host, *localname, *p, *user, term[1024];
136314564Sdim	speed_t ospeed;
137314564Sdim	struct sockaddr_storage ss;
138314564Sdim	socklen_t sslen;
139292932Sdim	size_t len, len2;
140314564Sdim	int i;
141314564Sdim
142292932Sdim	argoff = dflag = Dflag = 0;
143314564Sdim	one = 1;
144353358Sdim	host = localname = user = NULL;
145292932Sdim
146314564Sdim	if ((p = rindex(argv[0], '/')))
147292932Sdim		++p;
148314564Sdim	else
149314564Sdim		p = argv[0];
150292932Sdim
151353358Sdim	if (strcmp(p, "rlogin"))
152292932Sdim		host = p;
153292932Sdim
154314564Sdim	/* handle "rlogin host flags" */
155314564Sdim	if (!host && argc > 2 && argv[1][0] != '-') {
156292932Sdim		host = argv[1];
157292932Sdim		argoff = 1;
158360784Sdim	}
159360784Sdim
160360784Sdim#define	OPTIONS	"468DEde:i:l:"
161360784Sdim	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
162360784Sdim		switch(ch) {
163360784Sdim		case '4':
164360784Sdim			family = PF_INET;
165360784Sdim			break;
166360784Sdim
167314564Sdim		case '6':
168314564Sdim			family = PF_INET6;
169314564Sdim			break;
170292932Sdim
171314564Sdim		case '8':
172314564Sdim			eight = 1;
173292932Sdim			break;
174314564Sdim		case 'D':
175292932Sdim			Dflag = 1;
176314564Sdim			break;
177292932Sdim		case 'E':
178360784Sdim			noescape = 1;
179360784Sdim			break;
180292932Sdim		case 'd':
181360784Sdim			dflag = 1;
182360784Sdim			break;
183292932Sdim		case 'e':
184360784Sdim			noescape = 0;
185360784Sdim			escapechar = getescape(optarg);
186360784Sdim			break;
187360784Sdim		case 'i':
188360784Sdim			if (getuid() != 0)
189360784Sdim				errx(1, "-i user: permission denied");
190314564Sdim			localname = optarg;
191314564Sdim			break;
192314564Sdim		case 'l':
193292932Sdim			user = optarg;
194360784Sdim			break;
195360784Sdim		case '?':
196314564Sdim		default:
197314564Sdim			usage();
198292932Sdim		}
199314564Sdim	optind += argoff;
200292932Sdim
201360784Sdim	/* if haven't gotten a host yet, do so */
202360784Sdim	if (!host && !(host = argv[optind++]))
203292932Sdim		usage();
204314564Sdim
205314564Sdim	if (argv[optind])
206292932Sdim		usage();
207360784Sdim
208314564Sdim	if (!(pw = getpwuid(uid = getuid())))
209314564Sdim		errx(1, "unknown user id");
210314564Sdim	if (!user)
211314564Sdim		user = pw->pw_name;
212360784Sdim	if (!localname)
213292932Sdim		localname = pw->pw_name;
214360784Sdim
215360784Sdim	sp = NULL;
216314564Sdim	sp = getservbyname("login", "tcp");
217292932Sdim	if (sp == NULL)
218314564Sdim		errx(1, "login/tcp: unknown service");
219314564Sdim
220292932Sdim	if ((p = getenv("TERM")) != NULL)
221360784Sdim		(void)strlcpy(term, p, sizeof(term));
222360784Sdim	len = strlen(term);
223292932Sdim	if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) {
224314564Sdim		/* start at 2 to include the / */
225292932Sdim		for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++)
226314564Sdim			i /= 10;
227360784Sdim		if (len + len2 < sizeof(term))
228292932Sdim			(void)snprintf(term + len, len2 + 1, "/%d", ospeed);
229360784Sdim	}
230314564Sdim
231314564Sdim	(void)get_window_size(0, &winsize);
232292932Sdim
233314564Sdim	(void)signal(SIGPIPE, lostpeer);
234314564Sdim	/* will use SIGUSR1 for window size hack, so hold it off */
235292932Sdim	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
236314564Sdim	/*
237314564Sdim	 * We set SIGURG and SIGUSR1 below so that an
238360784Sdim	 * incoming signal will be held pending rather than being
239292932Sdim	 * discarded. Note that these routines will be ready to get
240360784Sdim	 * a signal by the time that they are unblocked below.
241314564Sdim	 */
242314564Sdim	(void)signal(SIGURG, copytochild);
243292932Sdim	(void)signal(SIGUSR1, writeroob);
244314564Sdim
245314564Sdim	rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
246292932Sdim
247314564Sdim	if (rem < 0)
248314564Sdim		exit(1);
249314564Sdim
250314564Sdim	if (dflag &&
251314564Sdim	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
252292932Sdim		warn("setsockopt");
253360784Sdim	if (Dflag &&
254314564Sdim	    setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
255292932Sdim		warn("setsockopt NODELAY (ignored)");
256314564Sdim
257314564Sdim	sslen = sizeof(ss);
258360784Sdim	one = IPTOS_LOWDELAY;
259292932Sdim	if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
260360784Sdim	    ss.ss_family == AF_INET) {
261360784Sdim		if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
262314564Sdim			       sizeof(int)) < 0)
263314564Sdim			warn("setsockopt TOS (ignored)");
264292932Sdim	} else
265314564Sdim		if (ss.ss_family == AF_INET)
266314564Sdim			warn("setsockopt getsockname failed");
267292932Sdim
268314564Sdim	(void)setuid(uid);
269341825Sdim	doit(omask);
270292932Sdim	/*NOTREACHED*/
271314564Sdim}
272314564Sdim
273314564Sdimint child;
274314564Sdim
275314564Sdimvoid
276314564Sdimdoit(long omask)
277314564Sdim{
278314564Sdim
279314564Sdim	(void)signal(SIGINT, SIG_IGN);
280314564Sdim	setsignal(SIGHUP);
281314564Sdim	setsignal(SIGQUIT);
282314564Sdim	mode(1);
283314564Sdim	child = fork();
284314564Sdim	if (child == -1) {
285314564Sdim		warn("fork");
286314564Sdim		done(1);
287314564Sdim	}
288314564Sdim	if (child == 0) {
289314564Sdim		if (reader(omask) == 0) {
290314564Sdim			msg("connection closed");
291314564Sdim			exit(0);
292314564Sdim		}
293314564Sdim		sleep(1);
294314564Sdim		msg("\007connection closed");
295314564Sdim		exit(1);
296314564Sdim	}
297314564Sdim
298360784Sdim	/*
299314564Sdim	 * We may still own the socket, and may have a pending SIGURG (or might
300360784Sdim	 * receive one soon) that we really want to send to the reader.  When
301314564Sdim	 * one of these comes in, the trap copytochild simply copies such
302314564Sdim	 * signals to the child. We can now unblock SIGURG and SIGUSR1
303314564Sdim	 * that were set above.
304314564Sdim	 */
305292932Sdim	(void)sigsetmask(omask);
306314564Sdim	(void)signal(SIGCHLD, catch_child);
307314564Sdim	writer();
308314564Sdim	msg("closed connection");
309314564Sdim	done(0);
310314564Sdim}
311314564Sdim
312292932Sdim/* trap a signal, unless it is being ignored. */
313314564Sdimvoid
314314564Sdimsetsignal(int sig)
315344779Sdim{
316344779Sdim	int omask = sigblock(sigmask(sig));
317344779Sdim
318314564Sdim	if (signal(sig, exit) == SIG_IGN)
319314564Sdim		(void)signal(sig, SIG_IGN);
320292932Sdim	(void)sigsetmask(omask);
321360784Sdim}
322292932Sdim
323314564Sdimvoid
324314564Sdimdone(int status)
325314564Sdim{
326314564Sdim	int w, wstatus;
327314564Sdim
328292932Sdim	mode(0);
329314564Sdim	if (child > 0) {
330314564Sdim		/* make sure catch_child does not snap it up */
331314564Sdim		(void)signal(SIGCHLD, SIG_DFL);
332292932Sdim		if (kill(child, SIGKILL) >= 0)
333360784Sdim			while ((w = wait(&wstatus)) > 0 && w != child);
334314564Sdim	}
335292932Sdim	exit(status);
336314564Sdim}
337292932Sdim
338360784Sdimint dosigwinch;
339360784Sdim
340292932Sdim/*
341314564Sdim * This is called when the reader process gets the out-of-band (urgent)
342292932Sdim * request to turn on the window-changing protocol.
343314564Sdim */
344314564Sdim/* ARGSUSED */
345353358Sdimvoid
346353358Sdimwriteroob(int signo __unused)
347314564Sdim{
348292932Sdim	if (dosigwinch == 0) {
349341825Sdim		sendwindow();
350341825Sdim		(void)signal(SIGWINCH, sigwinch);
351341825Sdim	}
352314564Sdim	dosigwinch = 1;
353314564Sdim}
354314564Sdim
355292932Sdim/* ARGSUSED */
356314564Sdimvoid
357314564Sdimcatch_child(int signo __unused)
358360784Sdim{
359292932Sdim	pid_t pid;
360314564Sdim	int status;
361314564Sdim
362314564Sdim	for (;;) {
363292932Sdim		pid = wait3(&status, WNOHANG|WUNTRACED, NULL);
364314564Sdim		if (pid == 0)
365292932Sdim			return;
366314564Sdim		/* if the child (reader) dies, just quit */
367314564Sdim		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
368314564Sdim			done(WTERMSIG(status) | WEXITSTATUS(status));
369314564Sdim	}
370314564Sdim	/* NOTREACHED */
371360784Sdim}
372360784Sdim
373314564Sdim/*
374314564Sdim * writer: write to remote: 0 -> line.
375314564Sdim * ~.				terminate
376314564Sdim * ~^Z				suspend rlogin process.
377314564Sdim * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
378314564Sdim */
379314564Sdimvoid
380314564Sdimwriter(void)
381314564Sdim{
382314564Sdim	int bol, local, n;
383314564Sdim	char c;
384314564Sdim
385360784Sdim	bol = 1;			/* beginning of line */
386314564Sdim	local = 0;
387360784Sdim	for (;;) {
388360784Sdim		n = read(STDIN_FILENO, &c, 1);
389314564Sdim		if (n <= 0) {
390314564Sdim			if (n < 0 && errno == EINTR)
391314564Sdim				continue;
392292932Sdim			break;
393292932Sdim		}
394314564Sdim		/*
395292932Sdim		 * If we're at the beginning of the line and recognize a
396314564Sdim		 * command character, then we echo locally.  Otherwise,
397314564Sdim		 * characters are echo'd remotely.  If the command character
398314564Sdim		 * is doubled, this acts as a force and local echo is
399360784Sdim		 * suppressed.
400360784Sdim		 */
401314564Sdim		if (bol) {
402314564Sdim			bol = 0;
403314564Sdim			if (!noescape && c == escapechar) {
404292932Sdim				local = 1;
405314564Sdim				continue;
406314564Sdim			}
407314564Sdim		} else if (local) {
408292932Sdim			local = 0;
409314564Sdim			if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
410292932Sdim				echo(c);
411314564Sdim				break;
412314564Sdim			}
413292932Sdim			if (CCEQ(deftty.c_cc[VSUSP], c) ||
414314564Sdim			    CCEQ(deftty.c_cc[VDSUSP], c)) {
415314564Sdim				bol = 1;
416314564Sdim				echo(c);
417314564Sdim				stop(c);
418292932Sdim				continue;
419314564Sdim			}
420292932Sdim			if (c != escapechar)
421314564Sdim				(void)write(rem, &escapechar, 1);
422314564Sdim		}
423314564Sdim
424292932Sdim		if (write(rem, &c, 1) == 0) {
425314564Sdim			msg("line gone");
426314564Sdim			break;
427292932Sdim		}
428314564Sdim		bol = CCEQ(deftty.c_cc[VKILL], c) ||
429314564Sdim		    CCEQ(deftty.c_cc[VEOF], c) ||
430314564Sdim		    CCEQ(deftty.c_cc[VINTR], c) ||
431353358Sdim		    CCEQ(deftty.c_cc[VSUSP], c) ||
432314564Sdim		    c == '\r' || c == '\n';
433353358Sdim	}
434353358Sdim}
435353358Sdim
436314564Sdimvoid
437314564Sdimecho(char c)
438314564Sdim{
439314564Sdim	char *p;
440314564Sdim	char buf[8];
441314564Sdim
442314564Sdim	p = buf;
443314564Sdim	c &= 0177;
444314564Sdim	*p++ = escapechar;
445360784Sdim	if (c < ' ') {
446360784Sdim		*p++ = '^';
447292932Sdim		*p++ = c + '@';
448314564Sdim	} else if (c == 0177) {
449314564Sdim		*p++ = '^';
450314564Sdim		*p++ = '?';
451314564Sdim	} else
452314564Sdim		*p++ = c;
453314564Sdim	*p++ = '\r';
454314564Sdim	*p++ = '\n';
455314564Sdim	(void)write(STDOUT_FILENO, buf, p - buf);
456314564Sdim}
457314564Sdim
458314564Sdimvoid
459314564Sdimstop(char cmdc)
460314564Sdim{
461314564Sdim	mode(0);
462314564Sdim	(void)signal(SIGCHLD, SIG_IGN);
463314564Sdim	(void)kill(CCEQ(deftty.c_cc[VSUSP], cmdc) ? 0 : getpid(), SIGTSTP);
464341825Sdim	(void)signal(SIGCHLD, catch_child);
465292932Sdim	mode(1);
466314564Sdim	sigwinch(0);			/* check for size changes */
467292932Sdim}
468314564Sdim
469314564Sdim/* ARGSUSED */
470314564Sdimvoid
471314564Sdimsigwinch(int signo __unused)
472314564Sdim{
473292932Sdim	struct winsize ws;
474314564Sdim
475292932Sdim	if (dosigwinch && get_window_size(0, &ws) == 0 &&
476353358Sdim	    bcmp(&ws, &winsize, sizeof(ws))) {
477314564Sdim		winsize = ws;
478292932Sdim		sendwindow();
479314564Sdim	}
480314564Sdim}
481314564Sdim
482314564Sdim/*
483353358Sdim * Send the window size to the server via the magic escape
484353358Sdim */
485314564Sdimvoid
486292932Sdimsendwindow(void)
487353358Sdim{
488292932Sdim	struct winsize *wp;
489314564Sdim	char obuf[4 + sizeof (struct winsize)];
490314564Sdim
491292932Sdim	wp = (struct winsize *)(obuf+4);
492314564Sdim	obuf[0] = 0377;
493314564Sdim	obuf[1] = 0377;
494314564Sdim	obuf[2] = 's';
495314564Sdim	obuf[3] = 's';
496314564Sdim	wp->ws_row = htons(winsize.ws_row);
497314564Sdim	wp->ws_col = htons(winsize.ws_col);
498314564Sdim	wp->ws_xpixel = htons(winsize.ws_xpixel);
499314564Sdim	wp->ws_ypixel = htons(winsize.ws_ypixel);
500314564Sdim
501314564Sdim	(void)write(rem, obuf, sizeof(obuf));
502314564Sdim}
503314564Sdim
504314564Sdim/*
505314564Sdim * reader: read from remote: line -> 1
506314564Sdim */
507314564Sdim#define	READING	1
508314564Sdim#define	WRITING	2
509314564Sdim
510321369Sdimjmp_buf rcvtop;
511314564Sdimint rcvcnt, rcvstate;
512321369Sdimpid_t ppid;
513314564Sdimchar rcvbuf[8 * 1024];
514314564Sdim
515314564Sdim/* ARGSUSED */
516314564Sdimvoid
517314564Sdimoob(int signo __unused)
518314564Sdim{
519314564Sdim	struct termios tty;
520314564Sdim	int atmark, n, rcvd;
521314564Sdim	char waste[BUFSIZ], mark;
522314564Sdim
523314564Sdim	rcvd = 0;
524314564Sdim	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
525314564Sdim		switch (errno) {
526314564Sdim		case EWOULDBLOCK:
527314564Sdim			/*
528314564Sdim			 * Urgent data not here yet.  It may not be possible
529314564Sdim			 * to send it yet if we are blocked for output and
530314564Sdim			 * our input buffer is full.
531314564Sdim			 */
532314564Sdim			if (rcvcnt < (int)sizeof(rcvbuf)) {
533314564Sdim				n = read(rem, rcvbuf + rcvcnt,
534314564Sdim				    sizeof(rcvbuf) - rcvcnt);
535314564Sdim				if (n <= 0)
536314564Sdim					return;
537314564Sdim				rcvd += n;
538314564Sdim			} else {
539360784Sdim				n = read(rem, waste, sizeof(waste));
540314564Sdim				if (n <= 0)
541314564Sdim					return;
542314564Sdim			}
543314564Sdim			continue;
544314564Sdim		default:
545314564Sdim			return;
546314564Sdim		}
547314564Sdim	}
548314564Sdim	if (mark & TIOCPKT_WINDOW) {
549314564Sdim		/* Let server know about window size changes */
550314564Sdim		(void)kill(ppid, SIGUSR1);
551314564Sdim	}
552314564Sdim	if (!eight && (mark & TIOCPKT_NOSTOP)) {
553314564Sdim		(void)tcgetattr(0, &tty);
554314564Sdim		tty.c_iflag &= ~IXON;
555314564Sdim		(void)tcsetattr(0, TCSANOW, &tty);
556314564Sdim	}
557314564Sdim	if (!eight && (mark & TIOCPKT_DOSTOP)) {
558314564Sdim		(void)tcgetattr(0, &tty);
559360784Sdim		tty.c_iflag |= (deftty.c_iflag & IXON);
560360784Sdim		(void)tcsetattr(0, TCSANOW, &tty);
561314564Sdim	}
562360784Sdim	if (mark & TIOCPKT_FLUSHWRITE) {
563360784Sdim		(void)tcflush(1, TCIOFLUSH);
564314564Sdim		for (;;) {
565314564Sdim			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
566314564Sdim				warn("ioctl");
567314564Sdim				break;
568314564Sdim			}
569360784Sdim			if (atmark)
570292932Sdim				break;
571314564Sdim			n = read(rem, waste, sizeof (waste));
572314564Sdim			if (n <= 0)
573314564Sdim				break;
574292932Sdim		}
575314564Sdim		/*
576292932Sdim		 * Don't want any pending data to be output, so clear the recv
577314564Sdim		 * buffer.  If we were hanging on a write when interrupted,
578360784Sdim		 * don't want it to restart.  If we were reading, restart
579292932Sdim		 * anyway.
580314564Sdim		 */
581314564Sdim		rcvcnt = 0;
582292932Sdim		longjmp(rcvtop, 1);
583314564Sdim	}
584314564Sdim
585292932Sdim	/* oob does not do FLUSHREAD (alas!) */
586314564Sdim
587314564Sdim	/*
588292932Sdim	 * If we filled the receive buffer while a read was pending, longjmp
589314564Sdim	 * to the top to restart appropriately.  Don't abort a pending write,
590360784Sdim	 * however, or we won't know how much was written.
591360784Sdim	 */
592292932Sdim	if (rcvd && rcvstate == READING)
593314564Sdim		longjmp(rcvtop, 1);
594314564Sdim}
595292932Sdim
596314564Sdim/* reader: read from remote: line -> 1 */
597314564Sdimint
598292932Sdimreader(int omask)
599314564Sdim{
600314564Sdim	int n, remaining;
601314564Sdim	char *bufp;
602314564Sdim	pid_t pid;
603314564Sdim
604314564Sdim	pid = getpid();
605314564Sdim	(void)signal(SIGTTOU, SIG_IGN);
606314564Sdim	(void)signal(SIGURG, oob);
607292932Sdim	(void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */
608314564Sdim	ppid = getppid();
609292932Sdim	(void)fcntl(rem, F_SETOWN, pid);
610360784Sdim	(void)setjmp(rcvtop);
611360784Sdim	(void)sigsetmask(omask);
612360784Sdim	bufp = rcvbuf;
613360784Sdim	for (;;) {
614360784Sdim		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
615314564Sdim			rcvstate = WRITING;
616314564Sdim			n = write(STDOUT_FILENO, bufp, remaining);
617314564Sdim			if (n < 0) {
618292932Sdim				if (errno != EINTR)
619314564Sdim					return (-1);
620314564Sdim				continue;
621314564Sdim			}
622314564Sdim			bufp += n;
623314564Sdim		}
624314564Sdim		bufp = rcvbuf;
625360784Sdim		rcvcnt = 0;
626292932Sdim		rcvstate = READING;
627314564Sdim
628314564Sdim		rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
629314564Sdim		if (rcvcnt == 0)
630292932Sdim			return (0);
631314564Sdim		if (rcvcnt < 0) {
632314564Sdim			if (errno == EINTR)
633292932Sdim				continue;
634314564Sdim			warn("read");
635292932Sdim			return (-1);
636314564Sdim		}
637360784Sdim	}
638360784Sdim}
639292932Sdim
640314564Sdimvoid
641314564Sdimmode(int f)
642314564Sdim{
643292932Sdim	struct termios tty;
644314564Sdim
645314564Sdim	switch (f) {
646292932Sdim	case 0:
647314564Sdim		(void)tcsetattr(0, TCSANOW, &deftty);
648292932Sdim		break;
649314564Sdim	case 1:
650314564Sdim		(void)tcgetattr(0, &deftty);
651314564Sdim		tty = deftty;
652314564Sdim		/* This is loosely derived from sys/kern/tty_compat.c. */
653314564Sdim		tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
654314564Sdim		tty.c_iflag &= ~ICRNL;
655314564Sdim		tty.c_oflag &= ~OPOST;
656292932Sdim		tty.c_cc[VMIN] = 1;
657314564Sdim		tty.c_cc[VTIME] = 0;
658360784Sdim		if (eight) {
659360784Sdim			tty.c_iflag &= IXOFF;
660292932Sdim			tty.c_cflag &= ~(CSIZE|PARENB);
661314564Sdim			tty.c_cflag |= CS8;
662314564Sdim		}
663292932Sdim		(void)tcsetattr(0, TCSANOW, &tty);
664314564Sdim		break;
665314564Sdim	default:
666292932Sdim		return;
667314564Sdim	}
668360784Sdim}
669360784Sdim
670292932Sdim/* ARGSUSED */
671314564Sdimvoid
672314564Sdimlostpeer(int signo __unused)
673314564Sdim{
674292932Sdim	(void)signal(SIGPIPE, SIG_IGN);
675314564Sdim	msg("\007connection closed");
676314564Sdim	done(1);
677314564Sdim}
678314564Sdim
679314564Sdim/* copy SIGURGs to the child process via SIGUSR1. */
680314564Sdim/* ARGSUSED */
681314564Sdimvoid
682314564Sdimcopytochild(int signo __unused)
683314564Sdim{
684314564Sdim	(void)kill(child, SIGUSR1);
685314564Sdim}
686314564Sdim
687314564Sdimvoid
688314564Sdimmsg(const char *str)
689314564Sdim{
690314564Sdim	(void)fprintf(stderr, "rlogin: %s\r\n", str);
691314564Sdim}
692314564Sdim
693314564Sdimvoid
694314564Sdimusage(void)
695314564Sdim{
696314564Sdim	(void)fprintf(stderr,
697314564Sdim	"usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
698314564Sdim	    "8DEd", " ");
699314564Sdim	exit(1);
700314564Sdim}
701314564Sdim
702314564Sdimu_int
703314564Sdimgetescape(const char *p)
704314564Sdim{
705314564Sdim	long val;
706314564Sdim	size_t len;
707314564Sdim
708314564Sdim	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
709314564Sdim		return ((u_int)*p);
710314564Sdim					/* otherwise, \nnn */
711360784Sdim	if (*p == '\\' && len >= 2 && len <= 4) {
712360784Sdim		val = strtol(++p, NULL, 8);
713360784Sdim		for (;;) {
714360784Sdim			if (!*++p)
715360784Sdim				return ((u_int)val);
716360784Sdim			if (*p < '0' || *p > '8')
717314564Sdim				break;
718314564Sdim		}
719353358Sdim	}
720314564Sdim	msg("illegal option value -- e");
721314564Sdim	usage();
722360784Sdim	/* NOTREACHED */
723292932Sdim}
724314564Sdim