rlogin.c revision 95621
1197679Sdes/*
260573Skris * Copyright (c) 1983, 1990, 1993
360573Skris *	The Regents of the University of California.  All rights reserved.
460573Skris *
560573Skris * Redistribution and use in source and binary forms, with or without
660573Skris * modification, are permitted provided that the following conditions
760573Skris * are met:
860573Skris * 1. Redistributions of source code must retain the above copyright
960573Skris *    notice, this list of conditions and the following disclaimer.
1060573Skris * 2. Redistributions in binary form must reproduce the above copyright
1160573Skris *    notice, this list of conditions and the following disclaimer in the
1260573Skris *    documentation and/or other materials provided with the distribution.
1360573Skris * 3. All advertising materials mentioning features or use of this software
1460573Skris *    must display the following acknowledgement:
1560573Skris *	This product includes software developed by the University of
1660573Skris *	California, Berkeley and its contributors.
1760573Skris * 4. Neither the name of the University nor the names of its contributors
1860573Skris *    may be used to endorse or promote products derived from this software
1960573Skris *    without specific prior written permission.
2060573Skris *
2160573Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2260573Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2360573Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2460573Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2565674Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2660573Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27162856Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2860573Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29162856Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30181111Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31181111Sdes * SUCH DAMAGE.
32162856Sdes */
33181111Sdes
34162856Sdes#ifndef lint
35162856Sdesstatic const char copyright[] =
36162856Sdes"@(#) Copyright (c) 1983, 1990, 1993\n\
37181111Sdes	The Regents of the University of California.  All rights reserved.\n";
38162856Sdes#endif /* not lint */
39197679Sdes
40162856Sdes#ifndef lint
4176262Sgreenstatic const char sccsid[] = "@(#)rlogin.c	8.1 (Berkeley) 6/6/93";
4260573Skris#endif /* not lint */
4376262Sgreen
44162856Sdes/*
4560573Skris * rlogin - remote login
4660573Skris */
47162856Sdes#include <sys/cdefs.h>
48162856Sdes__FBSDID("$FreeBSD: head/usr.bin/rlogin/rlogin.c 95621 2002-04-28 11:16:43Z markm $");
4960573Skris
5060573Skris#include <sys/param.h>
5176262Sgreen#include <sys/socket.h>
52147005Sdes#include <sys/time.h>
53162856Sdes#include <sys/resource.h>
5460573Skris#include <sys/wait.h>
55124211Sdes
56124211Sdes#include <netinet/in.h>
57124211Sdes#include <netinet/in_systm.h>
58162856Sdes#include <netinet/ip.h>
59124211Sdes#include <netinet/tcp.h>
6060573Skris
6160573Skris#include <err.h>
6276262Sgreen#include <errno.h>
63124211Sdes#include <fcntl.h>
64147005Sdes#include <libutil.h>
6560573Skris#include <netdb.h>
6698684Sdes#include <pwd.h>
6798684Sdes#include <setjmp.h>
6898684Sdes#include <sgtty.h>
6998684Sdes#include <signal.h>
7098684Sdes#include <stdio.h>
7198684Sdes#include <stdlib.h>
7298684Sdes#include <string.h>
73124211Sdes#include <unistd.h>
74124211Sdes
75124211Sdes#ifdef KERBEROS
76192595Sdes#include <openssl/des.h>
77192595Sdes#include <krb.h>
78192595Sdes
7998684Sdes#include "../../bin/rcp/pathnames.h"
8098684Sdes#include "krb.h"
8198684Sdes
8298684SdesCREDENTIALS cred;
83124211SdesKey_schedule schedule;
84124211Sdesint use_kerberos = 1, doencrypt;
85124211Sdeschar dst_realm_buf[REALM_SZ], *dest_realm = NULL;
86192595Sdes#endif
87192595Sdes
88192595Sdes#ifndef TIOCPKT_WINDOW
8998684Sdes#define	TIOCPKT_WINDOW	0x80
9098684Sdes#endif
9198684Sdes
9298684Sdes/* concession to Sun */
9369591Sgreen#ifndef SIGUSR1
9469591Sgreen#define	SIGUSR1	30
9560573Skris#endif
9660573Skris
9792559Sdesint eight, litout, rem;
9892559Sdesint family = PF_UNSPEC;
9960573Skris
10060573Skrisint noescape;
10192559Sdesu_char escapechar = '~';
10292559Sdes
10360573Skrisconst char *speeds[] = {
104181111Sdes	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
105181111Sdes	"1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
106181111Sdes#define	MAX_SPEED_LENGTH	(sizeof("115200") - 1)
107181111Sdes};
108181111Sdes
109181111Sdes#define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
110181111Sdesstruct	winsize winsize;
111181111Sdes
112181111Sdesvoid		catch_child(int);
113181111Sdesvoid		copytochild(int);
114181111Sdesvoid		doit(long) __dead2;
115181111Sdesvoid		done(int) __dead2;
116181111Sdesvoid		echo(char);
117181111Sdesu_int		getescape(char *);
118181111Sdesvoid		lostpeer(int);
119181111Sdesvoid		mode(int);
120181111Sdesvoid		msg(const char *);
121181111Sdesvoid		oob(int);
122181111Sdesint		reader(int);
123181111Sdesvoid		sendwindow(void);
124181111Sdesvoid		setsignal(int);
125181111Sdesvoid		sigwinch(int);
126181111Sdesvoid		stop(char);
127181111Sdesvoid		usage(void) __dead2;
128181111Sdesvoid		writer(void);
129181111Sdesvoid		writeroob(int);
130181111Sdes
131181111Sdesint
132181111Sdesmain(int argc, char *argv[])
133181111Sdes{
134181111Sdes	struct passwd *pw;
135181111Sdes	struct servent *sp;
136181111Sdes	struct sgttyb ttyb;
137181111Sdes	long omask;
138181111Sdes	int argoff, ch, dflag, Dflag, one, uid;
139181111Sdes	char *host, *localname, *p, *user, term[1024];
140181111Sdes#ifdef KERBEROS
141181111Sdes	char *k;
142181111Sdes#endif
143181111Sdes        struct sockaddr_storage ss;
144181111Sdes	int sslen;
145181111Sdes
146181111Sdes	argoff = dflag = Dflag = 0;
147181111Sdes	one = 1;
148181111Sdes	host = localname = user = NULL;
149181111Sdes
150181111Sdes	if ((p = rindex(argv[0], '/')))
151181111Sdes		++p;
152181111Sdes	else
153181111Sdes		p = argv[0];
154181111Sdes
155181111Sdes	if (strcmp(p, "rlogin"))
156181111Sdes		host = p;
157181111Sdes
158181111Sdes	/* handle "rlogin host flags" */
159181111Sdes	if (!host && argc > 2 && argv[1][0] != '-') {
160181111Sdes		host = argv[1];
161181111Sdes		argoff = 1;
162181111Sdes	}
163181111Sdes
164181111Sdes#ifdef KERBEROS
165181111Sdes#define	OPTIONS	"468DEKLde:i:k:l:x"
166181111Sdes#else
167181111Sdes#define	OPTIONS	"468DEKLde:i:l:"
168181111Sdes#endif
16960573Skris	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
17069591Sgreen		switch(ch) {
17160573Skris		case '4':
172126277Sdes			family = PF_INET;
173126277Sdes			break;
17460573Skris
17592559Sdes		case '6':
17660573Skris			family = PF_INET6;
17769591Sgreen			break;
17860573Skris
17960573Skris		case '8':
180162856Sdes			eight = 1;
18192559Sdes			break;
18292559Sdes		case 'D':
18360573Skris			Dflag = 1;
18469591Sgreen			break;
18576262Sgreen		case 'E':
186106130Sdes			noescape = 1;
18760573Skris			break;
18892559Sdes		case 'K':
18960573Skris#ifdef KERBEROS
19069591Sgreen			use_kerberos = 0;
19169591Sgreen#endif
19269591Sgreen			break;
19360573Skris		case 'L':
19469591Sgreen			litout = 1;
195106130Sdes			break;
19660573Skris		case 'd':
19760573Skris			dflag = 1;
19860573Skris			break;
19960573Skris		case 'e':
20060573Skris			noescape = 0;
20160573Skris			escapechar = getescape(optarg);
202106130Sdes			break;
20360573Skris		case 'i':
20460573Skris			if (getuid() != 0)
20560573Skris				errx(1, "-i user: permission denied");
20660573Skris			localname = optarg;
20760573Skris			break;
20860573Skris#ifdef KERBEROS
20960573Skris		case 'k':
21060573Skris			dest_realm = dst_realm_buf;
21160573Skris			(void)strncpy(dest_realm, optarg, REALM_SZ);
21260573Skris			break;
21360573Skris#endif
214162856Sdes		case 'l':
21592559Sdes			user = optarg;
21692559Sdes			break;
21760573Skris#ifdef CRYPT
21869591Sgreen#ifdef KERBEROS
21969591Sgreen		case 'x':
22076262Sgreen			doencrypt = 1;
22160573Skris			break;
22299053Sdes#endif
22399053Sdes#endif
22499053Sdes		case '?':
22560573Skris		default:
226124211Sdes			usage();
22799053Sdes		}
22899053Sdes	optind += argoff;
22999053Sdes
23069591Sgreen	/* if haven't gotten a host yet, do so */
23169591Sgreen	if (!host && !(host = argv[optind++]))
23260573Skris		usage();
23369591Sgreen
23469591Sgreen	if (argv[optind])
23569591Sgreen		usage();
23660573Skris
23776262Sgreen	if (!(pw = getpwuid(uid = getuid())))
23860573Skris		errx(1, "unknown user id");
23976262Sgreen	if (!user)
24076262Sgreen		user = pw->pw_name;
24176262Sgreen	if (!localname)
24276262Sgreen		localname = pw->pw_name;
24369591Sgreen
24498684Sdes	sp = NULL;
245128460Sdes#ifdef KERBEROS
24698684Sdes	k = auth_getval("auth_list");
24769591Sgreen	if (k && !strstr(k, "kerberos"))
24869591Sgreen	    use_kerberos = 0;
24969591Sgreen	if (use_kerberos) {
250137019Sdes		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
251124211Sdes		if (sp == NULL) {
252147005Sdes			use_kerberos = 0;
253147005Sdes			warn("can't get entry for %s/tcp service",
254147005Sdes			    doencrypt ? "eklogin" : "klogin");
25560573Skris		}
256157019Sdes	}
257157019Sdes#endif
258157019Sdes	if (sp == NULL)
259157019Sdes		sp = getservbyname("login", "tcp");
260137019Sdes	if (sp == NULL)
26198684Sdes		errx(1, "login/tcp: unknown service");
26269591Sgreen
26392559Sdes#define	MAX_TERM_LENGTH	(sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
26498684Sdes
26598684Sdes	(void)strncpy(term, (p = getenv("TERM")) ? p : "network",
266181111Sdes		      MAX_TERM_LENGTH);
26792559Sdes	term[MAX_TERM_LENGTH] = '\0';
26892559Sdes	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
26992559Sdes		(void)strcat(term, "/");
27092559Sdes		(void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
27192559Sdes	}
27260573Skris
27399053Sdes	(void)get_window_size(0, &winsize);
27499053Sdes
27599053Sdes	(void)signal(SIGPIPE, lostpeer);
27699053Sdes	/* will use SIGUSR1 for window size hack, so hold it off */
27799053Sdes	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
27899053Sdes	/*
27999053Sdes	 * We set SIGURG and SIGUSR1 below so that an
280124211Sdes	 * incoming signal will be held pending rather than being
28199053Sdes	 * discarded. Note that these routines will be ready to get
28299053Sdes	 * a signal by the time that they are unblocked below.
28399053Sdes	 */
28499053Sdes	(void)signal(SIGURG, copytochild);
285124211Sdes	(void)signal(SIGUSR1, writeroob);
28699053Sdes
28799053Sdes#ifdef KERBEROS
28899053Sdes	if (use_kerberos) {
28999053Sdes		setuid(getuid());
29099053Sdes		rem = KSUCCESS;
29199053Sdes		errno = 0;
29299053Sdes		if (dest_realm == NULL)
29399053Sdes			dest_realm = krb_realmofhost(host);
29476262Sgreen
29592559Sdes#ifdef CRYPT
296192595Sdes		if (doencrypt) {
297192595Sdes			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
298192595Sdes			    dest_realm, &cred, schedule);
299124211Sdes			des_set_key(&cred.session, schedule);
300124211Sdes		} else
301192595Sdes#endif /* CRYPT */
302124211Sdes			rem = krcmd(&host, sp->s_port, user, term, 0,
303124211Sdes			    dest_realm);
304124211Sdes		if (rem < 0) {
305124211Sdes			int i;
30676262Sgreen			char **newargv;
30768704Sgreen
30876262Sgreen			sp = getservbyname("login", "tcp");
30969591Sgreen			if (sp == NULL)
310181111Sdes				errx(1, "unknown service login/tcp");
31169591Sgreen			if (errno == ECONNREFUSED)
31269591Sgreen				warn("remote host doesn't support Kerberos");
31369591Sgreen			if (errno == ENOENT)
31476262Sgreen				warn("can't provide Kerberos auth data");
31569591Sgreen			newargv = malloc((argc + 2) * sizeof(*newargv));
31669591Sgreen			if (newargv == NULL)
31769591Sgreen				err(1, "malloc");
31869591Sgreen			newargv[0] = argv[0];
31969591Sgreen			newargv[1] = "-K";
32069591Sgreen			for(i = 1; i < argc; ++i)
32169591Sgreen			newargv[i + 1] = argv[i];
32276262Sgreen			newargv[argc + 1] = NULL;
32369591Sgreen			execv(_PATH_RLOGIN, newargv);
32492559Sdes		}
32592559Sdes	} else {
32676262Sgreen#ifdef CRYPT
32776262Sgreen		if (doencrypt)
32876262Sgreen			errx(1, "the -x flag requires Kerberos authentication");
32969591Sgreen#endif /* CRYPT */
33076262Sgreen		rem = rcmd_af(&host, sp->s_port, localname, user, term, 0,
331113911Sdes			      family);
332147005Sdes	}
33376262Sgreen#else
334147005Sdes	rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
335147005Sdes#endif /* KERBEROS */
336147005Sdes
337147005Sdes	if (rem < 0)
33860573Skris		exit(1);
33998941Sdes
340147005Sdes	if (dflag &&
341147005Sdes	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
342147005Sdes		warn("setsockopt");
343147005Sdes	if (Dflag &&
344147005Sdes	    setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
345147005Sdes		warn("setsockopt NODELAY (ignored)");
346147005Sdes
347147005Sdes	sslen = sizeof(ss);
348147005Sdes	one = IPTOS_LOWDELAY;
349149753Sdes	if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
350147005Sdes	    ss.ss_family == AF_INET) {
351147005Sdes		if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
352124211Sdes			       sizeof(int)) < 0)
35398941Sdes			warn("setsockopt TOS (ignored)");
354106130Sdes	} else
355106130Sdes		if (ss.ss_family == AF_INET)
356106130Sdes			warn("setsockopt getsockname failed");
357106130Sdes
358106130Sdes	(void)setuid(uid);
359106130Sdes	doit(omask);
360106130Sdes	/*NOTREACHED*/
36176262Sgreen}
36276262Sgreen
36369591Sgreenint child, defflags, deflflags, tabflag;
36492559Sdeschar deferase, defkill;
36592559Sdesstruct tchars deftc;
36692559Sdesstruct ltchars defltc;
36792559Sdesstruct tchars notc = { -1, -1, -1, -1, -1, -1 };
36892559Sdesstruct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
36992559Sdes
37092559Sdesvoid
37192559Sdesdoit(long omask)
37292559Sdes{
37392559Sdes	struct sgttyb sb;
37492559Sdes
37592559Sdes	(void)ioctl(0, TIOCGETP, (char *)&sb);
37692559Sdes	defflags = sb.sg_flags;
377181111Sdes	tabflag = defflags & TBDELAY;
378181111Sdes	defflags &= ECHO | CRMOD;
379181111Sdes	deferase = sb.sg_erase;
380181111Sdes	defkill = sb.sg_kill;
381181111Sdes	(void)ioctl(0, TIOCLGET, &deflflags);
382147005Sdes	(void)ioctl(0, TIOCGETC, &deftc);
383147005Sdes	notc.t_startc = deftc.t_startc;
384147005Sdes	notc.t_stopc = deftc.t_stopc;
38592559Sdes	(void)ioctl(0, TIOCGLTC, &defltc);
386147005Sdes	(void)signal(SIGINT, SIG_IGN);
38792559Sdes	setsignal(SIGHUP);
38892559Sdes	setsignal(SIGQUIT);
38992559Sdes	child = fork();
39092559Sdes	if (child == -1) {
39192559Sdes		warn("fork");
39292559Sdes		done(1);
39392559Sdes	}
39492559Sdes	if (child == 0) {
39576262Sgreen		mode(1);
39669591Sgreen		if (reader(omask) == 0) {
39792559Sdes			msg("connection closed.");
39869591Sgreen			exit(0);
39960573Skris		}
40092559Sdes		sleep(1);
40169591Sgreen		msg("\007connection closed.");
40298684Sdes		exit(1);
40360573Skris	}
40492559Sdes
40598684Sdes	/*
40698684Sdes	 * We may still own the socket, and may have a pending SIGURG (or might
40769591Sgreen	 * receive one soon) that we really want to send to the reader.  When
40898684Sdes	 * one of these comes in, the trap copytochild simply copies such
40998684Sdes	 * signals to the child. We can now unblock SIGURG and SIGUSR1
41092559Sdes	 * that were set above.
41192559Sdes	 */
41298684Sdes	(void)sigsetmask(omask);
41398684Sdes	(void)signal(SIGCHLD, catch_child);
41460573Skris	writer();
41569591Sgreen	msg("closed connection.");
41692559Sdes	done(0);
41792559Sdes}
41892559Sdes
41969591Sgreen/* trap a signal, unless it is being ignored. */
42060573Skrisvoid
42160573Skrissetsignal(int sig)
42292559Sdes{
42369591Sgreen	int omask = sigblock(sigmask(sig));
42469591Sgreen
42598684Sdes	if (signal(sig, exit) == SIG_IGN)
42698684Sdes		(void)signal(sig, SIG_IGN);
42769591Sgreen	(void)sigsetmask(omask);
42898684Sdes}
42998684Sdes
43098684Sdesvoid
43198684Sdesdone(int status)
43298684Sdes{
43398684Sdes	int w, wstatus;
43498684Sdes
43569591Sgreen	mode(0);
43669591Sgreen	if (child > 0) {
437181111Sdes		/* make sure catch_child does not snap it up */
438		(void)signal(SIGCHLD, SIG_DFL);
439		if (kill(child, SIGKILL) >= 0)
440			while ((w = wait(&wstatus)) > 0 && w != child);
441	}
442	exit(status);
443}
444
445int dosigwinch;
446
447/*
448 * This is called when the reader process gets the out-of-band (urgent)
449 * request to turn on the window-changing protocol.
450 */
451void
452writeroob(int signo __unused)
453{
454	if (dosigwinch == 0) {
455		sendwindow();
456		(void)signal(SIGWINCH, sigwinch);
457	}
458	dosigwinch = 1;
459}
460
461void
462catch_child(int signo __unused)
463{
464	union wait status;
465	int pid;
466
467	for (;;) {
468		pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
469		if (pid == 0)
470			return;
471		/* if the child (reader) dies, just quit */
472		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
473			done((int)(status.w_termsig | status.w_retcode));
474	}
475	/* NOTREACHED */
476}
477
478/*
479 * writer: write to remote: 0 -> line.
480 * ~.				terminate
481 * ~^Z				suspend rlogin process.
482 * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
483 */
484void
485writer(void)
486{
487	int bol, local, n;
488	char c;
489
490	bol = 1;			/* beginning of line */
491	local = 0;
492	for (;;) {
493		n = read(STDIN_FILENO, &c, 1);
494		if (n <= 0) {
495			if (n < 0 && errno == EINTR)
496				continue;
497			break;
498		}
499		/*
500		 * If we're at the beginning of the line and recognize a
501		 * command character, then we echo locally.  Otherwise,
502		 * characters are echo'd remotely.  If the command character
503		 * is doubled, this acts as a force and local echo is
504		 * suppressed.
505		 */
506		if (bol) {
507			bol = 0;
508			if (!noescape && c == escapechar) {
509				local = 1;
510				continue;
511			}
512		} else if (local) {
513			local = 0;
514			if (c == '.' || c == deftc.t_eofc) {
515				echo(c);
516				break;
517			}
518			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
519				bol = 1;
520				echo(c);
521				stop(c);
522				continue;
523			}
524			if (c != escapechar)
525#ifdef CRYPT
526#ifdef KERBEROS
527				if (doencrypt)
528					(void)des_enc_write(rem,
529					    (char *)&escapechar, 1,
530						schedule, &cred.session);
531				else
532#endif
533#endif
534					(void)write(rem, &escapechar, 1);
535		}
536
537#ifdef CRYPT
538#ifdef KERBEROS
539		if (doencrypt) {
540			if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
541				msg("line gone");
542				break;
543			}
544		} else
545#endif
546#endif
547			if (write(rem, &c, 1) == 0) {
548				msg("line gone");
549				break;
550			}
551		bol = c == defkill || c == deftc.t_eofc ||
552		    c == deftc.t_intrc || c == defltc.t_suspc ||
553		    c == '\r' || c == '\n';
554	}
555}
556
557void
558echo(char c)
559{
560	char *p;
561	char buf[8];
562
563	p = buf;
564	c &= 0177;
565	*p++ = escapechar;
566	if (c < ' ') {
567		*p++ = '^';
568		*p++ = c + '@';
569	} else if (c == 0177) {
570		*p++ = '^';
571		*p++ = '?';
572	} else
573		*p++ = c;
574	*p++ = '\r';
575	*p++ = '\n';
576	(void)write(STDOUT_FILENO, buf, p - buf);
577}
578
579void
580stop(char cmdc)
581{
582	mode(0);
583	(void)signal(SIGCHLD, SIG_IGN);
584	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
585	(void)signal(SIGCHLD, catch_child);
586	mode(1);
587	sigwinch(0);			/* check for size changes */
588}
589
590void
591sigwinch(int signo __unused)
592{
593	struct winsize ws;
594
595	if (dosigwinch && get_window_size(0, &ws) == 0 &&
596	    bcmp(&ws, &winsize, sizeof(ws))) {
597		winsize = ws;
598		sendwindow();
599	}
600}
601
602/*
603 * Send the window size to the server via the magic escape
604 */
605void
606sendwindow(void)
607{
608	struct winsize *wp;
609	char obuf[4 + sizeof (struct winsize)];
610
611	wp = (struct winsize *)(obuf+4);
612	obuf[0] = 0377;
613	obuf[1] = 0377;
614	obuf[2] = 's';
615	obuf[3] = 's';
616	wp->ws_row = htons(winsize.ws_row);
617	wp->ws_col = htons(winsize.ws_col);
618	wp->ws_xpixel = htons(winsize.ws_xpixel);
619	wp->ws_ypixel = htons(winsize.ws_ypixel);
620
621#ifdef CRYPT
622#ifdef KERBEROS
623	if(doencrypt)
624		(void)des_enc_write(rem, obuf, sizeof(obuf),
625			schedule, &cred.session);
626	else
627#endif
628#endif
629		(void)write(rem, obuf, sizeof(obuf));
630}
631
632/*
633 * reader: read from remote: line -> 1
634 */
635#define	READING	1
636#define	WRITING	2
637
638jmp_buf rcvtop;
639int ppid, rcvcnt, rcvstate;
640char rcvbuf[8 * 1024];
641
642void
643oob(int signo __unused)
644{
645	struct sgttyb sb;
646	int atmark, n, out, rcvd;
647	char waste[BUFSIZ], mark;
648
649	out = O_RDWR;
650	rcvd = 0;
651	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
652		switch (errno) {
653		case EWOULDBLOCK:
654			/*
655			 * Urgent data not here yet.  It may not be possible
656			 * to send it yet if we are blocked for output and
657			 * our input buffer is full.
658			 */
659			if (rcvcnt < (int)sizeof(rcvbuf)) {
660				n = read(rem, rcvbuf + rcvcnt,
661				    sizeof(rcvbuf) - rcvcnt);
662				if (n <= 0)
663					return;
664				rcvd += n;
665			} else {
666				n = read(rem, waste, sizeof(waste));
667				if (n <= 0)
668					return;
669			}
670			continue;
671		default:
672			return;
673		}
674	}
675	if (mark & TIOCPKT_WINDOW) {
676		/* Let server know about window size changes */
677		(void)kill(ppid, SIGUSR1);
678	}
679	if (!eight && (mark & TIOCPKT_NOSTOP)) {
680		(void)ioctl(0, TIOCGETP, (char *)&sb);
681		sb.sg_flags &= ~CBREAK;
682		sb.sg_flags |= RAW;
683		(void)ioctl(0, TIOCSETN, (char *)&sb);
684		notc.t_stopc = -1;
685		notc.t_startc = -1;
686		(void)ioctl(0, TIOCSETC, (char *)&notc);
687	}
688	if (!eight && (mark & TIOCPKT_DOSTOP)) {
689		(void)ioctl(0, TIOCGETP, (char *)&sb);
690		sb.sg_flags &= ~RAW;
691		sb.sg_flags |= CBREAK;
692		(void)ioctl(0, TIOCSETN, (char *)&sb);
693		notc.t_stopc = deftc.t_stopc;
694		notc.t_startc = deftc.t_startc;
695		(void)ioctl(0, TIOCSETC, (char *)&notc);
696	}
697	if (mark & TIOCPKT_FLUSHWRITE) {
698		(void)ioctl(1, TIOCFLUSH, (char *)&out);
699		for (;;) {
700			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
701				warn("ioctl");
702				break;
703			}
704			if (atmark)
705				break;
706			n = read(rem, waste, sizeof (waste));
707			if (n <= 0)
708				break;
709		}
710		/*
711		 * Don't want any pending data to be output, so clear the recv
712		 * buffer.  If we were hanging on a write when interrupted,
713		 * don't want it to restart.  If we were reading, restart
714		 * anyway.
715		 */
716		rcvcnt = 0;
717		longjmp(rcvtop, 1);
718	}
719
720	/* oob does not do FLUSHREAD (alas!) */
721
722	/*
723	 * If we filled the receive buffer while a read was pending, longjmp
724	 * to the top to restart appropriately.  Don't abort a pending write,
725	 * however, or we won't know how much was written.
726	 */
727	if (rcvd && rcvstate == READING)
728		longjmp(rcvtop, 1);
729}
730
731/* reader: read from remote: line -> 1 */
732int
733reader(int omask)
734{
735	int pid, n, remaining;
736	char *bufp;
737
738#if BSD >= 43 || defined(SUNOS4)
739	pid = getpid();		/* modern systems use positives for pid */
740#else
741	pid = -getpid();	/* old broken systems use negatives */
742#endif
743	(void)signal(SIGTTOU, SIG_IGN);
744	(void)signal(SIGURG, oob);
745	(void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */
746	ppid = getppid();
747	(void)fcntl(rem, F_SETOWN, pid);
748	(void)setjmp(rcvtop);
749	(void)sigsetmask(omask);
750	bufp = rcvbuf;
751	for (;;) {
752		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
753			rcvstate = WRITING;
754			n = write(STDOUT_FILENO, bufp, remaining);
755			if (n < 0) {
756				if (errno != EINTR)
757					return (-1);
758				continue;
759			}
760			bufp += n;
761		}
762		bufp = rcvbuf;
763		rcvcnt = 0;
764		rcvstate = READING;
765
766#ifdef CRYPT
767#ifdef KERBEROS
768		if (doencrypt)
769			rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf),
770				schedule, &cred.session);
771		else
772#endif
773#endif
774			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
775		if (rcvcnt == 0)
776			return (0);
777		if (rcvcnt < 0) {
778			if (errno == EINTR)
779				continue;
780			warn("read");
781			return (-1);
782		}
783	}
784}
785
786void
787mode(int f)
788{
789	struct ltchars *ltc;
790	struct sgttyb sb;
791	struct tchars *tc;
792	int lflags;
793
794	(void)ioctl(0, TIOCGETP, (char *)&sb);
795	(void)ioctl(0, TIOCLGET, (char *)&lflags);
796	switch(f) {
797	case 0:
798		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
799		sb.sg_flags |= defflags|tabflag;
800		tc = &deftc;
801		ltc = &defltc;
802		sb.sg_kill = defkill;
803		sb.sg_erase = deferase;
804		lflags = deflflags;
805		break;
806	case 1:
807		sb.sg_flags |= (eight ? RAW : CBREAK);
808		sb.sg_flags &= ~defflags;
809		/* preserve tab delays, but turn off XTABS */
810		if ((sb.sg_flags & TBDELAY) == XTABS)
811			sb.sg_flags &= ~TBDELAY;
812		tc = &notc;
813		ltc = &noltc;
814		sb.sg_kill = sb.sg_erase = -1;
815		if (litout)
816			lflags |= LLITOUT;
817		break;
818	default:
819		return;
820	}
821	(void)ioctl(0, TIOCSLTC, (char *)ltc);
822	(void)ioctl(0, TIOCSETC, (char *)tc);
823	(void)ioctl(0, TIOCSETN, (char *)&sb);
824	(void)ioctl(0, TIOCLSET, (char *)&lflags);
825}
826
827void
828lostpeer(int signo __unused)
829{
830	(void)signal(SIGPIPE, SIG_IGN);
831	msg("\007connection closed.");
832	done(1);
833}
834
835/* copy SIGURGs to the child process via SIGUSR1. */
836void
837copytochild(int signo __unused)
838{
839	(void)kill(child, SIGUSR1);
840}
841
842void
843msg(const char *str)
844{
845	(void)fprintf(stderr, "rlogin: %s\r\n", str);
846}
847
848void
849usage(void)
850{
851	(void)fprintf(stderr,
852	"usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
853#ifdef KERBEROS
854#ifdef CRYPT
855	    "8DEKLdx", " [-k realm] ");
856#else
857	    "8DEKLd", " [-k realm] ");
858#endif
859#else
860	    "8DEKLd", " ");
861#endif
862	exit(1);
863}
864
865u_int
866getescape(char *p)
867{
868	long val;
869	int len;
870
871	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
872		return ((u_int)*p);
873					/* otherwise, \nnn */
874	if (*p == '\\' && len >= 2 && len <= 4) {
875		val = strtol(++p, NULL, 8);
876		for (;;) {
877			if (!*++p)
878				return ((u_int)val);
879			if (*p < '0' || *p > '8')
880				break;
881		}
882	}
883	msg("illegal option value -- e");
884	usage();
885	/* NOTREACHED */
886}
887