rlogin.c revision 1590
1218792Snp/*
2218792Snp * Copyright (c) 1983, 1990, 1993
3218792Snp *	The Regents of the University of California.  All rights reserved.
4218792Snp *
5218792Snp * Redistribution and use in source and binary forms, with or without
6218792Snp * modification, are permitted provided that the following conditions
7218792Snp * are met:
8218792Snp * 1. Redistributions of source code must retain the above copyright
9218792Snp *    notice, this list of conditions and the following disclaimer.
10218792Snp * 2. Redistributions in binary form must reproduce the above copyright
11218792Snp *    notice, this list of conditions and the following disclaimer in the
12218792Snp *    documentation and/or other materials provided with the distribution.
13218792Snp * 3. All advertising materials mentioning features or use of this software
14218792Snp *    must display the following acknowledgement:
15218792Snp *	This product includes software developed by the University of
16218792Snp *	California, Berkeley and its contributors.
17218792Snp * 4. Neither the name of the University nor the names of its contributors
18218792Snp *    may be used to endorse or promote products derived from this software
19218792Snp *    without specific prior written permission.
20218792Snp *
21218792Snp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22218792Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23218792Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24218792Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25218792Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26218792Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27218792Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28218792Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29218792Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30218792Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31218792Snp * SUCH DAMAGE.
32218792Snp */
33218792Snp
34228561Snp#ifndef lint
35218792Snpstatic char copyright[] =
36218792Snp"@(#) Copyright (c) 1983, 1990, 1993\n\
37218792Snp	The Regents of the University of California.  All rights reserved.\n";
38257176Sglebius#endif /* not lint */
39218792Snp
40257176Sglebius#ifndef lint
41257176Sglebiusstatic char sccsid[] = "@(#)rlogin.c	8.1 (Berkeley) 6/6/93";
42345664Sjhb#endif /* not lint */
43257176Sglebius
44257176Sglebius/*
45218792Snp * rlogin - remote login
46218792Snp */
47218792Snp#include <sys/param.h>
48218792Snp#include <sys/socket.h>
49218792Snp#include <sys/time.h>
50218792Snp#include <sys/resource.h>
51218792Snp#include <sys/wait.h>
52257176Sglebius
53218792Snp#include <netinet/in.h>
54235944Sbz#include <netinet/in_systm.h>
55218792Snp#include <netinet/ip.h>
56218792Snp
57218792Snp#include <errno.h>
58301535Snp#include <fcntl.h>
59266757Snp#include <netdb.h>
60228561Snp#include <pwd.h>
61218792Snp#include <setjmp.h>
62275733Snp#include <sgtty.h>
63218792Snp#include <signal.h>
64218792Snp#include <stdio.h>
65218792Snp#include <stdlib.h>
66218792Snp#include <string.h>
67218792Snp#include <unistd.h>
68218792Snp
69218792Snp#ifdef __STDC__
70218792Snp#include <stdarg.h>
71218792Snp#else
72218792Snp#include <varargs.h>
73218792Snp#endif
74339396Snp
75218792Snp#ifdef KERBEROS
76218792Snp#include <kerberosIV/des.h>
77231115Snp#include <kerberosIV/krb.h>
78231115Snp
79231115Snp#include "krb.h"
80231115Snp
81231115SnpCREDENTIALS cred;
82231115SnpKey_schedule schedule;
83344858Sjhbint use_kerberos = 1, doencrypt;
84344858Sjhbchar dst_realm_buf[REALM_SZ], *dest_realm = NULL;
85218792Snp#endif
86218792Snp
87218792Snp#ifndef TIOCPKT_WINDOW
88218792Snp#define	TIOCPKT_WINDOW	0x80
89269411Snp#endif
90269411Snp
91269411Snp/* concession to Sun */
92269411Snp#ifndef SIGUSR1
93269411Snp#define	SIGUSR1	30
94269411Snp#endif
95269411Snp
96269411Snpint eight, litout, rem;
97218792Snp
98218792Snpint noescape;
99218792Snpu_char escapechar = '~';
100269411Snp
101269411Snpchar *speeds[] = {
102218792Snp	"0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
103269411Snp	"1800", "2400", "4800", "9600", "19200", "38400"
104269411Snp};
105269411Snp
106269411Snp#ifdef OLDSUN
107219392Snpstruct winsize {
108263317Snp	unsigned short ws_row, ws_col;
109219392Snp	unsigned short ws_xpixel, ws_ypixel;
110263317Snp};
111219392Snp#else
112275554Snp#define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
113218792Snp#endif
114269411Snpstruct	winsize winsize;
115276485Snp
116276485Snpvoid		catch_child __P((int));
117218792Snpvoid		copytochild __P((int));
118218792Snp__dead void	doit __P((long));
119218792Snp__dead void	done __P((int));
120218792Snpvoid		echo __P((char));
121219944Snpu_int		getescape __P((char *));
122219944Snpvoid		lostpeer __P((int));
123219944Snpvoid		mode __P((int));
124219944Snpvoid		msg __P((char *));
125219944Snpvoid		oob __P((int));
126219944Snpint		reader __P((int));
127219944Snpvoid		sendwindow __P((void));
128266757Snpvoid		setsignal __P((int));
129266757Snpvoid		sigwinch __P((int));
130266757Snpvoid		stop __P((char));
131266757Snp__dead void	usage __P((void));
132266757Snpvoid		writer __P((void));
133266757Snpvoid		writeroob __P((int));
134266757Snp
135266757Snp#ifdef	KERBEROS
136266757Snpvoid		warning __P((const char *, ...));
137266757Snp#endif
138266757Snp#ifdef OLDSUN
139245274Snpint		get_window_size __P((int, struct winsize *));
140245274Snp#endif
141245274Snp
142245274Snpint
143245274Snpmain(argc, argv)
144245274Snp	int argc;
145245274Snp	char *argv[];
146245274Snp{
147245274Snp	extern char *optarg;
148245274Snp	extern int optind;
149218792Snp	struct passwd *pw;
150218792Snp	struct servent *sp;
151218792Snp	struct sgttyb ttyb;
152330307Snp	long omask;
153228561Snp	int argoff, ch, dflag, one, uid;
154228561Snp	char *host, *p, *user, term[1024];
155278374Snp
156255050Snp	argoff = dflag = 0;
157306664Sjhb	one = 1;
158218792Snp	host = user = NULL;
159218792Snp
160218792Snp	if (p = rindex(argv[0], '/'))
161218792Snp		++p;
162291665Sjhb	else
163334562Snp		p = argv[0];
164291665Sjhb
165291665Sjhb	if (strcmp(p, "rlogin"))
166218792Snp		host = p;
167291665Sjhb
168291665Sjhb	/* handle "rlogin host flags" */
169284445Snp	if (!host && argc > 2 && argv[1][0] != '-') {
170284445Snp		host = argv[1];
171330307Snp		argoff = 1;
172330307Snp	}
173330307Snp
174218792Snp#ifdef KERBEROS
175218792Snp#define	OPTIONS	"8EKLde:k:l:x"
176291665Sjhb#else
177291665Sjhb#define	OPTIONS	"8EKLde:l:"
178245274Snp#endif
179245274Snp	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
180245274Snp		switch(ch) {
181218792Snp		case '8':
182291665Sjhb			eight = 1;
183218792Snp			break;
184291665Sjhb		case 'E':
185218792Snp			noescape = 1;
186218792Snp			break;
187218792Snp		case 'K':
188218792Snp#ifdef KERBEROS
189218792Snp			use_kerberos = 0;
190218792Snp#endif
191302110Snp			break;
192309560Sjhb		case 'L':
193218792Snp			litout = 1;
194218792Snp			break;
195218792Snp		case 'd':
196285648Snp			dflag = 1;
197218792Snp			break;
198291665Sjhb		case 'e':
199291665Sjhb			noescape = 0;
200291665Sjhb			escapechar = getescape(optarg);
201291665Sjhb			break;
202291665Sjhb#ifdef KERBEROS
203218792Snp		case 'k':
204318854Snp			dest_realm = dst_realm_buf;
205318854Snp			(void)strncpy(dest_realm, optarg, REALM_SZ);
206318854Snp			break;
207318854Snp#endif
208318854Snp		case 'l':
209228561Snp			user = optarg;
210228561Snp			break;
211228561Snp#ifdef CRYPT
212228561Snp#ifdef KERBEROS
213302110Snp		case 'x':
214302110Snp			doencrypt = 1;
215302110Snp			des_set_key(cred.session, schedule);
216302110Snp			break;
217218792Snp#endif
218330307Snp#endif
219218792Snp		case '?':
220330307Snp		default:
221218792Snp			usage();
222218792Snp		}
223218792Snp	optind += argoff;
224291665Sjhb	argc -= optind;
225291665Sjhb	argv += optind;
226291665Sjhb
227291665Sjhb	/* if haven't gotten a host yet, do so */
228291665Sjhb	if (!host && !(host = *argv++))
229291665Sjhb		usage();
230291665Sjhb
231291665Sjhb	if (*argv)
232291665Sjhb		usage();
233318850Snp
234318850Snp	if (!(pw = getpwuid(uid = getuid()))) {
235318850Snp		(void)fprintf(stderr, "rlogin: unknown user id.\n");
236318850Snp		exit(1);
237318850Snp	}
238301535Snp	if (!user)
239346871Snp		user = pw->pw_name;
240346871Snp
241346871Snp	sp = NULL;
242346871Snp#ifdef KERBEROS
243301535Snp	if (use_kerberos) {
244301535Snp		sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
245318850Snp		if (sp == NULL) {
246301535Snp			use_kerberos = 0;
247346871Snp			warning("can't get entry for %s/tcp service",
248318850Snp			    doencrypt ? "eklogin" : "klogin");
249318850Snp		}
250318850Snp	}
251318850Snp#endif
252318850Snp	if (sp == NULL)
253346871Snp		sp = getservbyname("login", "tcp");
254301535Snp	if (sp == NULL) {
255301535Snp		(void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
256318850Snp		exit(1);
257318850Snp	}
258318850Snp
259318850Snp	(void)strcpy(term, (p = getenv("TERM")) ? p : "network");
260318850Snp	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
261318850Snp		(void)strcat(term, "/");
262318850Snp		(void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
263318850Snp	}
264346871Snp
265346871Snp	(void)get_window_size(0, &winsize);
266346871Snp
267318850Snp	(void)signal(SIGPIPE, lostpeer);
268318850Snp	/* will use SIGUSR1 for window size hack, so hold it off */
269318850Snp	omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
270291665Sjhb	/*
271291665Sjhb	 * We set SIGURG and SIGUSR1 below so that an
272291665Sjhb	 * incoming signal will be held pending rather than being
273291665Sjhb	 * discarded. Note that these routines will be ready to get
274291665Sjhb	 * a signal by the time that they are unblocked below.
275291665Sjhb	 */
276291665Sjhb	(void)signal(SIGURG, copytochild);
277291665Sjhb	(void)signal(SIGUSR1, writeroob);
278291665Sjhb
279318850Snp#ifdef KERBEROS
280301535Snptry_connect:
281291665Sjhb	if (use_kerberos) {
282291665Sjhb		struct hostent *hp;
283291665Sjhb
284291665Sjhb		/* Fully qualify hostname (needed for krb_realmofhost). */
285291665Sjhb		hp = gethostbyname(host);
286291665Sjhb		if (hp != NULL && !(host = strdup(hp->h_name))) {
287291665Sjhb			(void)fprintf(stderr, "rlogin: %s\n",
288291665Sjhb			    strerror(ENOMEM));
289291665Sjhb			exit(1);
290291665Sjhb		}
291330307Snp
292330307Snp		rem = KSUCCESS;
293291665Sjhb		errno = 0;
294218792Snp		if (dest_realm == NULL)
295330307Snp			dest_realm = krb_realmofhost(host);
296218792Snp
297272200Snp#ifdef CRYPT
298272200Snp		if (doencrypt)
299272200Snp			rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
300276485Snp			    dest_realm, &cred, schedule);
301345664Sjhb		else
302345664Sjhb#endif /* CRYPT */
303345664Sjhb			rem = krcmd(&host, sp->s_port, user, term, 0,
304345664Sjhb			    dest_realm);
305272200Snp		if (rem < 0) {
306218792Snp			use_kerberos = 0;
307218792Snp			sp = getservbyname("login", "tcp");
308218792Snp			if (sp == NULL) {
309291665Sjhb				(void)fprintf(stderr,
310291665Sjhb				    "rlogin: unknown service login/tcp.\n");
311263317Snp				exit(1);
312263317Snp			}
313263317Snp			if (errno == ECONNREFUSED)
314263317Snp				warning("remote host doesn't support Kerberos");
315263317Snp			if (errno == ENOENT)
316263317Snp				warning("can't provide Kerberos auth data");
317263317Snp			goto try_connect;
318263317Snp		}
319263317Snp	} else {
320263317Snp#ifdef CRYPT
321263317Snp		if (doencrypt) {
322263317Snp			(void)fprintf(stderr,
323218792Snp			    "rlogin: the -x flag requires Kerberos authentication.\n");
324218792Snp			exit(1);
325263317Snp		}
326263317Snp#endif /* CRYPT */
327268971Snp		rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
328263317Snp	}
329263317Snp#else
330263317Snp	rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
331218792Snp#endif /* KERBEROS */
332218792Snp
333218792Snp	if (rem < 0)
334218792Snp		exit(1);
335218792Snp
336276485Snp	if (dflag &&
337218792Snp	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
338218792Snp		(void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
339218792Snp		    strerror(errno));
340269411Snp	one = IPTOS_LOWDELAY;
341269411Snp	if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
342269411Snp		perror("rlogin: setsockopt TOS (ignored)");
343269411Snp
344269411Snp	(void)setuid(uid);
345269411Snp	doit(omask);
346269411Snp	/*NOTREACHED*/
347269411Snp}
348269411Snp
349269411Snpint child, defflags, deflflags, tabflag;
350218792Snpchar deferase, defkill;
351218792Snpstruct tchars deftc;
352228561Snpstruct ltchars defltc;
353228561Snpstruct tchars notc = { -1, -1, -1, -1, -1, -1 };
354330307Snpstruct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
355228561Snp
356318842Snpvoid
357220649Snpdoit(omask)
358220649Snp	long omask;
359220649Snp{
360220649Snp	struct sgttyb sb;
361220649Snp
362302110Snp	(void)ioctl(0, TIOCGETP, (char *)&sb);
363302110Snp	defflags = sb.sg_flags;
364302110Snp	tabflag = defflags & TBDELAY;
365302110Snp	defflags &= ECHO | CRMOD;
366302110Snp	deferase = sb.sg_erase;
367218792Snp	defkill = sb.sg_kill;
368218792Snp	(void)ioctl(0, TIOCLGET, &deflflags);
369346852Snp	(void)ioctl(0, TIOCGETC, &deftc);
370346852Snp	notc.t_startc = deftc.t_startc;
371346852Snp	notc.t_stopc = deftc.t_stopc;
372346852Snp	(void)ioctl(0, TIOCGLTC, &defltc);
373346852Snp	(void)signal(SIGINT, SIG_IGN);
374346852Snp	setsignal(SIGHUP);
375346855Snp	setsignal(SIGQUIT);
376346863Snp	child = fork();
377346852Snp	if (child == -1) {
378346852Snp		(void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
379346852Snp		done(1);
380346852Snp	}
381346852Snp	if (child == 0) {
382302339Snp		mode(1);
383302339Snp		if (reader(omask) == 0) {
384302339Snp			msg("connection closed.");
385302339Snp			exit(0);
386302339Snp		}
387302339Snp		sleep(1);
388302339Snp		msg("\007connection closed.");
389218792Snp		exit(1);
390218792Snp	}
391218792Snp
392218792Snp	/*
393219290Snp	 * We may still own the socket, and may have a pending SIGURG (or might
394228561Snp	 * receive one soon) that we really want to send to the reader.  When
395218792Snp	 * one of these comes in, the trap copytochild simply copies such
396269411Snp	 * signals to the child. We can now unblock SIGURG and SIGUSR1
397269411Snp	 * that were set above.
398218792Snp	 */
399218792Snp	(void)sigsetmask(omask);
400228561Snp	(void)signal(SIGCHLD, catch_child);
401218792Snp	writer();
402269411Snp	msg("closed connection.");
403218792Snp	done(0);
404228561Snp}
405269411Snp
406228561Snp/* trap a signal, unless it is being ignored. */
407228561Snpvoid
408269411Snpsetsignal(sig)
409269411Snp	int sig;
410269411Snp{
411269411Snp	int omask = sigblock(sigmask(sig));
412218792Snp
413218792Snp	if (signal(sig, exit) == SIG_IGN)
414218792Snp		(void)signal(sig, SIG_IGN);
415228561Snp	(void)sigsetmask(omask);
416228561Snp}
417228561Snp
418228561Snp__dead void
419218792Snpdone(status)
420276485Snp	int status;
421276485Snp{
422276485Snp	int w, wstatus;
423318854Snp
424218792Snp	mode(0);
425218792Snp	if (child > 0) {
426248925Snp		/* make sure catch_child does not snap it up */
427249392Snp		(void)signal(SIGCHLD, SIG_DFL);
428248925Snp		if (kill(child, SIGKILL) >= 0)
429218792Snp			while ((w = wait(&wstatus)) > 0 && w != child);
430218792Snp	}
431218792Snp	exit(status);
432218792Snp}
433218792Snp
434218792Snpint dosigwinch;
435218792Snp
436228561Snp/*
437228561Snp * This is called when the reader process gets the out-of-band (urgent)
438306664Sjhb * request to turn on the window-changing protocol.
439218792Snp */
440218792Snpvoid
441218792Snpwriteroob(signo)
442339396Snp	int signo;
443248925Snp{
444248925Snp	if (dosigwinch == 0) {
445276485Snp		sendwindow();
446218792Snp		(void)signal(SIGWINCH, sigwinch);
447218792Snp	}
448276485Snp	dosigwinch = 1;
449276485Snp}
450219288Snp
451228561Snpvoid
452276485Snpcatch_child(signo)
453228561Snp	int signo;
454276485Snp{
455276485Snp	union wait status;
456276485Snp	int pid;
457276485Snp
458220873Snp	for (;;) {
459218792Snp		pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
460263317Snp		if (pid == 0)
461263317Snp			return;
462263317Snp		/* if the child (reader) dies, just quit */
463263317Snp		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
464263317Snp			done((int)(status.w_termsig | status.w_retcode));
465263317Snp	}
466255050Snp	/* NOTREACHED */
467255050Snp}
468263317Snp
469263317Snp/*
470263317Snp * writer: write to remote: 0 -> line.
471263317Snp * ~.				terminate
472263317Snp * ~^Z				suspend rlogin process.
473263317Snp * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
474228561Snp */
475296603Snpvoid
476296603Snpwriter()
477296603Snp{
478296603Snp	register int bol, local, n;
479296603Snp	char c;
480296603Snp
481296603Snp	bol = 1;			/* beginning of line */
482296603Snp	local = 0;
483296603Snp	for (;;) {
484296603Snp		n = read(STDIN_FILENO, &c, 1);
485296603Snp		if (n <= 0) {
486296603Snp			if (n < 0 && errno == EINTR)
487296603Snp				continue;
488296603Snp			break;
489296603Snp		}
490296603Snp		/*
491296603Snp		 * If we're at the beginning of the line and recognize a
492296603Snp		 * command character, then we echo locally.  Otherwise,
493296603Snp		 * characters are echo'd remotely.  If the command character
494296603Snp		 * is doubled, this acts as a force and local echo is
495296603Snp		 * suppressed.
496296603Snp		 */
497296603Snp		if (bol) {
498228561Snp			bol = 0;
499228561Snp			if (!noescape && c == escapechar) {
500255050Snp				local = 1;
501269428Snp				continue;
502228561Snp			}
503228561Snp		} else if (local) {
504269428Snp			local = 0;
505269428Snp			if (c == '.' || c == deftc.t_eofc) {
506269428Snp				echo(c);
507269428Snp				break;
508228561Snp			}
509218792Snp			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
510218792Snp				bol = 1;
511218792Snp				echo(c);
512218792Snp				stop(c);
513269428Snp				continue;
514269428Snp			}
515269428Snp			if (c != escapechar)
516269428Snp#ifdef CRYPT
517263317Snp#ifdef KERBEROS
518269428Snp				if (doencrypt)
519269428Snp					(void)des_write(rem,
520269428Snp					    (char *)&escapechar, 1);
521269428Snp				else
522263317Snp#endif
523269428Snp#endif
524269428Snp					(void)write(rem, &escapechar, 1);
525269428Snp		}
526269428Snp
527269428Snp#ifdef CRYPT
528269428Snp#ifdef KERBEROS
529269428Snp		if (doencrypt) {
530269428Snp			if (des_write(rem, &c, 1) == 0) {
531263317Snp				msg("line gone");
532263317Snp				break;
533263317Snp			}
534263317Snp		} else
535263317Snp#endif
536269428Snp#endif
537269428Snp			if (write(rem, &c, 1) == 0) {
538269428Snp				msg("line gone");
539269428Snp				break;
540269428Snp			}
541269428Snp		bol = c == defkill || c == deftc.t_eofc ||
542269428Snp		    c == deftc.t_intrc || c == defltc.t_suspc ||
543269428Snp		    c == '\r' || c == '\n';
544269428Snp	}
545269428Snp}
546269428Snp
547269428Snpvoid
548269428Snp#if __STDC__
549269428Snpecho(register char c)
550218792Snp#else
551218792Snpecho(c)
552276485Snp	register char c;
553276485Snp#endif
554220873Snp{
555218792Snp	register char *p;
556218792Snp	char buf[8];
557220873Snp
558220873Snp	p = buf;
559276485Snp	c &= 0177;
560220873Snp	*p++ = escapechar;
561276485Snp	if (c < ' ') {
562276485Snp		*p++ = '^';
563301628Snp		*p++ = c + '@';
564218792Snp	} else if (c == 0177) {
565276485Snp		*p++ = '^';
566218792Snp		*p++ = '?';
567218792Snp	} else
568218792Snp		*p++ = c;
569237819Snp	*p++ = '\r';
570218792Snp	*p++ = '\n';
571218792Snp	(void)write(STDOUT_FILENO, buf, p - buf);
572218792Snp}
573218792Snp
574276485Snpvoid
575276485Snp#if __STDC__
576276485Snpstop(char cmdc)
577276485Snp#else
578218792Snpstop(cmdc)
579218792Snp	char cmdc;
580220873Snp#endif
581218792Snp{
582218792Snp	mode(0);
583218792Snp	(void)signal(SIGCHLD, SIG_IGN);
584218792Snp	(void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
585228561Snp	(void)signal(SIGCHLD, catch_child);
586218792Snp	mode(1);
587219290Snp	sigwinch(0);			/* check for size changes */
588237819Snp}
589218792Snp
590219290Snpvoid
591218792Snpsigwinch(signo)
592218792Snp	int signo;
593218792Snp{
594218792Snp	struct winsize ws;
595218792Snp
596218792Snp	if (dosigwinch && get_window_size(0, &ws) == 0 &&
597218792Snp	    bcmp(&ws, &winsize, sizeof(ws))) {
598218792Snp		winsize = ws;
599218792Snp		sendwindow();
600218792Snp	}
601237263Snp}
602237263Snp
603237263Snp/*
604237263Snp * Send the window size to the server via the magic escape
605241733Sed */
606237263Snpvoid
607237263Snpsendwindow()
608237263Snp{
609228561Snp	struct winsize *wp;
610228561Snp	char obuf[4 + sizeof (struct winsize)];
611228561Snp
612228561Snp	wp = (struct winsize *)(obuf+4);
613228561Snp	obuf[0] = 0377;
614237263Snp	obuf[1] = 0377;
615237263Snp	obuf[2] = 's';
616237263Snp	obuf[3] = 's';
617237263Snp	wp->ws_row = htons(winsize.ws_row);
618237263Snp	wp->ws_col = htons(winsize.ws_col);
619241733Sed	wp->ws_xpixel = htons(winsize.ws_xpixel);
620237263Snp	wp->ws_ypixel = htons(winsize.ws_ypixel);
621228561Snp
622237263Snp#ifdef CRYPT
623237263Snp#ifdef KERBEROS
624237263Snp	if(doencrypt)
625237263Snp		(void)des_write(rem, obuf, sizeof(obuf));
626276485Snp	else
627237263Snp#endif
628237263Snp#endif
629276485Snp		(void)write(rem, obuf, sizeof(obuf));
630276485Snp}
631276485Snp
632276485Snp/*
633276485Snp * reader: read from remote: line -> 1
634276485Snp */
635228561Snp#define	READING	1
636228561Snp#define	WRITING	2
637228561Snp
638228561Snpjmp_buf rcvtop;
639228561Snpint ppid, rcvcnt, rcvstate;
640220873Snpchar rcvbuf[8 * 1024];
641220873Snp
642228561Snpvoid
643276485Snpoob(signo)
644228561Snp	int signo;
645276485Snp{
646276485Snp	struct sgttyb sb;
647276485Snp	int atmark, n, out, rcvd;
648276485Snp	char waste[BUFSIZ], mark;
649237263Snp
650276485Snp	out = O_RDWR;
651276485Snp	rcvd = 0;
652237263Snp	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
653220873Snp		switch (errno) {
654220873Snp		case EWOULDBLOCK:
655276485Snp			/*
656276485Snp			 * Urgent data not here yet.  It may not be possible
657276485Snp			 * to send it yet if we are blocked for output and
658220873Snp			 * our input buffer is full.
659220873Snp			 */
660220873Snp			if (rcvcnt < sizeof(rcvbuf)) {
661276485Snp				n = read(rem, rcvbuf + rcvcnt,
662276485Snp				    sizeof(rcvbuf) - rcvcnt);
663298955Spfg				if (n <= 0)
664276485Snp					return;
665276485Snp				rcvd += n;
666276485Snp			} else {
667276485Snp				n = read(rem, waste, sizeof(waste));
668276485Snp				if (n <= 0)
669220873Snp					return;
670220873Snp			}
671266757Snp			continue;
672266757Snp		default:
673346875Snp			return;
674291665Sjhb		}
675266757Snp	}
676269411Snp	if (mark & TIOCPKT_WINDOW) {
677266757Snp		/* Let server know about window size changes */
678266757Snp		(void)kill(ppid, SIGUSR1);
679266757Snp	}
680266757Snp	if (!eight && (mark & TIOCPKT_NOSTOP)) {
681266757Snp		(void)ioctl(0, TIOCGETP, (char *)&sb);
682266757Snp		sb.sg_flags &= ~CBREAK;
683266757Snp		sb.sg_flags |= RAW;
684266757Snp		(void)ioctl(0, TIOCSETN, (char *)&sb);
685266757Snp		notc.t_stopc = -1;
686266757Snp		notc.t_startc = -1;
687266757Snp		(void)ioctl(0, TIOCSETC, (char *)&notc);
688266757Snp	}
689266757Snp	if (!eight && (mark & TIOCPKT_DOSTOP)) {
690266757Snp		(void)ioctl(0, TIOCGETP, (char *)&sb);
691266757Snp		sb.sg_flags &= ~RAW;
692266757Snp		sb.sg_flags |= CBREAK;
693266757Snp		(void)ioctl(0, TIOCSETN, (char *)&sb);
694266757Snp		notc.t_stopc = deftc.t_stopc;
695266757Snp		notc.t_startc = deftc.t_startc;
696266757Snp		(void)ioctl(0, TIOCSETC, (char *)&notc);
697266757Snp	}
698266757Snp	if (mark & TIOCPKT_FLUSHWRITE) {
699266757Snp		(void)ioctl(1, TIOCFLUSH, (char *)&out);
700266757Snp		for (;;) {
701266757Snp			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
702266757Snp				(void)fprintf(stderr, "rlogin: ioctl: %s.\n",
703266757Snp				    strerror(errno));
704266757Snp				break;
705266757Snp			}
706266757Snp			if (atmark)
707266757Snp				break;
708266757Snp			n = read(rem, waste, sizeof (waste));
709266757Snp			if (n <= 0)
710266757Snp				break;
711266757Snp		}
712266757Snp		/*
713339396Snp		 * Don't want any pending data to be output, so clear the recv
714266757Snp		 * buffer.  If we were hanging on a write when interrupted,
715266757Snp		 * don't want it to restart.  If we were reading, restart
716266757Snp		 * anyway.
717266757Snp		 */
718266757Snp		rcvcnt = 0;
719266757Snp		longjmp(rcvtop, 1);
720266757Snp	}
721266757Snp
722266757Snp	/* oob does not do FLUSHREAD (alas!) */
723266757Snp
724266757Snp	/*
725266757Snp	 * If we filled the receive buffer while a read was pending, longjmp
726266757Snp	 * to the top to restart appropriately.  Don't abort a pending write,
727266757Snp	 * however, or we won't know how much was written.
728218792Snp	 */
729228561Snp	if (rcvd && rcvstate == READING)
730318854Snp		longjmp(rcvtop, 1);
731228561Snp}
732228561Snp
733266757Snp/* reader: read from remote: line -> 1 */
734266757Snpint
735228561Snpreader(omask)
736228561Snp	int omask;
737218792Snp{
738218792Snp	int pid, n, remaining;
739228561Snp	char *bufp;
740228561Snp
741218792Snp#if BSD >= 43 || defined(SUNOS4)
742218792Snp	pid = getpid();		/* modern systems use positives for pid */
743228561Snp#else
744228561Snp	pid = -getpid();	/* old broken systems use negatives */
745266757Snp#endif
746266757Snp	(void)signal(SIGTTOU, SIG_IGN);
747218792Snp	(void)signal(SIGURG, oob);
748306664Sjhb	ppid = getppid();
749306664Sjhb	(void)fcntl(rem, F_SETOWN, pid);
750306664Sjhb	(void)setjmp(rcvtop);
751306664Sjhb	(void)sigsetmask(omask);
752218792Snp	bufp = rcvbuf;
753218792Snp	for (;;) {
754255050Snp		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
755263317Snp			rcvstate = WRITING;
756263317Snp			n = write(STDOUT_FILENO, bufp, remaining);
757263317Snp			if (n < 0) {
758263317Snp				if (errno != EINTR)
759218792Snp					return (-1);
760218792Snp				continue;
761309560Sjhb			}
762309560Sjhb			bufp += n;
763309560Sjhb		}
764309560Sjhb		bufp = rcvbuf;
765309560Sjhb		rcvcnt = 0;
766309560Sjhb		rcvstate = READING;
767309560Sjhb
768309560Sjhb#ifdef CRYPT
769309560Sjhb#ifdef KERBEROS
770218792Snp		if (doencrypt)
771228561Snp			rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
772218792Snp		else
773218792Snp#endif
774309560Sjhb#endif
775218792Snp			rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
776218792Snp		if (rcvcnt == 0)
777218792Snp			return (0);
778218792Snp		if (rcvcnt < 0) {
779218792Snp			if (errno == EINTR)
780218792Snp				continue;
781218792Snp			(void)fprintf(stderr, "rlogin: read: %s.\n",
782218792Snp			    strerror(errno));
783218792Snp			return (-1);
784248925Snp		}
785248925Snp	}
786248925Snp}
787218792Snp
788218792Snpvoid
789218792Snpmode(f)
790296489Snp	int f;
791296489Snp{
792218792Snp	struct ltchars *ltc;
793218792Snp	struct sgttyb sb;
794218792Snp	struct tchars *tc;
795218792Snp	int lflags;
796218792Snp
797218792Snp	(void)ioctl(0, TIOCGETP, (char *)&sb);
798218792Snp	(void)ioctl(0, TIOCLGET, (char *)&lflags);
799218792Snp	switch(f) {
800302110Snp	case 0:
801302110Snp		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
802302110Snp		sb.sg_flags |= defflags|tabflag;
803306664Sjhb		tc = &deftc;
804306664Sjhb		ltc = &defltc;
805218792Snp		sb.sg_kill = defkill;
806218792Snp		sb.sg_erase = deferase;
807218792Snp		lflags = deflflags;
808218792Snp		break;
809255015Snp	case 1:
810302339Snp		sb.sg_flags |= (eight ? RAW : CBREAK);
811218792Snp		sb.sg_flags &= ~defflags;
812296383Snp		/* preserve tab delays, but turn off XTABS */
813218792Snp		if ((sb.sg_flags & TBDELAY) == XTABS)
814330307Snp			sb.sg_flags &= ~TBDELAY;
815218792Snp		tc = &notc;
816237263Snp		ltc = &noltc;
817228561Snp		sb.sg_kill = sb.sg_erase = -1;
818346805Snp		if (litout)
819346805Snp			lflags |= LLITOUT;
820346805Snp		break;
821346805Snp	default:
822331769Shselasky		return;
823292736Snp	}
824345040Sjhb	(void)ioctl(0, TIOCSLTC, (char *)ltc);
825222509Snp	(void)ioctl(0, TIOCSETC, (char *)tc);
826346855Snp	(void)ioctl(0, TIOCSETN, (char *)&sb);
827218792Snp	(void)ioctl(0, TIOCLSET, (char *)&lflags);
828345664Sjhb}
829218792Snp
830339396Snpvoid
831278374Snplostpeer(signo)
832278374Snp	int signo;
833218792Snp{
834284445Snp	(void)signal(SIGPIPE, SIG_IGN);
835218792Snp	msg("\007connection closed.");
836253691Snp	done(1);
837253691Snp}
838253691Snp
839253691Snp/* copy SIGURGs to the child process. */
840253691Snpvoid
841253691Snpcopytochild(signo)
842253691Snp	int signo;
843253691Snp{
844296641Snp	(void)kill(child, SIGURG);
845296641Snp}
846309458Sjhb
847309458Sjhbvoid
848245936Snpmsg(str)
849245936Snp	char *str;
850218792Snp{
851296383Snp	(void)fprintf(stderr, "rlogin: %s\r\n", str);
852218792Snp}
853218792Snp
854296710Snp#ifdef KERBEROS
855228561Snp/* VARARGS */
856296710Snpvoid
857228561Snp#if __STDC__
858228561Snpwarning(const char *fmt, ...)
859228561Snp#else
860309560Sjhbwarning(fmt, va_alist)
861228561Snp	char *fmt;
862228561Snp	va_dcl
863220873Snp#endif
864228561Snp{
865228561Snp	va_list ap;
866218792Snp
867218792Snp	(void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
868228561Snp#ifdef __STDC__
869228561Snp	va_start(ap, fmt);
870228561Snp#else
871228561Snp	va_start(ap);
872228561Snp#endif
873228561Snp	vfprintf(stderr, fmt, ap);
874296552Snp	va_end(ap);
875272200Snp	(void)fprintf(stderr, ".\n");
876296603Snp}
877296603Snp#endif
878318850Snp
879318850Snp__dead void
880318850Snpusage()
881245274Snp{
882245274Snp	(void)fprintf(stderr,
883286926Snp	    "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
884218792Snp#ifdef KERBEROS
885218792Snp#ifdef CRYPT
886218792Snp	    "8EKLx", " [-k realm] ");
887218792Snp#else
888218792Snp	    "8EKL", " [-k realm] ");
889218792Snp#endif
890218792Snp#else
891245274Snp	    "8EL", " ");
892245274Snp#endif
893245274Snp	exit(1);
894245274Snp}
895245274Snp
896218792Snp/*
897218792Snp * The following routine provides compatibility (such as it is) between older
898218792Snp * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
899218792Snp */
900218792Snp#ifdef OLDSUN
901218792Snpint
902218792Snpget_window_size(fd, wp)
903218792Snp	int fd;
904218792Snp	struct winsize *wp;
905218792Snp{
906218792Snp	struct ttysize ts;
907218792Snp	int error;
908218792Snp
909218792Snp	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
910218792Snp		return (error);
911218792Snp	wp->ws_row = ts.ts_lines;
912218792Snp	wp->ws_col = ts.ts_cols;
913218792Snp	wp->ws_xpixel = 0;
914218792Snp	wp->ws_ypixel = 0;
915218792Snp	return (0);
916218792Snp}
917218792Snp#endif
918218792Snp
919218792Snpu_int
920218792Snpgetescape(p)
921218792Snp	register char *p;
922218792Snp{
923218792Snp	long val;
924284445Snp	int len;
925284445Snp
926284445Snp	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
927284445Snp		return ((u_int)*p);
928284445Snp					/* otherwise, \nnn */
929284445Snp	if (*p == '\\' && len >= 2 && len <= 4) {
930284445Snp		val = strtol(++p, NULL, 8);
931284445Snp		for (;;) {
932284445Snp			if (!*++p)
933284445Snp				return ((u_int)val);
934284445Snp			if (*p < '0' || *p > '8')
935284445Snp				break;
936284445Snp		}
937284445Snp	}
938284445Snp	msg("illegal option value -- e");
939284445Snp	usage();
940284445Snp	/* NOTREACHED */
941284445Snp}
942291665Sjhb