Home | History | Annotate | Line # | Download | only in rlogin
rlogin.c revision 1.44.16.1
      1  1.44.16.1    martin /*	$NetBSD: rlogin.c,v 1.44.16.1 2020/04/13 08:05:46 martin Exp $	*/
      2        1.4       cgd 
      3        1.1       cgd /*
      4        1.4       cgd  * Copyright (c) 1983, 1990, 1993
      5        1.4       cgd  *	The Regents of the University of California.  All rights reserved.
      6        1.1       cgd  *
      7        1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8        1.1       cgd  * modification, are permitted provided that the following conditions
      9        1.1       cgd  * are met:
     10        1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11        1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12        1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13        1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14        1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15       1.29       agc  * 3. Neither the name of the University nor the names of its contributors
     16        1.1       cgd  *    may be used to endorse or promote products derived from this software
     17        1.1       cgd  *    without specific prior written permission.
     18        1.1       cgd  *
     19        1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20        1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21        1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22        1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23        1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24        1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25        1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26        1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27        1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28        1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29        1.1       cgd  * SUCH DAMAGE.
     30        1.1       cgd  */
     31        1.1       cgd 
     32       1.20     lukem #include <sys/cdefs.h>
     33        1.1       cgd #ifndef lint
     34       1.39     lukem __COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1993\
     35       1.39     lukem  The Regents of the University of California.  All rights reserved.");
     36        1.1       cgd #endif /* not lint */
     37        1.1       cgd 
     38        1.1       cgd #ifndef lint
     39        1.4       cgd #if 0
     40       1.15       tls static char sccsid[] = "@(#)rlogin.c	8.4 (Berkeley) 4/29/95";
     41        1.4       cgd #else
     42  1.44.16.1    martin __RCSID("$NetBSD: rlogin.c,v 1.44.16.1 2020/04/13 08:05:46 martin Exp $");
     43        1.4       cgd #endif
     44        1.1       cgd #endif /* not lint */
     45        1.1       cgd 
     46        1.1       cgd /*
     47        1.1       cgd  * rlogin - remote login
     48        1.1       cgd  */
     49        1.1       cgd #include <sys/param.h>
     50        1.5   mycroft #include <sys/ioctl.h>
     51        1.1       cgd #include <sys/socket.h>
     52        1.1       cgd #include <sys/time.h>
     53        1.1       cgd #include <sys/resource.h>
     54        1.1       cgd #include <sys/wait.h>
     55        1.1       cgd 
     56        1.1       cgd #include <netinet/in.h>
     57        1.1       cgd #include <netinet/in_systm.h>
     58        1.1       cgd #include <netinet/ip.h>
     59       1.42  christos #include <netinet/tcp.h>
     60        1.1       cgd 
     61       1.20     lukem #include <err.h>
     62        1.1       cgd #include <errno.h>
     63        1.4       cgd #include <fcntl.h>
     64        1.4       cgd #include <netdb.h>
     65        1.1       cgd #include <pwd.h>
     66        1.4       cgd #include <setjmp.h>
     67        1.4       cgd #include <signal.h>
     68       1.26       wiz #include <stdarg.h>
     69        1.1       cgd #include <stdio.h>
     70        1.4       cgd #include <stdlib.h>
     71        1.4       cgd #include <string.h>
     72       1.26       wiz #include <termios.h>
     73        1.1       cgd #include <unistd.h>
     74        1.4       cgd 
     75       1.31  christos #include "getport.h"
     76       1.31  christos 
     77        1.1       cgd #ifndef TIOCPKT_WINDOW
     78        1.1       cgd #define	TIOCPKT_WINDOW	0x80
     79        1.1       cgd #endif
     80        1.1       cgd 
     81        1.1       cgd /* concession to Sun */
     82        1.1       cgd #ifndef SIGUSR1
     83        1.1       cgd #define	SIGUSR1	30
     84        1.1       cgd #endif
     85        1.1       cgd 
     86        1.5   mycroft #ifndef CCEQ
     87        1.5   mycroft #define CCEQ(val, c)	(c == val ? val != _POSIX_VDISABLE : 0)
     88        1.5   mycroft #endif
     89        1.5   mycroft 
     90       1.41     joerg static int eight, rem;
     91       1.41     joerg static struct termios deftty;
     92        1.1       cgd 
     93       1.41     joerg static int noescape;
     94       1.41     joerg static u_char escapechar = '~';
     95        1.1       cgd 
     96        1.4       cgd #ifdef OLDSUN
     97        1.1       cgd struct winsize {
     98        1.1       cgd 	unsigned short ws_row, ws_col;
     99        1.1       cgd 	unsigned short ws_xpixel, ws_ypixel;
    100        1.1       cgd };
    101        1.4       cgd #else
    102        1.4       cgd #define	get_window_size(fd, wp)	ioctl(fd, TIOCGWINSZ, wp)
    103        1.1       cgd #endif
    104       1.41     joerg static struct	winsize winsize;
    105        1.1       cgd 
    106       1.41     joerg static void		catch_child(int);
    107       1.41     joerg static void		copytochild(int);
    108       1.41     joerg __dead static void	doit(sigset_t *);
    109       1.41     joerg __dead static void	done(int);
    110       1.41     joerg static void		echo(int);
    111       1.41     joerg static u_int		getescape(char *);
    112       1.41     joerg __dead static void	lostpeer(int);
    113       1.41     joerg static void		mode(int);
    114       1.41     joerg static void		msg(const char *);
    115       1.41     joerg static void		oob(int);
    116       1.41     joerg static int		reader(sigset_t *);
    117       1.41     joerg static void		sendwindow(void);
    118       1.41     joerg static void		setsignal(int);
    119       1.41     joerg static void		sigwinch(int);
    120       1.41     joerg static void		stop(int);
    121       1.41     joerg __dead static void	usage(void);
    122       1.41     joerg static void		writer(void);
    123       1.41     joerg static void		writeroob(int);
    124        1.4       cgd 
    125        1.4       cgd #ifdef OLDSUN
    126       1.41     joerg static int		get_window_size(int, struct winsize *);
    127        1.1       cgd #endif
    128        1.1       cgd 
    129        1.4       cgd int
    130       1.26       wiz main(int argc, char *argv[])
    131        1.1       cgd {
    132        1.1       cgd 	struct passwd *pw;
    133        1.1       cgd 	struct servent *sp;
    134        1.5   mycroft 	struct termios tty;
    135  1.44.16.1    martin 	sigset_t imask, omask;
    136       1.34  ginsbach 	uid_t uid;
    137       1.42  christos 	int argoff, ch, dflag, nflag, one;
    138       1.12       mrg 	int i, len, len2;
    139       1.35  ginsbach 	int family = AF_UNSPEC;
    140       1.18       mrg 	char *host, *p, *user, *name, term[1024] = "network";
    141       1.12       mrg 	speed_t ospeed;
    142       1.15       tls 	struct sigaction sa;
    143       1.31  christos 	char *service = NULL;
    144       1.17       mrg 	struct rlimit rlim;
    145        1.1       cgd 
    146       1.42  christos 	argoff = dflag = nflag = 0;
    147        1.1       cgd 	one = 1;
    148        1.1       cgd 	host = user = NULL;
    149       1.28   hubertf 	sp = NULL;
    150        1.1       cgd 
    151       1.25       cgd 	if (strcmp(getprogname(), "rlogin") != 0) {
    152       1.25       cgd 		host = strdup(getprogname());
    153       1.25       cgd 		if (host == NULL)
    154       1.25       cgd 			err(1, NULL);
    155       1.25       cgd 	}
    156        1.1       cgd 
    157        1.1       cgd 	/* handle "rlogin host flags" */
    158        1.1       cgd 	if (!host && argc > 2 && argv[1][0] != '-') {
    159        1.1       cgd 		host = argv[1];
    160        1.1       cgd 		argoff = 1;
    161        1.1       cgd 	}
    162        1.1       cgd 
    163       1.42  christos #define	OPTIONS	"468dEe:l:np:"
    164       1.20     lukem 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
    165        1.1       cgd 		switch(ch) {
    166       1.35  ginsbach 		case '4':
    167       1.35  ginsbach 			family = AF_INET;
    168       1.35  ginsbach 			break;
    169       1.35  ginsbach 		case '6':
    170       1.35  ginsbach 			family = AF_INET6;
    171       1.35  ginsbach 			break;
    172        1.1       cgd 		case '8':
    173        1.1       cgd 			eight = 1;
    174        1.1       cgd 			break;
    175       1.36       wiz 		case 'd':
    176       1.36       wiz 			dflag = 1;
    177       1.36       wiz 			break;
    178        1.1       cgd 		case 'E':
    179        1.1       cgd 			noescape = 1;
    180        1.1       cgd 			break;
    181        1.1       cgd 		case 'e':
    182        1.4       cgd 			noescape = 0;
    183        1.1       cgd 			escapechar = getescape(optarg);
    184        1.1       cgd 			break;
    185        1.1       cgd 		case 'l':
    186        1.1       cgd 			user = optarg;
    187        1.1       cgd 			break;
    188       1.42  christos 		case 'n':
    189       1.42  christos 			nflag = 1;
    190       1.42  christos 			break;
    191       1.28   hubertf 		case 'p':
    192       1.31  christos 			sp = getport(service = optarg, "tcp");
    193       1.28   hubertf 			break;
    194        1.1       cgd 		case '?':
    195        1.1       cgd 		default:
    196        1.1       cgd 			usage();
    197        1.1       cgd 		}
    198        1.1       cgd 	optind += argoff;
    199        1.1       cgd 	argc -= optind;
    200        1.1       cgd 	argv += optind;
    201        1.1       cgd 
    202        1.1       cgd 	/* if haven't gotten a host yet, do so */
    203        1.1       cgd 	if (!host && !(host = *argv++))
    204        1.1       cgd 		usage();
    205        1.1       cgd 
    206        1.1       cgd 	if (*argv)
    207        1.1       cgd 		usage();
    208        1.1       cgd 
    209       1.15       tls 	if (!(pw = getpwuid(uid = getuid())))
    210       1.15       tls 		errx(1, "unknown user id.");
    211       1.15       tls 	/* Accept user1@host format, though "-l user2" overrides user1 */
    212       1.15       tls 	p = strchr(host, '@');
    213       1.15       tls 	if (p) {
    214       1.15       tls 		*p = '\0';
    215       1.15       tls 		if (!user && p > host)
    216       1.15       tls 			user = host;
    217       1.15       tls 		host = p + 1;
    218       1.15       tls 		if (*host == '\0')
    219       1.15       tls 			usage();
    220        1.1       cgd 	}
    221       1.18       mrg 	if ((name = strdup(pw->pw_name)) == NULL)
    222       1.18       mrg 		err(1, "malloc");
    223        1.1       cgd 	if (!user)
    224       1.18       mrg 		user = name;
    225        1.1       cgd 
    226       1.21       mrg 	if (sp == NULL)
    227        1.1       cgd 		sp = getservbyname("login", "tcp");
    228       1.15       tls 	if (sp == NULL)
    229       1.15       tls 		errx(1, "login/tcp: unknown service.");
    230        1.1       cgd 
    231       1.27    itojun 	if ((p = getenv("TERM")) != NULL)
    232       1.27    itojun 		(void)strlcpy(term, p, sizeof(term));
    233       1.12       mrg 	len = strlen(term);
    234       1.40     lukem 	if (len < (int)(sizeof(term) - 1) && tcgetattr(0, &tty) == 0) {
    235       1.12       mrg 		/* start at 2 to include the / */
    236       1.13   thorpej 		for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++)
    237       1.12       mrg 			i /= 10;
    238       1.12       mrg 
    239       1.40     lukem 		if (len + len2 < (int)sizeof(term))
    240       1.14  explorer 			(void)snprintf(term + len, len2 + 1, "/%d", ospeed);
    241        1.1       cgd 	}
    242        1.1       cgd 
    243        1.1       cgd 	(void)get_window_size(0, &winsize);
    244        1.1       cgd 
    245       1.15       tls 	sigemptyset(&sa.sa_mask);
    246       1.15       tls 	sa.sa_flags = SA_RESTART;
    247       1.15       tls 	sa.sa_handler = lostpeer;
    248       1.21       mrg 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
    249        1.1       cgd 	/* will use SIGUSR1 for window size hack, so hold it off */
    250  1.44.16.1    martin 	sigemptyset(&imask);
    251  1.44.16.1    martin 	sigaddset(&imask, SIGURG);
    252  1.44.16.1    martin 	sigaddset(&imask, SIGUSR1);
    253  1.44.16.1    martin 	(void)sigprocmask(SIG_SETMASK, &imask, &omask);
    254        1.4       cgd 	/*
    255        1.4       cgd 	 * We set SIGURG and SIGUSR1 below so that an
    256        1.4       cgd 	 * incoming signal will be held pending rather than being
    257       1.21       mrg 	 * discarded. Note that these routines will be ready to get
    258       1.32  ginsbach 	 * a signal by the time that they are unblocked below.
    259        1.4       cgd 	 */
    260       1.15       tls 	sa.sa_handler = copytochild;
    261       1.15       tls 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
    262       1.15       tls 	sa.sa_handler = writeroob;
    263       1.15       tls 	(void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
    264       1.42  christos 
    265       1.17       mrg 	/* don't dump core */
    266       1.17       mrg 	rlim.rlim_cur = rlim.rlim_max = 0;
    267       1.17       mrg 	if (setrlimit(RLIMIT_CORE, &rlim) < 0)
    268       1.17       mrg 		warn("setrlimit");
    269        1.1       cgd 
    270       1.35  ginsbach 	rem = rcmd_af(&host, sp->s_port, name, user, term, 0, family);
    271        1.1       cgd 	if (rem < 0)
    272        1.1       cgd 		exit(1);
    273        1.1       cgd 
    274        1.1       cgd 	if (dflag &&
    275        1.1       cgd 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
    276       1.15       tls 		warn("setsockopt DEBUG (ignored)");
    277       1.42  christos 	if (nflag &&
    278       1.42  christos 	    setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
    279       1.42  christos 		warn("setsockopt NODELAY (ignored)");
    280       1.42  christos 
    281       1.42  christos 	{
    282       1.42  christos 		struct sockaddr_storage ss;
    283       1.42  christos 		socklen_t sslen = sizeof(ss);
    284       1.42  christos 		if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0
    285       1.42  christos 		     && ((struct sockaddr *)&ss)->sa_family == AF_INET) {
    286       1.42  christos 			one = IPTOS_LOWDELAY;
    287       1.42  christos 			if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
    288       1.42  christos 			    sizeof(int)) < 0)
    289       1.42  christos 				warn("setsockopt TOS (ignored)");
    290       1.24    itojun 		}
    291       1.24    itojun 	}
    292        1.1       cgd 
    293        1.1       cgd 	(void)setuid(uid);
    294  1.44.16.1    martin 	doit(&omask);
    295        1.1       cgd 	/*NOTREACHED*/
    296       1.20     lukem 	return (0);
    297        1.1       cgd }
    298        1.1       cgd 
    299       1.41     joerg static pid_t child;
    300        1.1       cgd 
    301       1.41     joerg static void
    302       1.26       wiz doit(sigset_t *smask)
    303        1.1       cgd {
    304       1.15       tls 	struct sigaction sa;
    305        1.1       cgd 
    306       1.15       tls 	sigemptyset(&sa.sa_mask);
    307       1.15       tls 	sa.sa_flags = SA_RESTART;
    308       1.15       tls 	sa.sa_handler = SIG_IGN;
    309       1.15       tls 	(void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
    310        1.4       cgd 	setsignal(SIGHUP);
    311        1.4       cgd 	setsignal(SIGQUIT);
    312        1.5   mycroft 	mode(1);
    313        1.1       cgd 	child = fork();
    314        1.1       cgd 	if (child == -1) {
    315       1.15       tls 		warn("fork");
    316        1.1       cgd 		done(1);
    317        1.1       cgd 	}
    318        1.1       cgd 	if (child == 0) {
    319       1.15       tls 		mode(1);
    320       1.15       tls 		if (reader(smask) == 0) {
    321        1.1       cgd 			msg("connection closed.");
    322        1.1       cgd 			exit(0);
    323        1.1       cgd 		}
    324        1.1       cgd 		sleep(1);
    325        1.5   mycroft 		msg("\aconnection closed.");
    326        1.1       cgd 		exit(1);
    327        1.1       cgd 	}
    328        1.1       cgd 
    329        1.1       cgd 	/*
    330        1.1       cgd 	 * We may still own the socket, and may have a pending SIGURG (or might
    331        1.4       cgd 	 * receive one soon) that we really want to send to the reader.  When
    332        1.4       cgd 	 * one of these comes in, the trap copytochild simply copies such
    333        1.4       cgd 	 * signals to the child. We can now unblock SIGURG and SIGUSR1
    334        1.4       cgd 	 * that were set above.
    335        1.1       cgd 	 */
    336       1.15       tls 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
    337       1.15       tls 	sa.sa_handler = catch_child;
    338       1.15       tls 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
    339        1.1       cgd 	writer();
    340        1.1       cgd 	msg("closed connection.");
    341        1.1       cgd 	done(0);
    342        1.1       cgd }
    343        1.1       cgd 
    344        1.1       cgd /* trap a signal, unless it is being ignored. */
    345       1.41     joerg static void
    346       1.26       wiz setsignal(int sig)
    347        1.1       cgd {
    348  1.44.16.1    martin 	struct sigaction isa, osa;
    349  1.44.16.1    martin 	sigset_t isigs, osigs;
    350       1.15       tls 
    351  1.44.16.1    martin 	sigemptyset(&isigs);
    352  1.44.16.1    martin 	sigaddset(&isigs, sig);
    353  1.44.16.1    martin 	sigprocmask(SIG_BLOCK, &isigs, &osigs);
    354  1.44.16.1    martin 
    355  1.44.16.1    martin 	sigemptyset(&isa.sa_mask);
    356  1.44.16.1    martin 	isa.sa_handler = exit;
    357  1.44.16.1    martin 	isa.sa_flags = SA_RESTART;
    358  1.44.16.1    martin 	(void)sigaction(sig, &isa, &osa);
    359  1.44.16.1    martin 	if (osa.sa_handler == SIG_IGN)
    360  1.44.16.1    martin 		(void)sigaction(sig, &osa, (struct sigaction *) 0);
    361       1.15       tls 
    362  1.44.16.1    martin 	(void)sigprocmask(SIG_SETMASK, &osigs, (sigset_t *) 0);
    363        1.1       cgd }
    364        1.1       cgd 
    365       1.41     joerg static void
    366       1.26       wiz done(int status)
    367        1.1       cgd {
    368       1.15       tls 	pid_t w;
    369       1.15       tls 	int wstatus;
    370       1.15       tls 	struct sigaction sa;
    371        1.1       cgd 
    372        1.1       cgd 	mode(0);
    373        1.1       cgd 	if (child > 0) {
    374        1.1       cgd 		/* make sure catch_child does not snap it up */
    375       1.15       tls 		sigemptyset(&sa.sa_mask);
    376       1.15       tls 		sa.sa_handler = SIG_DFL;
    377       1.15       tls 		sa.sa_flags = 0;
    378       1.15       tls 		(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
    379        1.1       cgd 		if (kill(child, SIGKILL) >= 0)
    380       1.15       tls 			while ((w = wait(&wstatus)) > 0 && w != child)
    381       1.15       tls 				continue;
    382        1.1       cgd 	}
    383        1.1       cgd 	exit(status);
    384        1.1       cgd }
    385        1.1       cgd 
    386       1.41     joerg static int dosigwinch;
    387        1.1       cgd 
    388        1.1       cgd /*
    389        1.1       cgd  * This is called when the reader process gets the out-of-band (urgent)
    390        1.1       cgd  * request to turn on the window-changing protocol.
    391        1.1       cgd  */
    392       1.41     joerg static void
    393       1.26       wiz writeroob(int signo)
    394        1.1       cgd {
    395       1.15       tls 	struct sigaction sa;
    396       1.15       tls 
    397        1.1       cgd 	if (dosigwinch == 0) {
    398        1.1       cgd 		sendwindow();
    399       1.15       tls 		sigemptyset(&sa.sa_mask);
    400       1.15       tls 		sa.sa_handler = sigwinch;
    401       1.15       tls 		sa.sa_flags = SA_RESTART;
    402       1.15       tls 		(void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
    403        1.1       cgd 	}
    404        1.1       cgd 	dosigwinch = 1;
    405        1.1       cgd }
    406        1.1       cgd 
    407       1.41     joerg static void
    408       1.26       wiz catch_child(int signo)
    409        1.1       cgd {
    410       1.15       tls 	int status;
    411       1.15       tls 	pid_t pid;
    412        1.1       cgd 
    413        1.1       cgd 	for (;;) {
    414       1.15       tls 		pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
    415        1.1       cgd 		if (pid == 0)
    416        1.1       cgd 			return;
    417        1.1       cgd 		/* if the child (reader) dies, just quit */
    418        1.4       cgd 		if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
    419       1.15       tls 			done(WEXITSTATUS(status) | WTERMSIG(status));
    420        1.1       cgd 	}
    421        1.1       cgd 	/* NOTREACHED */
    422        1.1       cgd }
    423        1.1       cgd 
    424        1.1       cgd /*
    425        1.1       cgd  * writer: write to remote: 0 -> line.
    426        1.1       cgd  * ~.				terminate
    427        1.1       cgd  * ~^Z				suspend rlogin process.
    428        1.1       cgd  * ~<delayed-suspend char>	suspend rlogin process, but leave reader alone.
    429        1.1       cgd  */
    430       1.41     joerg static void
    431       1.26       wiz writer(void)
    432        1.1       cgd {
    433       1.20     lukem 	int bol, local, n;
    434        1.1       cgd 	char c;
    435        1.1       cgd 
    436        1.1       cgd 	bol = 1;			/* beginning of line */
    437        1.1       cgd 	local = 0;
    438        1.1       cgd 	for (;;) {
    439        1.1       cgd 		n = read(STDIN_FILENO, &c, 1);
    440        1.1       cgd 		if (n <= 0) {
    441        1.1       cgd 			if (n < 0 && errno == EINTR)
    442        1.1       cgd 				continue;
    443        1.1       cgd 			break;
    444        1.1       cgd 		}
    445        1.1       cgd 		/*
    446        1.1       cgd 		 * If we're at the beginning of the line and recognize a
    447        1.1       cgd 		 * command character, then we echo locally.  Otherwise,
    448        1.1       cgd 		 * characters are echo'd remotely.  If the command character
    449        1.1       cgd 		 * is doubled, this acts as a force and local echo is
    450        1.1       cgd 		 * suppressed.
    451        1.1       cgd 		 */
    452        1.1       cgd 		if (bol) {
    453        1.1       cgd 			bol = 0;
    454        1.1       cgd 			if (!noescape && c == escapechar) {
    455        1.1       cgd 				local = 1;
    456        1.1       cgd 				continue;
    457        1.1       cgd 			}
    458        1.1       cgd 		} else if (local) {
    459        1.1       cgd 			local = 0;
    460        1.5   mycroft 			if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
    461       1.21       mrg 				echo((int)c);
    462        1.1       cgd 				break;
    463        1.1       cgd 			}
    464        1.6   mycroft 			if (CCEQ(deftty.c_cc[VSUSP], c)) {
    465        1.1       cgd 				bol = 1;
    466       1.21       mrg 				echo((int)c);
    467        1.6   mycroft 				stop(1);
    468        1.6   mycroft 				continue;
    469        1.6   mycroft 			}
    470        1.6   mycroft 			if (CCEQ(deftty.c_cc[VDSUSP], c)) {
    471        1.6   mycroft 				bol = 1;
    472       1.21       mrg 				echo((int)c);
    473        1.6   mycroft 				stop(0);
    474        1.1       cgd 				continue;
    475        1.1       cgd 			}
    476       1.23   thorpej 			if (c != escapechar) {
    477       1.42  christos 				(void)write(rem, &escapechar, 1);
    478       1.23   thorpej 			}
    479        1.1       cgd 		}
    480        1.1       cgd 
    481       1.42  christos 		if (write(rem, &c, 1) == 0) {
    482       1.42  christos 			msg("line gone");
    483       1.42  christos 			break;
    484       1.42  christos 		}
    485       1.19       tls 
    486        1.5   mycroft 		bol = CCEQ(deftty.c_cc[VKILL], c) ||
    487        1.5   mycroft 		    CCEQ(deftty.c_cc[VEOF], c) ||
    488        1.5   mycroft 		    CCEQ(deftty.c_cc[VINTR], c) ||
    489        1.5   mycroft 		    CCEQ(deftty.c_cc[VSUSP], c) ||
    490        1.1       cgd 		    c == '\r' || c == '\n';
    491        1.1       cgd 	}
    492        1.1       cgd }
    493        1.1       cgd 
    494       1.41     joerg static void
    495       1.26       wiz echo(int i)
    496        1.1       cgd {
    497       1.21       mrg 	char c = (char)i;
    498       1.20     lukem 	char *p;
    499        1.1       cgd 	char buf[8];
    500        1.1       cgd 
    501        1.1       cgd 	p = buf;
    502        1.1       cgd 	c &= 0177;
    503        1.1       cgd 	*p++ = escapechar;
    504        1.1       cgd 	if (c < ' ') {
    505        1.1       cgd 		*p++ = '^';
    506        1.1       cgd 		*p++ = c + '@';
    507        1.1       cgd 	} else if (c == 0177) {
    508        1.1       cgd 		*p++ = '^';
    509        1.1       cgd 		*p++ = '?';
    510        1.1       cgd 	} else
    511        1.1       cgd 		*p++ = c;
    512        1.1       cgd 	*p++ = '\r';
    513        1.1       cgd 	*p++ = '\n';
    514        1.1       cgd 	(void)write(STDOUT_FILENO, buf, p - buf);
    515        1.1       cgd }
    516        1.1       cgd 
    517       1.41     joerg static void
    518       1.26       wiz stop(int all)
    519        1.1       cgd {
    520       1.15       tls 	struct sigaction sa;
    521       1.15       tls 
    522        1.1       cgd 	mode(0);
    523       1.15       tls 	sigemptyset(&sa.sa_mask);
    524       1.15       tls 	sa.sa_handler = SIG_IGN;
    525       1.15       tls 	sa.sa_flags = SA_RESTART;
    526       1.15       tls 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
    527        1.6   mycroft 	(void)kill(all ? 0 : getpid(), SIGTSTP);
    528       1.15       tls 	sa.sa_handler = catch_child;
    529       1.15       tls 	(void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
    530        1.1       cgd 	mode(1);
    531        1.4       cgd 	sigwinch(0);			/* check for size changes */
    532        1.1       cgd }
    533        1.1       cgd 
    534       1.41     joerg static void
    535       1.26       wiz sigwinch(int signo)
    536        1.1       cgd {
    537        1.1       cgd 	struct winsize ws;
    538        1.1       cgd 
    539        1.1       cgd 	if (dosigwinch && get_window_size(0, &ws) == 0 &&
    540       1.15       tls 	    memcmp(&ws, &winsize, sizeof(ws))) {
    541        1.1       cgd 		winsize = ws;
    542        1.1       cgd 		sendwindow();
    543        1.1       cgd 	}
    544        1.1       cgd }
    545        1.1       cgd 
    546        1.1       cgd /*
    547        1.1       cgd  * Send the window size to the server via the magic escape
    548        1.1       cgd  */
    549       1.41     joerg static void
    550       1.26       wiz sendwindow(void)
    551        1.1       cgd {
    552        1.1       cgd 	struct winsize *wp;
    553        1.1       cgd 	char obuf[4 + sizeof (struct winsize)];
    554        1.1       cgd 
    555        1.1       cgd 	wp = (struct winsize *)(obuf+4);
    556        1.1       cgd 	obuf[0] = 0377;
    557        1.1       cgd 	obuf[1] = 0377;
    558        1.1       cgd 	obuf[2] = 's';
    559        1.1       cgd 	obuf[3] = 's';
    560        1.1       cgd 	wp->ws_row = htons(winsize.ws_row);
    561        1.1       cgd 	wp->ws_col = htons(winsize.ws_col);
    562        1.1       cgd 	wp->ws_xpixel = htons(winsize.ws_xpixel);
    563        1.1       cgd 	wp->ws_ypixel = htons(winsize.ws_ypixel);
    564        1.1       cgd 
    565       1.42  christos 	(void)write(rem, obuf, sizeof(obuf));
    566        1.1       cgd }
    567        1.1       cgd 
    568        1.1       cgd /*
    569        1.1       cgd  * reader: read from remote: line -> 1
    570        1.1       cgd  */
    571        1.1       cgd #define	READING	1
    572        1.1       cgd #define	WRITING	2
    573        1.1       cgd 
    574       1.41     joerg static jmp_buf rcvtop;
    575       1.41     joerg static pid_t ppid;
    576       1.41     joerg static int rcvcnt, rcvstate;
    577       1.41     joerg static char rcvbuf[8 * 1024];
    578        1.1       cgd 
    579       1.41     joerg static void
    580       1.26       wiz oob(int signo)
    581        1.1       cgd {
    582        1.5   mycroft 	struct termios tty;
    583        1.8   mycroft 	int atmark, n, rcvd;
    584        1.1       cgd 	char waste[BUFSIZ], mark;
    585        1.1       cgd 
    586        1.1       cgd 	rcvd = 0;
    587        1.4       cgd 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
    588        1.1       cgd 		switch (errno) {
    589        1.1       cgd 		case EWOULDBLOCK:
    590        1.1       cgd 			/*
    591        1.1       cgd 			 * Urgent data not here yet.  It may not be possible
    592        1.1       cgd 			 * to send it yet if we are blocked for output and
    593        1.1       cgd 			 * our input buffer is full.
    594        1.1       cgd 			 */
    595       1.40     lukem 			if (rcvcnt < (int)sizeof(rcvbuf)) {
    596        1.1       cgd 				n = read(rem, rcvbuf + rcvcnt,
    597        1.1       cgd 				    sizeof(rcvbuf) - rcvcnt);
    598        1.1       cgd 				if (n <= 0)
    599        1.1       cgd 					return;
    600        1.1       cgd 				rcvd += n;
    601        1.1       cgd 			} else {
    602        1.1       cgd 				n = read(rem, waste, sizeof(waste));
    603        1.1       cgd 				if (n <= 0)
    604        1.1       cgd 					return;
    605        1.1       cgd 			}
    606        1.1       cgd 			continue;
    607        1.1       cgd 		default:
    608        1.1       cgd 			return;
    609        1.4       cgd 		}
    610        1.1       cgd 	}
    611        1.1       cgd 	if (mark & TIOCPKT_WINDOW) {
    612        1.1       cgd 		/* Let server know about window size changes */
    613        1.1       cgd 		(void)kill(ppid, SIGUSR1);
    614        1.1       cgd 	}
    615        1.1       cgd 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
    616        1.5   mycroft 		(void)tcgetattr(0, &tty);
    617        1.5   mycroft 		tty.c_iflag &= ~IXON;
    618        1.5   mycroft 		(void)tcsetattr(0, TCSANOW, &tty);
    619        1.1       cgd 	}
    620        1.1       cgd 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
    621        1.5   mycroft 		(void)tcgetattr(0, &tty);
    622        1.5   mycroft 		tty.c_iflag |= (deftty.c_iflag & IXON);
    623        1.5   mycroft 		(void)tcsetattr(0, TCSANOW, &tty);
    624        1.1       cgd 	}
    625        1.1       cgd 	if (mark & TIOCPKT_FLUSHWRITE) {
    626        1.8   mycroft 		(void)tcflush(1, TCIOFLUSH);
    627        1.1       cgd 		for (;;) {
    628        1.1       cgd 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
    629       1.15       tls 				warn("ioctl SIOCATMARK (ignored)");
    630        1.1       cgd 				break;
    631        1.1       cgd 			}
    632        1.1       cgd 			if (atmark)
    633        1.1       cgd 				break;
    634        1.1       cgd 			n = read(rem, waste, sizeof (waste));
    635        1.1       cgd 			if (n <= 0)
    636        1.1       cgd 				break;
    637        1.1       cgd 		}
    638        1.1       cgd 		/*
    639        1.1       cgd 		 * Don't want any pending data to be output, so clear the recv
    640        1.1       cgd 		 * buffer.  If we were hanging on a write when interrupted,
    641        1.1       cgd 		 * don't want it to restart.  If we were reading, restart
    642        1.1       cgd 		 * anyway.
    643        1.1       cgd 		 */
    644        1.1       cgd 		rcvcnt = 0;
    645        1.1       cgd 		longjmp(rcvtop, 1);
    646        1.1       cgd 	}
    647        1.1       cgd 
    648        1.1       cgd 	/* oob does not do FLUSHREAD (alas!) */
    649        1.1       cgd 
    650        1.1       cgd 	/*
    651        1.1       cgd 	 * If we filled the receive buffer while a read was pending, longjmp
    652        1.1       cgd 	 * to the top to restart appropriately.  Don't abort a pending write,
    653        1.1       cgd 	 * however, or we won't know how much was written.
    654        1.1       cgd 	 */
    655        1.1       cgd 	if (rcvd && rcvstate == READING)
    656        1.1       cgd 		longjmp(rcvtop, 1);
    657        1.1       cgd }
    658        1.1       cgd 
    659        1.1       cgd /* reader: read from remote: line -> 1 */
    660       1.41     joerg static int
    661       1.26       wiz reader(sigset_t *smask)
    662        1.1       cgd {
    663       1.15       tls 	pid_t pid;
    664       1.15       tls 	int n, remaining;
    665        1.4       cgd 	char *bufp;
    666       1.15       tls 	struct sigaction sa;
    667        1.1       cgd 
    668        1.4       cgd 	pid = getpid();		/* modern systems use positives for pid */
    669       1.15       tls 	sigemptyset(&sa.sa_mask);
    670       1.15       tls 	sa.sa_flags = SA_RESTART;
    671       1.15       tls 	sa.sa_handler = SIG_IGN;
    672       1.15       tls 	(void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
    673       1.15       tls 	sa.sa_handler = oob;
    674       1.15       tls 	(void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
    675        1.1       cgd 	ppid = getppid();
    676        1.1       cgd 	(void)fcntl(rem, F_SETOWN, pid);
    677        1.1       cgd 	(void)setjmp(rcvtop);
    678       1.15       tls 	(void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
    679        1.4       cgd 	bufp = rcvbuf;
    680        1.1       cgd 	for (;;) {
    681        1.1       cgd 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
    682        1.1       cgd 			rcvstate = WRITING;
    683        1.1       cgd 			n = write(STDOUT_FILENO, bufp, remaining);
    684        1.1       cgd 			if (n < 0) {
    685        1.1       cgd 				if (errno != EINTR)
    686        1.4       cgd 					return (-1);
    687        1.1       cgd 				continue;
    688        1.1       cgd 			}
    689        1.1       cgd 			bufp += n;
    690        1.1       cgd 		}
    691        1.1       cgd 		bufp = rcvbuf;
    692        1.1       cgd 		rcvcnt = 0;
    693        1.1       cgd 		rcvstate = READING;
    694        1.1       cgd 
    695       1.42  christos 		rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
    696        1.1       cgd 		if (rcvcnt == 0)
    697        1.1       cgd 			return (0);
    698        1.1       cgd 		if (rcvcnt < 0) {
    699        1.1       cgd 			if (errno == EINTR)
    700        1.1       cgd 				continue;
    701       1.15       tls 			warn("read");
    702        1.4       cgd 			return (-1);
    703        1.1       cgd 		}
    704        1.1       cgd 	}
    705        1.1       cgd }
    706        1.1       cgd 
    707       1.41     joerg static void
    708       1.26       wiz mode(int f)
    709        1.1       cgd {
    710        1.5   mycroft 	struct termios tty;
    711        1.5   mycroft 
    712        1.5   mycroft 	switch (f) {
    713        1.1       cgd 	case 0:
    714        1.5   mycroft 		(void)tcsetattr(0, TCSANOW, &deftty);
    715        1.1       cgd 		break;
    716        1.1       cgd 	case 1:
    717        1.5   mycroft 		(void)tcgetattr(0, &deftty);
    718        1.5   mycroft 		tty = deftty;
    719        1.7   mycroft 		/* This is loosely derived from sys/compat/tty_compat.c. */
    720        1.7   mycroft 		tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
    721        1.5   mycroft 		tty.c_iflag &= ~ICRNL;
    722        1.5   mycroft 		tty.c_oflag &= ~OPOST;
    723        1.9  christos 		tty.c_cc[VMIN] = 1;
    724        1.9  christos 		tty.c_cc[VTIME] = 0;
    725        1.5   mycroft 		if (eight) {
    726        1.5   mycroft 			tty.c_iflag &= IXOFF;
    727        1.5   mycroft 			tty.c_cflag &= ~(CSIZE|PARENB);
    728        1.5   mycroft 			tty.c_cflag |= CS8;
    729        1.5   mycroft 		}
    730        1.5   mycroft 		(void)tcsetattr(0, TCSANOW, &tty);
    731        1.1       cgd 		break;
    732       1.15       tls 
    733        1.1       cgd 	default:
    734        1.1       cgd 		return;
    735        1.1       cgd 	}
    736        1.1       cgd }
    737        1.1       cgd 
    738       1.41     joerg static void
    739       1.26       wiz lostpeer(int signo)
    740        1.1       cgd {
    741       1.15       tls 	struct sigaction sa;
    742       1.15       tls 	sa.sa_flags = SA_RESTART;
    743       1.15       tls 	sa.sa_handler = SIG_IGN;
    744       1.44       shm 	sigemptyset(&sa.sa_mask);
    745       1.15       tls 	(void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
    746        1.5   mycroft 	msg("\aconnection closed.");
    747        1.1       cgd 	done(1);
    748        1.1       cgd }
    749        1.1       cgd 
    750        1.1       cgd /* copy SIGURGs to the child process. */
    751       1.41     joerg static void
    752       1.26       wiz copytochild(int signo)
    753        1.1       cgd {
    754       1.15       tls 
    755        1.1       cgd 	(void)kill(child, SIGURG);
    756        1.1       cgd }
    757        1.1       cgd 
    758       1.41     joerg static void
    759       1.31  christos msg(const char *str)
    760        1.1       cgd {
    761       1.15       tls 
    762        1.1       cgd 	(void)fprintf(stderr, "rlogin: %s\r\n", str);
    763        1.1       cgd }
    764        1.1       cgd 
    765       1.41     joerg static void
    766       1.26       wiz usage(void)
    767        1.1       cgd {
    768        1.1       cgd 	(void)fprintf(stderr,
    769       1.43       wiz 	    "Usage: %s [-468dEn] [-e char] [-l username] [-p port] "
    770       1.42  christos 	    "[username@]host\n", getprogname());
    771        1.1       cgd 	exit(1);
    772        1.1       cgd }
    773        1.1       cgd 
    774        1.1       cgd /*
    775        1.4       cgd  * The following routine provides compatibility (such as it is) between older
    776        1.1       cgd  * Suns and others.  Suns have only a `ttysize', so we convert it to a winsize.
    777        1.1       cgd  */
    778        1.4       cgd #ifdef OLDSUN
    779       1.41     joerg static int
    780       1.41     joerg get_window_size(int fd, struct winsize *wp)
    781        1.1       cgd {
    782        1.1       cgd 	struct ttysize ts;
    783        1.1       cgd 	int error;
    784        1.1       cgd 
    785        1.1       cgd 	if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
    786        1.4       cgd 		return (error);
    787        1.1       cgd 	wp->ws_row = ts.ts_lines;
    788        1.1       cgd 	wp->ws_col = ts.ts_cols;
    789        1.1       cgd 	wp->ws_xpixel = 0;
    790        1.1       cgd 	wp->ws_ypixel = 0;
    791        1.4       cgd 	return (0);
    792        1.1       cgd }
    793        1.1       cgd #endif
    794        1.1       cgd 
    795       1.41     joerg static u_int
    796       1.26       wiz getescape(char *p)
    797        1.1       cgd {
    798        1.1       cgd 	long val;
    799        1.1       cgd 	int len;
    800        1.1       cgd 
    801        1.1       cgd 	if ((len = strlen(p)) == 1)	/* use any single char, including '\' */
    802        1.4       cgd 		return ((u_int)*p);
    803        1.1       cgd 					/* otherwise, \nnn */
    804        1.1       cgd 	if (*p == '\\' && len >= 2 && len <= 4) {
    805        1.4       cgd 		val = strtol(++p, NULL, 8);
    806        1.1       cgd 		for (;;) {
    807        1.1       cgd 			if (!*++p)
    808        1.4       cgd 				return ((u_int)val);
    809        1.1       cgd 			if (*p < '0' || *p > '8')
    810        1.1       cgd 				break;
    811        1.1       cgd 		}
    812        1.1       cgd 	}
    813        1.1       cgd 	msg("illegal option value -- e");
    814        1.1       cgd 	usage();
    815        1.1       cgd 	/* NOTREACHED */
    816       1.20     lukem 	return (0);
    817        1.1       cgd }
    818