rlogin.c revision 91434
118334Speter/*
218334Speter * Copyright (c) 1983, 1990, 1993
318334Speter *	The Regents of the University of California.  All rights reserved.
418334Speter *
518334Speter * Redistribution and use in source and binary forms, with or without
618334Speter * modification, are permitted provided that the following conditions
718334Speter * are met:
818334Speter * 1. Redistributions of source code must retain the above copyright
918334Speter *    notice, this list of conditions and the following disclaimer.
1018334Speter * 2. Redistributions in binary form must reproduce the above copyright
1118334Speter *    notice, this list of conditions and the following disclaimer in the
1218334Speter *    documentation and/or other materials provided with the distribution.
1318334Speter * 3. All advertising materials mentioning features or use of this software
1418334Speter *    must display the following acknowledgement:
1518334Speter *	This product includes software developed by the University of
1618334Speter *	California, Berkeley and its contributors.
1718334Speter * 4. Neither the name of the University nor the names of its contributors
1818334Speter *    may be used to endorse or promote products derived from this software
1918334Speter *    without specific prior written permission.
2018334Speter *
2118334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2218334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2318334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2418334Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2518334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2618334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2718334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2818334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2918334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3018334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3118334Speter * SUCH DAMAGE.
3218334Speter */
3318334Speter
3418334Speter#ifndef lint
3518334Speterstatic const char copyright[] =
3618334Speter"@(#) Copyright (c) 1983, 1990, 1993\n\
3718334Speter	The Regents of the University of California.  All rights reserved.\n";
3818334Speter#endif /* not lint */
3918334Speter
4018334Speter#ifndef lint
4118334Speterstatic const char sccsid[] = "@(#)rlogin.c	8.1 (Berkeley) 6/6/93";
4218334Speterstatic const char rcsid[] =
4318334Speter  "$FreeBSD: head/usr.bin/rlogin/rlogin.c 91434 2002-02-27 22:45:10Z fenner $";
4418334Speter#endif /* not lint */
4518334Speter
4618334Speter/*
4718334Speter * rlogin - remote login
4818334Speter */
4918334Speter#include <sys/param.h>
5018334Speter#include <sys/socket.h>
5118334Speter#include <sys/time.h>
5218334Speter#include <sys/resource.h>
5318334Speter#include <sys/wait.h>
5418334Speter
5518334Speter#include <netinet/in.h>
5618334Speter#include <netinet/in_systm.h>
5718334Speter#include <netinet/ip.h>
5818334Speter#include <netinet/tcp.h>
5918334Speter
6018334Speter#include <err.h>
6118334Speter#include <errno.h>
6218334Speter#include <fcntl.h>
6318334Speter#include <libutil.h>
6418334Speter#include <netdb.h>
6518334Speter#include <pwd.h>
6618334Speter#include <setjmp.h>
6718334Speter#include <sgtty.h>
6818334Speter#include <signal.h>
6918334Speter#include <stdio.h>
7018334Speter#include <stdlib.h>
7118334Speter#include <string.h>
7218334Speter#include <unistd.h>
7318334Speter#include <err.h>
7418334Speter
7518334Speter#ifdef KERBEROS
7618334Speter#include <openssl/des.h>
7718334Speter#include <krb.h>
7818334Speter
7918334Speter#include "../../bin/rcp/pathnames.h"
8018334Speter#include "krb.h"
8118334Speter
8218334SpeterCREDENTIALS cred;
8318334SpeterKey_schedule schedule;
8418334Speterint use_kerberos = 1, doencrypt;
8518334Speterchar dst_realm_buf[REALM_SZ], *dest_realm = NULL;
8618334Speter#endif
8718334Speter
8818334Speter#ifndef TIOCPKT_WINDOW
8918334Speter#define	TIOCPKT_WINDOW	0x80
9018334Speter#endif
9118334Speter
9218334Speter/* concession to Sun */
9318334Speter#ifndef SIGUSR1
9418334Speter#define	SIGUSR1	30
9518334Speter#endif
9618334Speter
9718334Speterint eight, litout, rem;
9818334Speterint family = PF_UNSPEC;
9918334Speter
10018334Speterint noescape;
10118334Speteru_char escapechar = '~';
10218334Speter
10318334Speterchar *speeds[] = {
10418334Speter	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
10518334Speter	"1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
10618334Speter#define	MAX_SPEED_LENGTH	(sizeof("115200") - 1)
10718334Speter};
10818334Speter
10918334Speter#ifdef OLDSUN
11018334Speterstruct winsize {
11118334Speter	unsigned short ws_row, ws_col;
11218334Speter	unsigned short ws_xpixel, ws_ypixel;
11318334Speter};
11418334Speter#else
11518334Speter#define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
11618334Speter#endif
11718334Speterstruct	winsize winsize;
11818334Speter
11918334Spetervoid		catch_child __P((int));
12018334Spetervoid		copytochild __P((int));
12118334Spetervoid		doit __P((long)) __dead2;
12218334Spetervoid		done __P((int)) __dead2;
12318334Spetervoid		echo __P((char));
12418334Speteru_int		getescape __P((char *));
12518334Spetervoid		lostpeer __P((int));
12618334Spetervoid		mode __P((int));
12718334Spetervoid		msg __P((char *));
12818334Spetervoid		oob __P((int));
12918334Speterint		reader __P((int));
13018334Spetervoid		sendwindow __P((void));
13118334Spetervoid		setsignal __P((int));
13218334Spetervoid		sigwinch __P((int));
13318334Spetervoid		stop __P((char));
13418334Spetervoid		usage __P((void)) __dead2;
13518334Spetervoid		writer __P((void));
13618334Spetervoid		writeroob __P((int));
13718334Speter
13818334Speter#ifdef OLDSUN
13918334Speterint		get_window_size __P((int, struct winsize *));
14018334Speter#endif
14118334Speter
14218334Speterint
14318334Spetermain(argc, argv)
14418334Speter	int argc;
14518334Speter	char *argv[];
14618334Speter{
14718334Speter	struct passwd *pw;
14818334Speter	struct servent *sp;
14918334Speter	struct sgttyb ttyb;
15018334Speter	long omask;
15118334Speter	int argoff, ch, dflag, Dflag, one, uid;
15218334Speter	char *host, *localname, *p, *user, term[1024];
15318334Speter#ifdef KERBEROS
15418334Speter	char *k;
15518334Speter#endif
15618334Speter        struct sockaddr_storage ss;
15718334Speter	int sslen;
15818334Speter
15918334Speter	argoff = dflag = Dflag = 0;
16018334Speter	one = 1;
16118334Speter	host = localname = user = NULL;
16218334Speter
16318334Speter	if ((p = rindex(argv[0], '/')))
16418334Speter		++p;
16518334Speter	else
16618334Speter		p = argv[0];
16718334Speter
16818334Speter	if (strcmp(p, "rlogin"))
16918334Speter		host = p;
17018334Speter
17118334Speter	/* handle "rlogin host flags" */
17218334Speter	if (!host && argc > 2 && argv[1][0] != '-') {
17318334Speter		host = argv[1];
17418334Speter		argoff = 1;
17518334Speter	}
17618334Speter
17718334Speter#ifdef KERBEROS
17818334Speter#define	OPTIONS	"468DEKLde:i:k:l:x"
17918334Speter#else
18018334Speter#define	OPTIONS	"468DEKLde:i:l:"
18118334Speter#endif
18218334Speter	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
18318334Speter		switch(ch) {
18418334Speter		case '4':
18518334Speter			family = PF_INET;
18618334Speter			break;
18718334Speter
18818334Speter		case '6':
18918334Speter			family = PF_INET6;
19018334Speter			break;
19118334Speter
19218334Speter		case '8':
19318334Speter			eight = 1;
19418334Speter			break;
19518334Speter		case 'D':
19618334Speter			Dflag = 1;
19718334Speter			break;
19818334Speter		case 'E':
19918334Speter			noescape = 1;
20018334Speter			break;
20118334Speter		case 'K':
20218334Speter#ifdef KERBEROS
20318334Speter			use_kerberos = 0;
20418334Speter#endif
20518334Speter			break;
20618334Speter		case 'L':
20718334Speter			litout = 1;
20818334Speter			break;
20918334Speter		case 'd':
21018334Speter			dflag = 1;
21118334Speter			break;
21218334Speter		case 'e':
21318334Speter			noescape = 0;
21418334Speter			escapechar = getescape(optarg);
21518334Speter			break;
21618334Speter		case 'i':
21718334Speter			if (getuid() != 0)
21818334Speter				errx(1, "-i user: permission denied");
21918334Speter			localname = optarg;
22018334Speter			break;
22118334Speter#ifdef KERBEROS
22218334Speter		case 'k':
22318334Speter			dest_realm = dst_realm_buf;
22418334Speter			(void)strncpy(dest_realm, optarg, REALM_SZ);
22518334Speter			break;
22618334Speter#endif
22718334Speter		case 'l':
22818334Speter			user = optarg;
22918334Speter			break;
23018334Speter#ifdef CRYPT
23118334Speter#ifdef KERBEROS
23218334Speter		case 'x':
23318334Speter			doencrypt = 1;
23418334Speter			break;
23518334Speter#endif
23618334Speter#endif
23718334Speter		case '?':
23818334Speter		default:
23918334Speter			usage();
24018334Speter		}
24118334Speter	optind += argoff;
24218334Speter
24318334Speter	/* if haven't gotten a host yet, do so */
24418334Speter	if (!host && !(host = argv[optind++]))
24518334Speter		usage();
24618334Speter
24718334Speter	if (argv[optind])
24818334Speter		usage();
24918334Speter
25018334Speter	if (!(pw = getpwuid(uid = getuid())))
25118334Speter		errx(1, "unknown user id");
25218334Speter	if (!user)
25318334Speter		user = pw->pw_name;
25418334Speter	if (!localname)
25518334Speter		localname = pw->pw_name;
25618334Speter
25718334Speter	sp = NULL;
25818334Speter#ifdef KERBEROS
25918334Speter	k = auth_getval("auth_list");
26018334Speter	if (k && !strstr(k, "kerberos"))
26118334Speter	    use_kerberos = 0;
26218334Speter	if (use_kerberos) {
26318334Speter		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
26418334Speter		if (sp == NULL) {
26518334Speter			use_kerberos = 0;
26618334Speter			warn("can't get entry for %s/tcp service",
26718334Speter			    doencrypt ? "eklogin" : "klogin");
26818334Speter		}
26918334Speter	}
27018334Speter#endif
27118334Speter	if (sp == NULL)
27218334Speter		sp = getservbyname("login", "tcp");
27318334Speter	if (sp == NULL)
27418334Speter		errx(1, "login/tcp: unknown service");
27518334Speter
27618334Speter#define	MAX_TERM_LENGTH	(sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
27718334Speter
27818334Speter	(void)strncpy(term, (p = getenv("TERM")) ? p : "network",
27918334Speter		      MAX_TERM_LENGTH);
28018334Speter	term[MAX_TERM_LENGTH] = '\0';
28118334Speter	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
28218334Speter		(void)strcat(term, "/");
28318334Speter		(void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
28418334Speter	}
28518334Speter
28618334Speter	(void)get_window_size(0, &winsize);
28718334Speter
28818334Speter	(void)signal(SIGPIPE, lostpeer);
28918334Speter	/* will use SIGUSR1 for window size hack, so hold it off */
29018334Speter	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
29118334Speter	/*
29218334Speter	 * We set SIGURG and SIGUSR1 below so that an
29318334Speter	 * incoming signal will be held pending rather than being
29418334Speter	 * discarded. Note that these routines will be ready to get
29518334Speter	 * a signal by the time that they are unblocked below.
29618334Speter	 */
29718334Speter	(void)signal(SIGURG, copytochild);
29818334Speter	(void)signal(SIGUSR1, writeroob);
29918334Speter
30018334Speter#ifdef KERBEROS
30118334Speter	if (use_kerberos) {
30218334Speter		setuid(getuid());
30318334Speter		rem = KSUCCESS;
30418334Speter		errno = 0;
30518334Speter		if (dest_realm == NULL)
30618334Speter			dest_realm = krb_realmofhost(host);
30718334Speter
30818334Speter#ifdef CRYPT
30918334Speter		if (doencrypt) {
31018334Speter			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
31118334Speter			    dest_realm, &cred, schedule);
31218334Speter			des_set_key(&cred.session, schedule);
31318334Speter		} else
31418334Speter#endif /* CRYPT */
31518334Speter			rem = krcmd(&host, sp->s_port, user, term, 0,
31618334Speter			    dest_realm);
31718334Speter		if (rem < 0) {
31818334Speter			int i;
31918334Speter			char **newargv;
32018334Speter
32118334Speter			sp = getservbyname("login", "tcp");
32218334Speter			if (sp == NULL)
32318334Speter				errx(1, "unknown service login/tcp");
32418334Speter			if (errno == ECONNREFUSED)
32518334Speter				warn("remote host doesn't support Kerberos");
32618334Speter			if (errno == ENOENT)
32718334Speter				warn("can't provide Kerberos auth data");
32818334Speter			newargv = malloc((argc + 2) * sizeof(*newargv));
32918334Speter			if (newargv == NULL)
33018334Speter				err(1, "malloc");
33118334Speter			newargv[0] = argv[0];
33218334Speter			newargv[1] = "-K";
33318334Speter			for(i = 1; i < argc; ++i)
33418334Speter			newargv[i + 1] = argv[i];
33518334Speter			newargv[argc + 1] = NULL;
33618334Speter			execv(_PATH_RLOGIN, newargv);
33718334Speter		}
33818334Speter	} else {
33918334Speter#ifdef CRYPT
34018334Speter		if (doencrypt)
34118334Speter			errx(1, "the -x flag requires Kerberos authentication");
34218334Speter#endif /* CRYPT */
34318334Speter		rem = rcmd_af(&host, sp->s_port, localname, user, term, 0,
34418334Speter			      family);
34518334Speter	}
34618334Speter#else
34718334Speter	rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
34818334Speter#endif /* KERBEROS */
34918334Speter
35018334Speter	if (rem < 0)
35118334Speter		exit(1);
35218334Speter
35318334Speter	if (dflag &&
35418334Speter	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
35518334Speter		warn("setsockopt");
35618334Speter	if (Dflag &&
35718334Speter	    setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
35818334Speter		warn("setsockopt NODELAY (ignored)");
35918334Speter
36018334Speter	sslen = sizeof(ss);
36118334Speter	one = IPTOS_LOWDELAY;
36218334Speter	if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
36318334Speter	    ss.ss_family == AF_INET) {
36418334Speter		if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
36518334Speter			       sizeof(int)) < 0)
36618334Speter			warn("setsockopt TOS (ignored)");
36718334Speter	} else
36818334Speter		if (ss.ss_family == AF_INET)
36918334Speter			warn("setsockopt getsockname failed");
37018334Speter
37118334Speter	(void)setuid(uid);
37218334Speter	doit(omask);
37318334Speter	/*NOTREACHED*/
37418334Speter}
37518334Speter
37618334Speterint child, defflags, deflflags, tabflag;
37718334Speterchar deferase, defkill;
37818334Speterstruct tchars deftc;
37918334Speterstruct ltchars defltc;
38018334Speterstruct tchars notc = { -1, -1, -1, -1, -1, -1 };
38118334Speterstruct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
38218334Speter
38318334Spetervoid
38418334Speterdoit(omask)
38518334Speter	long omask;
38618334Speter{
38718334Speter	struct sgttyb sb;
38818334Speter
38918334Speter	(void)ioctl(0, TIOCGETP, (char *)&sb);
39018334Speter	defflags = sb.sg_flags;
39118334Speter	tabflag = defflags & TBDELAY;
39218334Speter	defflags &= ECHO | CRMOD;
39318334Speter	deferase = sb.sg_erase;
39418334Speter	defkill = sb.sg_kill;
39518334Speter	(void)ioctl(0, TIOCLGET, &deflflags);
39618334Speter	(void)ioctl(0, TIOCGETC, &deftc);
39718334Speter	notc.t_startc = deftc.t_startc;
39818334Speter	notc.t_stopc = deftc.t_stopc;
39918334Speter	(void)ioctl(0, TIOCGLTC, &defltc);
40018334Speter	(void)signal(SIGINT, SIG_IGN);
40118334Speter	setsignal(SIGHUP);
40218334Speter	setsignal(SIGQUIT);
40318334Speter	child = fork();
40418334Speter	if (child == -1) {
40518334Speter		warn("fork");
40618334Speter		done(1);
40718334Speter	}
40818334Speter	if (child == 0) {
40918334Speter		mode(1);
41018334Speter		if (reader(omask) == 0) {
41118334Speter			msg("connection closed.");
41218334Speter			exit(0);
41318334Speter		}
41418334Speter		sleep(1);
41518334Speter		msg("\007connection closed.");
41618334Speter		exit(1);
41718334Speter	}
41818334Speter
41918334Speter	/*
42018334Speter	 * We may still own the socket, and may have a pending SIGURG (or might
42118334Speter	 * receive one soon) that we really want to send to the reader.  When
42218334Speter	 * one of these comes in, the trap copytochild simply copies such
42318334Speter	 * signals to the child. We can now unblock SIGURG and SIGUSR1
42418334Speter	 * that were set above.
42518334Speter	 */
42618334Speter	(void)sigsetmask(omask);
42718334Speter	(void)signal(SIGCHLD, catch_child);
42818334Speter	writer();
42918334Speter	msg("closed connection.");
43018334Speter	done(0);
43118334Speter}
43218334Speter
43318334Speter/* trap a signal, unless it is being ignored. */
43418334Spetervoid
43518334Spetersetsignal(sig)
43618334Speter	int sig;
43718334Speter{
43818334Speter	int omask = sigblock(sigmask(sig));
43918334Speter
44018334Speter	if (signal(sig, exit) == SIG_IGN)
44118334Speter		(void)signal(sig, SIG_IGN);
44218334Speter	(void)sigsetmask(omask);
44318334Speter}
44418334Speter
44518334Spetervoid
44618334Speterdone(status)
44718334Speter	int status;
44818334Speter{
44918334Speter	int w, wstatus;
45018334Speter
45118334Speter	mode(0);
45218334Speter	if (child > 0) {
45318334Speter		/* make sure catch_child does not snap it up */
45418334Speter		(void)signal(SIGCHLD, SIG_DFL);
45518334Speter		if (kill(child, SIGKILL) >= 0)
45618334Speter			while ((w = wait(&wstatus)) > 0 && w != child);
45718334Speter	}
45818334Speter	exit(status);
45918334Speter}
46018334Speter
46118334Speterint dosigwinch;
46218334Speter
46318334Speter/*
46418334Speter * This is called when the reader process gets the out-of-band (urgent)
46518334Speter * request to turn on the window-changing protocol.
46618334Speter */
46718334Spetervoid
46818334Speterwriteroob(signo)
46918334Speter	int signo;
47018334Speter{
47118334Speter	if (dosigwinch == 0) {
47218334Speter		sendwindow();
47318334Speter		(void)signal(SIGWINCH, sigwinch);
47418334Speter	}
47518334Speter	dosigwinch = 1;
47618334Speter}
47718334Speter
47818334Spetervoid
47918334Spetercatch_child(signo)
48018334Speter	int signo;
48118334Speter{
48218334Speter	union wait status;
48318334Speter	int pid;
48418334Speter
48518334Speter	for (;;) {
48618334Speter		pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
48718334Speter		if (pid == 0)
48818334Speter			return;
48918334Speter		/* if the child (reader) dies, just quit */
49018334Speter		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
49118334Speter			done((int)(status.w_termsig | status.w_retcode));
49218334Speter	}
49318334Speter	/* NOTREACHED */
49418334Speter}
49518334Speter
49618334Speter/*
49718334Speter * writer: write to remote: 0 -> line.
49818334Speter * ~.				terminate
49918334Speter * ~^Z				suspend rlogin process.
50018334Speter * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
50118334Speter */
50218334Spetervoid
50318334Speterwriter()
50418334Speter{
50518334Speter	register int bol, local, n;
50618334Speter	char c;
50718334Speter
50818334Speter	bol = 1;			/* beginning of line */
50918334Speter	local = 0;
51018334Speter	for (;;) {
51118334Speter		n = read(STDIN_FILENO, &c, 1);
51218334Speter		if (n <= 0) {
51318334Speter			if (n < 0 && errno == EINTR)
51418334Speter				continue;
51518334Speter			break;
51618334Speter		}
51718334Speter		/*
51818334Speter		 * If we're at the beginning of the line and recognize a
51918334Speter		 * command character, then we echo locally.  Otherwise,
52018334Speter		 * characters are echo'd remotely.  If the command character
52118334Speter		 * is doubled, this acts as a force and local echo is
52218334Speter		 * suppressed.
52318334Speter		 */
52418334Speter		if (bol) {
52518334Speter			bol = 0;
52618334Speter			if (!noescape && c == escapechar) {
52718334Speter				local = 1;
52818334Speter				continue;
52918334Speter			}
53018334Speter		} else if (local) {
53118334Speter			local = 0;
53218334Speter			if (c == '.' || c == deftc.t_eofc) {
53318334Speter				echo(c);
53418334Speter				break;
53518334Speter			}
53618334Speter			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
53718334Speter				bol = 1;
53818334Speter				echo(c);
53918334Speter				stop(c);
54018334Speter				continue;
54118334Speter			}
54218334Speter			if (c != escapechar)
54318334Speter#ifdef CRYPT
54418334Speter#ifdef KERBEROS
54518334Speter				if (doencrypt)
54618334Speter					(void)des_enc_write(rem,
54718334Speter					    (char *)&escapechar, 1,
54818334Speter						schedule, &cred.session);
54918334Speter				else
55018334Speter#endif
55118334Speter#endif
55218334Speter					(void)write(rem, &escapechar, 1);
55318334Speter		}
55418334Speter
55518334Speter#ifdef CRYPT
55618334Speter#ifdef KERBEROS
55718334Speter		if (doencrypt) {
55818334Speter			if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
55918334Speter				msg("line gone");
56018334Speter				break;
56118334Speter			}
56218334Speter		} else
56318334Speter#endif
56418334Speter#endif
56518334Speter			if (write(rem, &c, 1) == 0) {
56618334Speter				msg("line gone");
56718334Speter				break;
56818334Speter			}
56918334Speter		bol = c == defkill || c == deftc.t_eofc ||
57018334Speter		    c == deftc.t_intrc || c == defltc.t_suspc ||
57118334Speter		    c == '\r' || c == '\n';
57218334Speter	}
57318334Speter}
57418334Speter
57518334Spetervoid
57618334Speter#if __STDC__
57718334Speterecho(register char c)
57818334Speter#else
57918334Speterecho(c)
58018334Speter	register char c;
58118334Speter#endif
58218334Speter{
58318334Speter	register char *p;
58418334Speter	char buf[8];
58518334Speter
58618334Speter	p = buf;
58718334Speter	c &= 0177;
58818334Speter	*p++ = escapechar;
58918334Speter	if (c < ' ') {
59018334Speter		*p++ = '^';
59118334Speter		*p++ = c + '@';
59218334Speter	} else if (c == 0177) {
59318334Speter		*p++ = '^';
59418334Speter		*p++ = '?';
59518334Speter	} else
59618334Speter		*p++ = c;
59718334Speter	*p++ = '\r';
59818334Speter	*p++ = '\n';
59918334Speter	(void)write(STDOUT_FILENO, buf, p - buf);
60018334Speter}
60118334Speter
60218334Spetervoid
60318334Speter#if __STDC__
60418334Speterstop(char cmdc)
60518334Speter#else
60618334Speterstop(cmdc)
60718334Speter	char cmdc;
60818334Speter#endif
60918334Speter{
61018334Speter	mode(0);
61118334Speter	(void)signal(SIGCHLD, SIG_IGN);
61218334Speter	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
61318334Speter	(void)signal(SIGCHLD, catch_child);
61418334Speter	mode(1);
61518334Speter	sigwinch(0);			/* check for size changes */
61618334Speter}
61718334Speter
61818334Spetervoid
61918334Spetersigwinch(signo)
62018334Speter	int signo;
62118334Speter{
62218334Speter	struct winsize ws;
62318334Speter
62418334Speter	if (dosigwinch && get_window_size(0, &ws) == 0 &&
62518334Speter	    bcmp(&ws, &winsize, sizeof(ws))) {
62618334Speter		winsize = ws;
62718334Speter		sendwindow();
62818334Speter	}
62918334Speter}
63018334Speter
63118334Speter/*
63218334Speter * Send the window size to the server via the magic escape
63318334Speter */
63418334Spetervoid
63518334Spetersendwindow()
63618334Speter{
63718334Speter	struct winsize *wp;
63818334Speter	char obuf[4 + sizeof (struct winsize)];
63918334Speter
64018334Speter	wp = (struct winsize *)(obuf+4);
64118334Speter	obuf[0] = 0377;
64218334Speter	obuf[1] = 0377;
64318334Speter	obuf[2] = 's';
64418334Speter	obuf[3] = 's';
64518334Speter	wp->ws_row = htons(winsize.ws_row);
64618334Speter	wp->ws_col = htons(winsize.ws_col);
64718334Speter	wp->ws_xpixel = htons(winsize.ws_xpixel);
64818334Speter	wp->ws_ypixel = htons(winsize.ws_ypixel);
64918334Speter
65018334Speter#ifdef CRYPT
65118334Speter#ifdef KERBEROS
65218334Speter	if(doencrypt)
65318334Speter		(void)des_enc_write(rem, obuf, sizeof(obuf),
65418334Speter			schedule, &cred.session);
65518334Speter	else
65618334Speter#endif
65718334Speter#endif
65818334Speter		(void)write(rem, obuf, sizeof(obuf));
65918334Speter}
66018334Speter
66118334Speter/*
66218334Speter * reader: read from remote: line -> 1
66318334Speter */
66418334Speter#define	READING	1
66518334Speter#define	WRITING	2
66618334Speter
66718334Speterjmp_buf rcvtop;
66818334Speterint ppid, rcvcnt, rcvstate;
66918334Speterchar rcvbuf[8 * 1024];
67018334Speter
67118334Spetervoid
67218334Speteroob(signo)
67318334Speter	int signo;
67418334Speter{
67518334Speter	struct sgttyb sb;
67618334Speter	int atmark, n, out, rcvd;
67718334Speter	char waste[BUFSIZ], mark;
67818334Speter
67918334Speter	out = O_RDWR;
68018334Speter	rcvd = 0;
68118334Speter	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
68218334Speter		switch (errno) {
68318334Speter		case EWOULDBLOCK:
68418334Speter			/*
68518334Speter			 * Urgent data not here yet.  It may not be possible
68618334Speter			 * to send it yet if we are blocked for output and
68718334Speter			 * our input buffer is full.
68818334Speter			 */
68918334Speter			if (rcvcnt < sizeof(rcvbuf)) {
69018334Speter				n = read(rem, rcvbuf + rcvcnt,
69118334Speter				    sizeof(rcvbuf) - rcvcnt);
69218334Speter				if (n <= 0)
69318334Speter					return;
69418334Speter				rcvd += n;
69518334Speter			} else {
69618334Speter				n = read(rem, waste, sizeof(waste));
69718334Speter				if (n <= 0)
69818334Speter					return;
69918334Speter			}
70018334Speter			continue;
70118334Speter		default:
70218334Speter			return;
70318334Speter		}
70418334Speter	}
70518334Speter	if (mark & TIOCPKT_WINDOW) {
70618334Speter		/* Let server know about window size changes */
70718334Speter		(void)kill(ppid, SIGUSR1);
70818334Speter	}
70918334Speter	if (!eight && (mark & TIOCPKT_NOSTOP)) {
71018334Speter		(void)ioctl(0, TIOCGETP, (char *)&sb);
71118334Speter		sb.sg_flags &= ~CBREAK;
71218334Speter		sb.sg_flags |= RAW;
71318334Speter		(void)ioctl(0, TIOCSETN, (char *)&sb);
71418334Speter		notc.t_stopc = -1;
71518334Speter		notc.t_startc = -1;
71618334Speter		(void)ioctl(0, TIOCSETC, (char *)&notc);
71718334Speter	}
71818334Speter	if (!eight && (mark & TIOCPKT_DOSTOP)) {
71918334Speter		(void)ioctl(0, TIOCGETP, (char *)&sb);
72018334Speter		sb.sg_flags &= ~RAW;
72118334Speter		sb.sg_flags |= CBREAK;
72218334Speter		(void)ioctl(0, TIOCSETN, (char *)&sb);
72318334Speter		notc.t_stopc = deftc.t_stopc;
72418334Speter		notc.t_startc = deftc.t_startc;
72518334Speter		(void)ioctl(0, TIOCSETC, (char *)&notc);
72618334Speter	}
72718334Speter	if (mark & TIOCPKT_FLUSHWRITE) {
72818334Speter		(void)ioctl(1, TIOCFLUSH, (char *)&out);
72918334Speter		for (;;) {
73018334Speter			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
73118334Speter				warn("ioctl");
73218334Speter				break;
73318334Speter			}
73418334Speter			if (atmark)
73518334Speter				break;
73618334Speter			n = read(rem, waste, sizeof (waste));
73718334Speter			if (n <= 0)
73818334Speter				break;
73918334Speter		}
74018334Speter		/*
74118334Speter		 * Don't want any pending data to be output, so clear the recv
74218334Speter		 * buffer.  If we were hanging on a write when interrupted,
74318334Speter		 * don't want it to restart.  If we were reading, restart
74418334Speter		 * anyway.
74518334Speter		 */
74618334Speter		rcvcnt = 0;
74718334Speter		longjmp(rcvtop, 1);
74818334Speter	}
74918334Speter
75018334Speter	/* oob does not do FLUSHREAD (alas!) */
75118334Speter
75218334Speter	/*
75318334Speter	 * If we filled the receive buffer while a read was pending, longjmp
75418334Speter	 * to the top to restart appropriately.  Don't abort a pending write,
75518334Speter	 * however, or we won't know how much was written.
75618334Speter	 */
75718334Speter	if (rcvd && rcvstate == READING)
75818334Speter		longjmp(rcvtop, 1);
75918334Speter}
76018334Speter
76118334Speter/* reader: read from remote: line -> 1 */
76218334Speterint
76318334Speterreader(omask)
76418334Speter	int omask;
76518334Speter{
76618334Speter	int pid, n, remaining;
76718334Speter	char *bufp;
76818334Speter
76918334Speter#if BSD >= 43 || defined(SUNOS4)
77018334Speter	pid = getpid();		/* modern systems use positives for pid */
77118334Speter#else
77218334Speter	pid = -getpid();	/* old broken systems use negatives */
77318334Speter#endif
77418334Speter	(void)signal(SIGTTOU, SIG_IGN);
77518334Speter	(void)signal(SIGURG, oob);
77618334Speter	(void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */
77718334Speter	ppid = getppid();
77818334Speter	(void)fcntl(rem, F_SETOWN, pid);
77918334Speter	(void)setjmp(rcvtop);
78018334Speter	(void)sigsetmask(omask);
78118334Speter	bufp = rcvbuf;
78218334Speter	for (;;) {
78318334Speter		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
78418334Speter			rcvstate = WRITING;
78518334Speter			n = write(STDOUT_FILENO, bufp, remaining);
78618334Speter			if (n < 0) {
78718334Speter				if (errno != EINTR)
78818334Speter					return (-1);
78918334Speter				continue;
79018334Speter			}
79118334Speter			bufp += n;
79218334Speter		}
79318334Speter		bufp = rcvbuf;
79418334Speter		rcvcnt = 0;
79518334Speter		rcvstate = READING;
79618334Speter
79718334Speter#ifdef CRYPT
79818334Speter#ifdef KERBEROS
79918334Speter		if (doencrypt)
80018334Speter			rcvcnt = des_enc_read(rem, rcvbuf, sizeof(rcvbuf),
80118334Speter				schedule, &cred.session);
80218334Speter		else
80318334Speter#endif
80418334Speter#endif
80518334Speter			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
80618334Speter		if (rcvcnt == 0)
80718334Speter			return (0);
80818334Speter		if (rcvcnt < 0) {
80918334Speter			if (errno == EINTR)
81018334Speter				continue;
81118334Speter			warn("read");
81218334Speter			return (-1);
81318334Speter		}
81418334Speter	}
81518334Speter}
81618334Speter
81718334Spetervoid
81818334Spetermode(f)
81918334Speter	int f;
82018334Speter{
82118334Speter	struct ltchars *ltc;
82218334Speter	struct sgttyb sb;
82318334Speter	struct tchars *tc;
82418334Speter	int lflags;
82518334Speter
82618334Speter	(void)ioctl(0, TIOCGETP, (char *)&sb);
82718334Speter	(void)ioctl(0, TIOCLGET, (char *)&lflags);
82818334Speter	switch(f) {
82918334Speter	case 0:
83018334Speter		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
83118334Speter		sb.sg_flags |= defflags|tabflag;
83218334Speter		tc = &deftc;
83318334Speter		ltc = &defltc;
83418334Speter		sb.sg_kill = defkill;
83518334Speter		sb.sg_erase = deferase;
83618334Speter		lflags = deflflags;
83718334Speter		break;
83818334Speter	case 1:
83918334Speter		sb.sg_flags |= (eight ? RAW : CBREAK);
84018334Speter		sb.sg_flags &= ~defflags;
84118334Speter		/* preserve tab delays, but turn off XTABS */
84218334Speter		if ((sb.sg_flags & TBDELAY) == XTABS)
84318334Speter			sb.sg_flags &= ~TBDELAY;
84418334Speter		tc = &notc;
84518334Speter		ltc = &noltc;
84618334Speter		sb.sg_kill = sb.sg_erase = -1;
84718334Speter		if (litout)
84818334Speter			lflags |= LLITOUT;
84918334Speter		break;
85018334Speter	default:
85118334Speter		return;
85218334Speter	}
85318334Speter	(void)ioctl(0, TIOCSLTC, (char *)ltc);
85418334Speter	(void)ioctl(0, TIOCSETC, (char *)tc);
85518334Speter	(void)ioctl(0, TIOCSETN, (char *)&sb);
85618334Speter	(void)ioctl(0, TIOCLSET, (char *)&lflags);
85718334Speter}
85818334Speter
85918334Spetervoid
86018334Speterlostpeer(signo)
86118334Speter	int signo;
86218334Speter{
86318334Speter	(void)signal(SIGPIPE, SIG_IGN);
86418334Speter	msg("\007connection closed.");
86518334Speter	done(1);
86618334Speter}
86718334Speter
86818334Speter/* copy SIGURGs to the child process via SIGUSR1. */
86918334Spetervoid
87018334Spetercopytochild(signo)
87118334Speter	int signo;
87218334Speter{
87318334Speter	(void)kill(child, SIGUSR1);
87418334Speter}
87518334Speter
87618334Spetervoid
87718334Spetermsg(str)
87818334Speter	char *str;
87918334Speter{
88018334Speter	(void)fprintf(stderr, "rlogin: %s\r\n", str);
88118334Speter}
88218334Speter
88318334Spetervoid
88418334Speterusage()
88518334Speter{
88618334Speter	(void)fprintf(stderr,
88718334Speter	"usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
88818334Speter#ifdef KERBEROS
88918334Speter#ifdef CRYPT
89018334Speter	    "8DEKLdx", " [-k realm] ");
89118334Speter#else
89218334Speter	    "8DEKLd", " [-k realm] ");
89318334Speter#endif
89418334Speter#else
89518334Speter	    "8DEKLd", " ");
89618334Speter#endif
89718334Speter	exit(1);
89818334Speter}
89918334Speter
90018334Speter/*
90118334Speter * The following routine provides compatibility (such as it is) between older
90218334Speter * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
90318334Speter */
90418334Speter#ifdef OLDSUN
90518334Speterint
90618334Speterget_window_size(fd, wp)
90718334Speter	int fd;
90818334Speter	struct winsize *wp;
90918334Speter{
91018334Speter	struct ttysize ts;
91118334Speter	int error;
91218334Speter
91318334Speter	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
91418334Speter		return (error);
91518334Speter	wp->ws_row = ts.ts_lines;
91618334Speter	wp->ws_col = ts.ts_cols;
91718334Speter	wp->ws_xpixel = 0;
91818334Speter	wp->ws_ypixel = 0;
91918334Speter	return (0);
92018334Speter}
92118334Speter#endif
92218334Speter
92318334Speteru_int
92418334Spetergetescape(p)
92518334Speter	register char *p;
92618334Speter{
92718334Speter	long val;
92818334Speter	int len;
92918334Speter
93018334Speter	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
93118334Speter		return ((u_int)*p);
93218334Speter					/* otherwise, \nnn */
93318334Speter	if (*p == '\\' && len >= 2 && len <= 4) {
93418334Speter		val = strtol(++p, NULL, 8);
93518334Speter		for (;;) {
93618334Speter			if (!*++p)
93718334Speter				return ((u_int)val);
93818334Speter			if (*p < '0' || *p > '8')
93918334Speter				break;
94018334Speter		}
94118334Speter	}
94218334Speter	msg("illegal option value -- e");
94318334Speter	usage();
94418334Speter	/* NOTREACHED */
94518334Speter}
94618334Speter