rlogin.c revision 57450
150477Speter/*
240269Srnordier * Copyright (c) 1983, 1990, 1993
3213136Spjd *	The Regents of the University of California.  All rights reserved.
440326Srnordier *
5172940Sjhb * Redistribution and use in source and binary forms, with or without
6172940Sjhb * modification, are permitted provided that the following conditions
780751Sjhb * are met:
880751Sjhb * 1. Redistributions of source code must retain the above copyright
942480Srnordier *    notice, this list of conditions and the following disclaimer.
1042480Srnordier * 2. Redistributions in binary form must reproduce the above copyright
1140541Srnordier *    notice, this list of conditions and the following disclaimer in the
1240541Srnordier *    documentation and/or other materials provided with the distribution.
13104673Sgreen * 3. All advertising materials mentioning features or use of this software
1440269Srnordier *    must display the following acknowledgement:
15172940Sjhb *	This product includes software developed by the University of
1640269Srnordier *	California, Berkeley and its contributors.
17125537Sru * 4. Neither the name of the University nor the names of its contributors
18172940Sjhb *    may be used to endorse or promote products derived from this software
19172940Sjhb *    without specific prior written permission.
20172940Sjhb *
21104635Sphk * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22213136Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23213136Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24132870Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25132870Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26132870Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27108149Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2896327Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29148046Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30213136Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31172940Sjhb * SUCH DAMAGE.
32125932Sru */
33125932Sru
34125932Sru#ifndef lint
3597860Sphkstatic const char copyright[] =
36213136Spjd"@(#) Copyright (c) 1983, 1990, 1993\n\
37173026Sjhb	The Regents of the University of California.  All rights reserved.\n";
38172940Sjhb#endif /* not lint */
39213568Spho
4040269Srnordier#ifndef lint
4140269Srnordierstatic const char sccsid[] = "@(#)rlogin.c	8.1 (Berkeley) 6/6/93";
42169732Skanstatic const char rcsid[] =
43169732Skan  "$FreeBSD: head/usr.bin/rlogin/rlogin.c 57450 2000-02-24 21:06:22Z markm $";
4440269Srnordier#endif /* not lint */
45125621Sru
4640269Srnordier/*
47125537Sru * rlogin - remote login
48125537Sru */
4940269Srnordier#include <sys/param.h>
50172940Sjhb#include <sys/socket.h>
51125537Sru#include <sys/time.h>
52172940Sjhb#include <sys/resource.h>
53172940Sjhb#include <sys/wait.h>
54172940Sjhb
55109886Sphk#include <netinet/in.h>
56172940Sjhb#include <netinet/in_systm.h>
57125537Sru#include <netinet/ip.h>
58172940Sjhb#include <netinet/tcp.h>
59172940Sjhb
6040269Srnordier#include <err.h>
61172940Sjhb#include <errno.h>
62172940Sjhb#include <fcntl.h>
6340269Srnordier#include <libutil.h>
64213136Spjd#include <netdb.h>
6596424Speter#include <pwd.h>
66172940Sjhb#include <setjmp.h>
67172940Sjhb#include <sgtty.h>
6880751Sjhb#include <signal.h>
69213136Spjd#include <stdio.h>
70213136Spjd#include <stdlib.h>
7140269Srnordier#include <string.h>
72172940Sjhb#include <unistd.h>
7380751Sjhb#include <err.h>
74211677Simp
75172940Sjhb#ifdef KERBEROS
76125556Sru#include <openssl/des.h>
77116864Speter#include <krb.h>
78116864Speter
79116864Speter#include "../../bin/rcp/pathnames.h"
80116864Speter#include "krb.h"
81125537Sru
82CREDENTIALS cred;
83Key_schedule schedule;
84int use_kerberos = 1, doencrypt;
85char 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
97int eight, litout, rem;
98int family = PF_UNSPEC;
99
100int noescape;
101u_char escapechar = '~';
102
103char *speeds[] = {
104	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
105	"1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
106#define	MAX_SPEED_LENGTH	(sizeof("115200") - 1)
107};
108
109#ifdef OLDSUN
110struct winsize {
111	unsigned short ws_row, ws_col;
112	unsigned short ws_xpixel, ws_ypixel;
113};
114#else
115#define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
116#endif
117struct	winsize winsize;
118
119void		catch_child __P((int));
120void		copytochild __P((int));
121void		doit __P((long)) __dead2;
122void		done __P((int)) __dead2;
123void		echo __P((char));
124u_int		getescape __P((char *));
125void		lostpeer __P((int));
126void		mode __P((int));
127void		msg __P((char *));
128void		oob __P((int));
129int		reader __P((int));
130void		sendwindow __P((void));
131void		setsignal __P((int));
132void		sigwinch __P((int));
133void		stop __P((char));
134void		usage __P((void)) __dead2;
135void		writer __P((void));
136void		writeroob __P((int));
137
138#ifdef OLDSUN
139int		get_window_size __P((int, struct winsize *));
140#endif
141
142int
143main(argc, argv)
144	int argc;
145	char *argv[];
146{
147	extern char *optarg;
148	extern int optind;
149	struct passwd *pw;
150	struct servent *sp;
151	struct sgttyb ttyb;
152	long omask;
153	int argoff, ch, dflag, Dflag, one, uid;
154	char *host, *localname, *p, *user, term[1024];
155#ifdef KERBEROS
156	char *k;
157#endif
158        struct sockaddr_storage ss;
159	int sslen;
160
161	argoff = dflag = Dflag = 0;
162	one = 1;
163	host = localname = user = NULL;
164
165	if ((p = rindex(argv[0], '/')))
166		++p;
167	else
168		p = argv[0];
169
170	if (strcmp(p, "rlogin"))
171		host = p;
172
173	/* handle "rlogin host flags" */
174	if (!host && argc > 2 && argv[1][0] != '-') {
175		host = argv[1];
176		argoff = 1;
177	}
178
179#ifdef KERBEROS
180#define	OPTIONS	"468DEKLde:i:k:l:x"
181#else
182#define	OPTIONS	"468DEKLde:i:l:"
183#endif
184	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
185		switch(ch) {
186		case '4':
187			family = PF_INET;
188			break;
189
190		case '6':
191			family = PF_INET6;
192			break;
193
194		case '8':
195			eight = 1;
196			break;
197		case 'D':
198			Dflag = 1;
199			break;
200		case 'E':
201			noescape = 1;
202			break;
203		case 'K':
204#ifdef KERBEROS
205			use_kerberos = 0;
206#endif
207			break;
208		case 'L':
209			litout = 1;
210			break;
211		case 'd':
212			dflag = 1;
213			break;
214		case 'e':
215			noescape = 0;
216			escapechar = getescape(optarg);
217			break;
218		case 'i':
219			if (getuid() != 0)
220				errx(1, "-i user: permission denied");
221			localname = optarg;
222			break;
223#ifdef KERBEROS
224		case 'k':
225			dest_realm = dst_realm_buf;
226			(void)strncpy(dest_realm, optarg, REALM_SZ);
227			break;
228#endif
229		case 'l':
230			user = optarg;
231			break;
232#ifdef CRYPT
233#ifdef KERBEROS
234		case 'x':
235			doencrypt = 1;
236			break;
237#endif
238#endif
239		case '?':
240		default:
241			usage();
242		}
243	optind += argoff;
244
245	/* if haven't gotten a host yet, do so */
246	if (!host && !(host = argv[optind++]))
247		usage();
248
249	if (argv[optind])
250		usage();
251
252	if (!(pw = getpwuid(uid = getuid())))
253		errx(1, "unknown user id");
254	if (!user)
255		user = pw->pw_name;
256	if (!localname)
257		localname = pw->pw_name;
258
259	sp = NULL;
260#ifdef KERBEROS
261	k = auth_getval("auth_list");
262	if (k && !strstr(k, "kerberos"))
263	    use_kerberos = 0;
264	if (use_kerberos) {
265		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
266		if (sp == NULL) {
267			use_kerberos = 0;
268			warn("can't get entry for %s/tcp service",
269			    doencrypt ? "eklogin" : "klogin");
270		}
271	}
272#endif
273	if (sp == NULL)
274		sp = getservbyname("login", "tcp");
275	if (sp == NULL)
276		errx(1, "login/tcp: unknown service");
277
278#define	MAX_TERM_LENGTH	(sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
279
280	(void)strncpy(term, (p = getenv("TERM")) ? p : "network",
281		      MAX_TERM_LENGTH);
282	term[MAX_TERM_LENGTH] = '\0';
283	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
284		(void)strcat(term, "/");
285		(void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
286	}
287
288	(void)get_window_size(0, &winsize);
289
290	(void)signal(SIGPIPE, lostpeer);
291	/* will use SIGUSR1 for window size hack, so hold it off */
292	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
293	/*
294	 * We set SIGURG and SIGUSR1 below so that an
295	 * incoming signal will be held pending rather than being
296	 * discarded. Note that these routines will be ready to get
297	 * a signal by the time that they are unblocked below.
298	 */
299	(void)signal(SIGURG, copytochild);
300	(void)signal(SIGUSR1, writeroob);
301
302#ifdef KERBEROS
303	if (use_kerberos) {
304		setuid(getuid());
305		rem = KSUCCESS;
306		errno = 0;
307		if (dest_realm == NULL)
308			dest_realm = krb_realmofhost(host);
309
310#ifdef CRYPT
311		if (doencrypt) {
312			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
313			    dest_realm, &cred, schedule);
314			des_set_key(&cred.session, schedule);
315		} else
316#endif /* CRYPT */
317			rem = krcmd(&host, sp->s_port, user, term, 0,
318			    dest_realm);
319		if (rem < 0) {
320			int i;
321			char **newargv;
322
323			sp = getservbyname("login", "tcp");
324			if (sp == NULL)
325				errx(1, "unknown service login/tcp");
326			if (errno == ECONNREFUSED)
327				warn("remote host doesn't support Kerberos");
328			if (errno == ENOENT)
329				warn("can't provide Kerberos auth data");
330			newargv = malloc((argc + 2) * sizeof(*newargv));
331			if (newargv == NULL)
332				err(1, "malloc");
333			newargv[0] = argv[0];
334			newargv[1] = "-K";
335			for(i = 1; i < argc; ++i)
336			newargv[i + 1] = argv[i];
337			newargv[argc + 1] = NULL;
338			execv(_PATH_RLOGIN, newargv);
339		}
340	} else {
341#ifdef CRYPT
342		if (doencrypt)
343			errx(1, "the -x flag requires Kerberos authentication");
344#endif /* CRYPT */
345		rem = rcmd_af(&host, sp->s_port, localname, user, term, 0,
346			      family);
347	}
348#else
349	rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
350#endif /* KERBEROS */
351
352	if (rem < 0)
353		exit(1);
354
355	if (dflag &&
356	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
357		warn("setsockopt");
358	if (Dflag &&
359	    setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
360		warn("setsockopt NODELAY (ignored)");
361
362	sslen = sizeof(ss);
363	one = IPTOS_LOWDELAY;
364	if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
365	    ss.ss_family == AF_INET) {
366		if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
367			       sizeof(int)) < 0)
368			warn("setsockopt TOS (ignored)");
369	} else
370		if (ss.ss_family == AF_INET)
371			warn("setsockopt getsockname failed");
372
373	(void)setuid(uid);
374	doit(omask);
375	/*NOTREACHED*/
376}
377
378int child, defflags, deflflags, tabflag;
379char deferase, defkill;
380struct tchars deftc;
381struct ltchars defltc;
382struct tchars notc = { -1, -1, -1, -1, -1, -1 };
383struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
384
385void
386doit(omask)
387	long omask;
388{
389	struct sgttyb sb;
390
391	(void)ioctl(0, TIOCGETP, (char *)&sb);
392	defflags = sb.sg_flags;
393	tabflag = defflags & TBDELAY;
394	defflags &= ECHO | CRMOD;
395	deferase = sb.sg_erase;
396	defkill = sb.sg_kill;
397	(void)ioctl(0, TIOCLGET, &deflflags);
398	(void)ioctl(0, TIOCGETC, &deftc);
399	notc.t_startc = deftc.t_startc;
400	notc.t_stopc = deftc.t_stopc;
401	(void)ioctl(0, TIOCGLTC, &defltc);
402	(void)signal(SIGINT, SIG_IGN);
403	setsignal(SIGHUP);
404	setsignal(SIGQUIT);
405	child = fork();
406	if (child == -1) {
407		warn("fork");
408		done(1);
409	}
410	if (child == 0) {
411		mode(1);
412		if (reader(omask) == 0) {
413			msg("connection closed.");
414			exit(0);
415		}
416		sleep(1);
417		msg("\007connection closed.");
418		exit(1);
419	}
420
421	/*
422	 * We may still own the socket, and may have a pending SIGURG (or might
423	 * receive one soon) that we really want to send to the reader.  When
424	 * one of these comes in, the trap copytochild simply copies such
425	 * signals to the child. We can now unblock SIGURG and SIGUSR1
426	 * that were set above.
427	 */
428	(void)sigsetmask(omask);
429	(void)signal(SIGCHLD, catch_child);
430	writer();
431	msg("closed connection.");
432	done(0);
433}
434
435/* trap a signal, unless it is being ignored. */
436void
437setsignal(sig)
438	int sig;
439{
440	int omask = sigblock(sigmask(sig));
441
442	if (signal(sig, exit) == SIG_IGN)
443		(void)signal(sig, SIG_IGN);
444	(void)sigsetmask(omask);
445}
446
447void
448done(status)
449	int status;
450{
451	int w, wstatus;
452
453	mode(0);
454	if (child > 0) {
455		/* make sure catch_child does not snap it up */
456		(void)signal(SIGCHLD, SIG_DFL);
457		if (kill(child, SIGKILL) >= 0)
458			while ((w = wait(&wstatus)) > 0 && w != child);
459	}
460	exit(status);
461}
462
463int dosigwinch;
464
465/*
466 * This is called when the reader process gets the out-of-band (urgent)
467 * request to turn on the window-changing protocol.
468 */
469void
470writeroob(signo)
471	int signo;
472{
473	if (dosigwinch == 0) {
474		sendwindow();
475		(void)signal(SIGWINCH, sigwinch);
476	}
477	dosigwinch = 1;
478}
479
480void
481catch_child(signo)
482	int signo;
483{
484	union wait status;
485	int pid;
486
487	for (;;) {
488		pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
489		if (pid == 0)
490			return;
491		/* if the child (reader) dies, just quit */
492		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
493			done((int)(status.w_termsig | status.w_retcode));
494	}
495	/* NOTREACHED */
496}
497
498/*
499 * writer: write to remote: 0 -> line.
500 * ~.				terminate
501 * ~^Z				suspend rlogin process.
502 * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
503 */
504void
505writer()
506{
507	register int bol, local, n;
508	char c;
509
510	bol = 1;			/* beginning of line */
511	local = 0;
512	for (;;) {
513		n = read(STDIN_FILENO, &c, 1);
514		if (n <= 0) {
515			if (n < 0 && errno == EINTR)
516				continue;
517			break;
518		}
519		/*
520		 * If we're at the beginning of the line and recognize a
521		 * command character, then we echo locally.  Otherwise,
522		 * characters are echo'd remotely.  If the command character
523		 * is doubled, this acts as a force and local echo is
524		 * suppressed.
525		 */
526		if (bol) {
527			bol = 0;
528			if (!noescape && c == escapechar) {
529				local = 1;
530				continue;
531			}
532		} else if (local) {
533			local = 0;
534			if (c == '.' || c == deftc.t_eofc) {
535				echo(c);
536				break;
537			}
538			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
539				bol = 1;
540				echo(c);
541				stop(c);
542				continue;
543			}
544			if (c != escapechar)
545#ifdef CRYPT
546#ifdef KERBEROS
547				if (doencrypt)
548					(void)des_enc_write(rem,
549					    (char *)&escapechar, 1,
550						schedule, &cred.session);
551				else
552#endif
553#endif
554					(void)write(rem, &escapechar, 1);
555		}
556
557#ifdef CRYPT
558#ifdef KERBEROS
559		if (doencrypt) {
560			if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
561				msg("line gone");
562				break;
563			}
564		} else
565#endif
566#endif
567			if (write(rem, &c, 1) == 0) {
568				msg("line gone");
569				break;
570			}
571		bol = c == defkill || c == deftc.t_eofc ||
572		    c == deftc.t_intrc || c == defltc.t_suspc ||
573		    c == '\r' || c == '\n';
574	}
575}
576
577void
578#if __STDC__
579echo(register char c)
580#else
581echo(c)
582	register char c;
583#endif
584{
585	register char *p;
586	char buf[8];
587
588	p = buf;
589	c &= 0177;
590	*p++ = escapechar;
591	if (c < ' ') {
592		*p++ = '^';
593		*p++ = c + '@';
594	} else if (c == 0177) {
595		*p++ = '^';
596		*p++ = '?';
597	} else
598		*p++ = c;
599	*p++ = '\r';
600	*p++ = '\n';
601	(void)write(STDOUT_FILENO, buf, p - buf);
602}
603
604void
605#if __STDC__
606stop(char cmdc)
607#else
608stop(cmdc)
609	char cmdc;
610#endif
611{
612	mode(0);
613	(void)signal(SIGCHLD, SIG_IGN);
614	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
615	(void)signal(SIGCHLD, catch_child);
616	mode(1);
617	sigwinch(0);			/* check for size changes */
618}
619
620void
621sigwinch(signo)
622	int signo;
623{
624	struct winsize ws;
625
626	if (dosigwinch && get_window_size(0, &ws) == 0 &&
627	    bcmp(&ws, &winsize, sizeof(ws))) {
628		winsize = ws;
629		sendwindow();
630	}
631}
632
633/*
634 * Send the window size to the server via the magic escape
635 */
636void
637sendwindow()
638{
639	struct winsize *wp;
640	char obuf[4 + sizeof (struct winsize)];
641
642	wp = (struct winsize *)(obuf+4);
643	obuf[0] = 0377;
644	obuf[1] = 0377;
645	obuf[2] = 's';
646	obuf[3] = 's';
647	wp->ws_row = htons(winsize.ws_row);
648	wp->ws_col = htons(winsize.ws_col);
649	wp->ws_xpixel = htons(winsize.ws_xpixel);
650	wp->ws_ypixel = htons(winsize.ws_ypixel);
651
652#ifdef CRYPT
653#ifdef KERBEROS
654	if(doencrypt)
655		(void)des_enc_write(rem, obuf, sizeof(obuf),
656			schedule, &cred.session);
657	else
658#endif
659#endif
660		(void)write(rem, obuf, sizeof(obuf));
661}
662
663/*
664 * reader: read from remote: line -> 1
665 */
666#define	READING	1
667#define	WRITING	2
668
669jmp_buf rcvtop;
670int ppid, rcvcnt, rcvstate;
671char rcvbuf[8 * 1024];
672
673void
674oob(signo)
675	int signo;
676{
677	struct sgttyb sb;
678	int atmark, n, out, rcvd;
679	char waste[BUFSIZ], mark;
680
681	out = O_RDWR;
682	rcvd = 0;
683	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
684		switch (errno) {
685		case EWOULDBLOCK:
686			/*
687			 * Urgent data not here yet.  It may not be possible
688			 * to send it yet if we are blocked for output and
689			 * our input buffer is full.
690			 */
691			if (rcvcnt < sizeof(rcvbuf)) {
692				n = read(rem, rcvbuf + rcvcnt,
693				    sizeof(rcvbuf) - rcvcnt);
694				if (n <= 0)
695					return;
696				rcvd += n;
697			} else {
698				n = read(rem, waste, sizeof(waste));
699				if (n <= 0)
700					return;
701			}
702			continue;
703		default:
704			return;
705		}
706	}
707	if (mark & TIOCPKT_WINDOW) {
708		/* Let server know about window size changes */
709		(void)kill(ppid, SIGUSR1);
710	}
711	if (!eight && (mark & TIOCPKT_NOSTOP)) {
712		(void)ioctl(0, TIOCGETP, (char *)&sb);
713		sb.sg_flags &= ~CBREAK;
714		sb.sg_flags |= RAW;
715		(void)ioctl(0, TIOCSETN, (char *)&sb);
716		notc.t_stopc = -1;
717		notc.t_startc = -1;
718		(void)ioctl(0, TIOCSETC, (char *)&notc);
719	}
720	if (!eight && (mark & TIOCPKT_DOSTOP)) {
721		(void)ioctl(0, TIOCGETP, (char *)&sb);
722		sb.sg_flags &= ~RAW;
723		sb.sg_flags |= CBREAK;
724		(void)ioctl(0, TIOCSETN, (char *)&sb);
725		notc.t_stopc = deftc.t_stopc;
726		notc.t_startc = deftc.t_startc;
727		(void)ioctl(0, TIOCSETC, (char *)&notc);
728	}
729	if (mark & TIOCPKT_FLUSHWRITE) {
730		(void)ioctl(1, TIOCFLUSH, (char *)&out);
731		for (;;) {
732			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
733				warn("ioctl");
734				break;
735			}
736			if (atmark)
737				break;
738			n = read(rem, waste, sizeof (waste));
739			if (n <= 0)
740				break;
741		}
742		/*
743		 * Don't want any pending data to be output, so clear the recv
744		 * buffer.  If we were hanging on a write when interrupted,
745		 * don't want it to restart.  If we were reading, restart
746		 * anyway.
747		 */
748		rcvcnt = 0;
749		longjmp(rcvtop, 1);
750	}
751
752	/* oob does not do FLUSHREAD (alas!) */
753
754	/*
755	 * If we filled the receive buffer while a read was pending, longjmp
756	 * to the top to restart appropriately.  Don't abort a pending write,
757	 * however, or we won't know how much was written.
758	 */
759	if (rcvd && rcvstate == READING)
760		longjmp(rcvtop, 1);
761}
762
763/* reader: read from remote: line -> 1 */
764int
765reader(omask)
766	int omask;
767{
768	int pid, n, remaining;
769	char *bufp;
770
771#if BSD >= 43 || defined(SUNOS4)
772	pid = getpid();		/* modern systems use positives for pid */
773#else
774	pid = -getpid();	/* old broken systems use negatives */
775#endif
776	(void)signal(SIGTTOU, SIG_IGN);
777	(void)signal(SIGURG, oob);
778	ppid = getppid();
779	(void)fcntl(rem, F_SETOWN, pid);
780	(void)setjmp(rcvtop);
781	(void)sigsetmask(omask);
782	bufp = rcvbuf;
783	for (;;) {
784		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
785			rcvstate = WRITING;
786			n = write(STDOUT_FILENO, bufp, remaining);
787			if (n < 0) {
788				if (errno != EINTR)
789					return (-1);
790				continue;
791			}
792			bufp += n;
793		}
794		bufp = rcvbuf;
795		rcvcnt = 0;
796		rcvstate = READING;
797
798#ifdef CRYPT
799#ifdef KERBEROS
800		if (doencrypt)
801			rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf),
802				schedule, &cred.session);
803		else
804#endif
805#endif
806			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
807		if (rcvcnt == 0)
808			return (0);
809		if (rcvcnt < 0) {
810			if (errno == EINTR)
811				continue;
812			warn("read");
813			return (-1);
814		}
815	}
816}
817
818void
819mode(f)
820	int f;
821{
822	struct ltchars *ltc;
823	struct sgttyb sb;
824	struct tchars *tc;
825	int lflags;
826
827	(void)ioctl(0, TIOCGETP, (char *)&sb);
828	(void)ioctl(0, TIOCLGET, (char *)&lflags);
829	switch(f) {
830	case 0:
831		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
832		sb.sg_flags |= defflags|tabflag;
833		tc = &deftc;
834		ltc = &defltc;
835		sb.sg_kill = defkill;
836		sb.sg_erase = deferase;
837		lflags = deflflags;
838		break;
839	case 1:
840		sb.sg_flags |= (eight ? RAW : CBREAK);
841		sb.sg_flags &= ~defflags;
842		/* preserve tab delays, but turn off XTABS */
843		if ((sb.sg_flags & TBDELAY) == XTABS)
844			sb.sg_flags &= ~TBDELAY;
845		tc = &notc;
846		ltc = &noltc;
847		sb.sg_kill = sb.sg_erase = -1;
848		if (litout)
849			lflags |= LLITOUT;
850		break;
851	default:
852		return;
853	}
854	(void)ioctl(0, TIOCSLTC, (char *)ltc);
855	(void)ioctl(0, TIOCSETC, (char *)tc);
856	(void)ioctl(0, TIOCSETN, (char *)&sb);
857	(void)ioctl(0, TIOCLSET, (char *)&lflags);
858}
859
860void
861lostpeer(signo)
862	int signo;
863{
864	(void)signal(SIGPIPE, SIG_IGN);
865	msg("\007connection closed.");
866	done(1);
867}
868
869/* copy SIGURGs to the child process. */
870void
871copytochild(signo)
872	int signo;
873{
874	(void)kill(child, SIGURG);
875}
876
877void
878msg(str)
879	char *str;
880{
881	(void)fprintf(stderr, "rlogin: %s\r\n", str);
882}
883
884void
885usage()
886{
887	(void)fprintf(stderr,
888	"usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
889#ifdef KERBEROS
890#ifdef CRYPT
891	    "8DEKLdx", " [-k realm] ");
892#else
893	    "8DEKLd", " [-k realm] ");
894#endif
895#else
896	    "8DELd", " ");
897#endif
898	exit(1);
899}
900
901/*
902 * The following routine provides compatibility (such as it is) between older
903 * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
904 */
905#ifdef OLDSUN
906int
907get_window_size(fd, wp)
908	int fd;
909	struct winsize *wp;
910{
911	struct ttysize ts;
912	int error;
913
914	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
915		return (error);
916	wp->ws_row = ts.ts_lines;
917	wp->ws_col = ts.ts_cols;
918	wp->ws_xpixel = 0;
919	wp->ws_ypixel = 0;
920	return (0);
921}
922#endif
923
924u_int
925getescape(p)
926	register char *p;
927{
928	long val;
929	int len;
930
931	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
932		return ((u_int)*p);
933					/* otherwise, \nnn */
934	if (*p == '\\' && len >= 2 && len <= 4) {
935		val = strtol(++p, NULL, 8);
936		for (;;) {
937			if (!*++p)
938				return ((u_int)val);
939			if (*p < '0' || *p > '8')
940				break;
941		}
942	}
943	msg("illegal option value -- e");
944	usage();
945	/* NOTREACHED */
946}
947