xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 26814)
121580Sdist /*
221580Sdist  * Copyright (c) 1983 Regents of the University of California.
321580Sdist  * All rights reserved.  The Berkeley software License Agreement
421580Sdist  * specifies the terms and conditions for redistribution.
521580Sdist  */
621580Sdist 
711758Ssam #ifndef lint
821580Sdist char copyright[] =
921580Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021580Sdist  All rights reserved.\n";
1121580Sdist #endif not lint
1211758Ssam 
1321580Sdist #ifndef lint
14*26814Skarels static char sccsid[] = "@(#)telnet.c	5.5 (Berkeley) 03/11/86";
1521580Sdist #endif not lint
1621580Sdist 
176000Sroot /*
186000Sroot  * User telnet program.
196000Sroot  */
209217Ssam #include <sys/types.h>
219217Ssam #include <sys/socket.h>
229972Ssam #include <sys/ioctl.h>
239217Ssam 
249217Ssam #include <netinet/in.h>
259217Ssam 
2612212Ssam #define	TELOPTS
2712212Ssam #include <arpa/telnet.h>
2812212Ssam 
296000Sroot #include <stdio.h>
306000Sroot #include <ctype.h>
316000Sroot #include <errno.h>
326000Sroot #include <signal.h>
336000Sroot #include <setjmp.h>
348345Ssam #include <netdb.h>
359217Ssam 
366000Sroot #define	strip(x)	((x)&0177)
376000Sroot 
386000Sroot char	ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
398378Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
406000Sroot 
416000Sroot char	hisopts[256];
426000Sroot char	myopts[256];
436000Sroot 
446000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
456000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
466000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
476000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
486000Sroot 
496000Sroot int	connected;
506000Sroot int	net;
519972Ssam int	showoptions = 0;
527377Sfeldman int	options;
5310339Ssam int	debug = 0;
549972Ssam int	crmod = 0;
5525289Skarels int	telnetport = 1;
566000Sroot char	*prompt;
579972Ssam char	escape = CTRL(]);
586000Sroot 
596000Sroot char	line[200];
606000Sroot int	margc;
616000Sroot char	*margv[20];
626000Sroot 
636000Sroot jmp_buf	toplevel;
646000Sroot jmp_buf	peerdied;
656000Sroot 
666000Sroot extern	int errno;
676000Sroot 
686000Sroot int	tn(), quit(), suspend(), bye(), help();
696024Ssam int	setescape(), status(), toggle(), setoptions();
7017922Sralph int	setcrmod(), setdebug(), sendesc(), ayt(), intp();
716000Sroot 
728378Ssam #define HELPINDENT (sizeof ("connect"))
736000Sroot 
746000Sroot struct cmd {
7511758Ssam 	char	*name;		/* command name */
7611758Ssam 	char	*help;		/* help string */
7711758Ssam 	int	(*handler)();	/* routine which executes command */
786000Sroot };
796000Sroot 
8011758Ssam char	openhelp[] =	"connect to a site";
8111758Ssam char	closehelp[] =	"close current connection";
8211758Ssam char	quithelp[] =	"exit telnet";
8311758Ssam char	zhelp[] =	"suspend telnet";
8411758Ssam char	debughelp[] =	"toggle debugging";
8511758Ssam char	escapehelp[] =	"set escape character";
8611758Ssam char	statushelp[] =	"print status information";
8711758Ssam char	helphelp[] =	"print help information";
8811758Ssam char	optionshelp[] =	"toggle viewing of options processing";
8911758Ssam char	crmodhelp[] =	"toggle mapping of received carriage returns";
9017922Sralph char	sendeschelp[] =	"send escape character";
9125289Skarels char	aythelp[] =	"send \"Are You There\"";
9225289Skarels char	intphelp[] =	"send \"Interrupt Process\"";
936000Sroot 
946000Sroot struct cmd cmdtab[] = {
9511758Ssam 	{ "open",	openhelp,	tn },
9611758Ssam 	{ "close",	closehelp,	bye },
9711758Ssam 	{ "quit",	quithelp,	quit },
986000Sroot 	{ "z",		zhelp,		suspend },
9911758Ssam 	{ "escape",	escapehelp,	setescape },
10011758Ssam 	{ "status",	statushelp,	status },
10111758Ssam 	{ "options",	optionshelp,	setoptions },
10211758Ssam 	{ "crmod",	crmodhelp,	setcrmod },
10311758Ssam 	{ "debug",	debughelp,	setdebug },
10417922Sralph 	{ "ayt",	aythelp,	ayt },
10517922Sralph 	{ "interrupt",	intphelp,	intp },
10617922Sralph 	{ "passthru",	sendeschelp,	sendesc },
10725289Skarels 	{ "help",	helphelp,	help },
10811758Ssam 	{ "?",		helphelp,	help },
1096000Sroot 	0
1106000Sroot };
1116000Sroot 
1129972Ssam struct sockaddr_in sin;
1136000Sroot 
1146000Sroot int	intr(), deadpeer();
1156000Sroot char	*control();
1166000Sroot struct	cmd *getcmd();
1178345Ssam struct	servent *sp;
1186000Sroot 
11913076Ssam struct	tchars otc;
12013076Ssam struct	ltchars oltc;
12113076Ssam struct	sgttyb ottyb;
1228378Ssam 
1236000Sroot main(argc, argv)
1246000Sroot 	int argc;
1256000Sroot 	char *argv[];
1266000Sroot {
1278345Ssam 	sp = getservbyname("telnet", "tcp");
1288345Ssam 	if (sp == 0) {
1298345Ssam 		fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
1308345Ssam 		exit(1);
1318345Ssam 	}
13213076Ssam 	ioctl(0, TIOCGETP, (char *)&ottyb);
13313076Ssam 	ioctl(0, TIOCGETC, (char *)&otc);
13413076Ssam 	ioctl(0, TIOCGLTC, (char *)&oltc);
1356000Sroot 	setbuf(stdin, 0);
1366000Sroot 	setbuf(stdout, 0);
1376000Sroot 	prompt = argv[0];
13817484Sleres 	if (argc > 1 && !strcmp(argv[1], "-d")) {
13917484Sleres 		debug = 1;
14017484Sleres 		argv++;
14117484Sleres 		argc--;
14217484Sleres 	}
1436000Sroot 	if (argc != 1) {
1446000Sroot 		if (setjmp(toplevel) != 0)
1456000Sroot 			exit(0);
1466000Sroot 		tn(argc, argv);
1476000Sroot 	}
1486000Sroot 	setjmp(toplevel);
1496000Sroot 	for (;;)
1506000Sroot 		command(1);
1516000Sroot }
1526000Sroot 
1538377Ssam char	*hostname;
1548377Ssam char	hnamebuf[32];
1556000Sroot 
1566000Sroot tn(argc, argv)
1576000Sroot 	int argc;
1586000Sroot 	char *argv[];
1596000Sroot {
1606000Sroot 	register int c;
16125425Skarels 	register struct hostent *host = 0;
1626000Sroot 
1636000Sroot 	if (connected) {
1648377Ssam 		printf("?Already connected to %s\n", hostname);
1656000Sroot 		return;
1666000Sroot 	}
1676000Sroot 	if (argc < 2) {
1686000Sroot 		strcpy(line, "Connect ");
1696000Sroot 		printf("(to) ");
1706000Sroot 		gets(&line[strlen(line)]);
1716000Sroot 		makeargv();
1726000Sroot 		argc = margc;
1736000Sroot 		argv = margv;
1746000Sroot 	}
1756000Sroot 	if (argc > 3) {
1766000Sroot 		printf("usage: %s host-name [port]\n", argv[0]);
1776000Sroot 		return;
1786000Sroot 	}
17925425Skarels 	sin.sin_addr.s_addr = inet_addr(argv[1]);
18025425Skarels 	if (sin.sin_addr.s_addr != -1) {
18125425Skarels 		sin.sin_family = AF_INET;
18225425Skarels 		strcpy(hnamebuf, argv[1]);
18325425Skarels 		hostname = hnamebuf;
1848377Ssam 	} else {
18525425Skarels 		host = gethostbyname(argv[1]);
18625425Skarels 		if (host) {
18725425Skarels 			sin.sin_family = host->h_addrtype;
18825425Skarels 			bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
18925425Skarels 				host->h_length);
19025425Skarels 			hostname = host->h_name;
19125425Skarels 		} else {
1928377Ssam 			printf("%s: unknown host\n", argv[1]);
1938377Ssam 			return;
1948377Ssam 		}
1956000Sroot 	}
1968345Ssam 	sin.sin_port = sp->s_port;
1976024Ssam 	if (argc == 3) {
1986024Ssam 		sin.sin_port = atoi(argv[2]);
19925289Skarels 		if (sin.sin_port <= 0) {
20025289Skarels 			sp = getservbyname(argv[2], "tcp");
20125289Skarels 			if (sp)
20225289Skarels 				sin.sin_port = sp->s_port;
20325289Skarels 			else {
20425289Skarels 				printf("%s: bad port number\n", argv[2]);
20525289Skarels 				return;
20625289Skarels 			}
20725289Skarels 		} else {
20825289Skarels 			sin.sin_port = atoi(argv[2]);
20925289Skarels 			sin.sin_port = htons(sin.sin_port);
2106024Ssam 		}
21125289Skarels 		telnetport = 0;
2126024Ssam 	}
21317922Sralph 	net = socket(AF_INET, SOCK_STREAM, 0);
2149217Ssam 	if (net < 0) {
2159217Ssam 		perror("telnet: socket");
2166000Sroot 		return;
2176000Sroot 	}
21817484Sleres 	if (debug &&
21917484Sleres 	    setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0)
22011758Ssam 		perror("setsockopt (SO_DEBUG)");
22112989Ssam 	signal(SIGINT, intr);
22212989Ssam 	signal(SIGPIPE, deadpeer);
2236000Sroot 	printf("Trying...\n");
22425425Skarels 	while (connect(net, (caddr_t)&sin, sizeof (sin)) < 0) {
22525425Skarels 		if (host && host->h_addr_list[1]) {
22625425Skarels 			int oerrno = errno;
22725425Skarels 
22825425Skarels 			fprintf(stderr, "telnet: connect to address %s: ",
22925425Skarels 				inet_ntoa(sin.sin_addr));
23025425Skarels 			errno = oerrno;
23125425Skarels 			perror(0);
23225425Skarels 			host->h_addr_list++;
23325425Skarels 			bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
23425425Skarels 				host->h_length);
23525425Skarels 			fprintf(stderr, "Trying %s...\n",
23625425Skarels 				inet_ntoa(sin.sin_addr));
237*26814Skarels 			(void) close(net);
238*26814Skarels 			net = socket(AF_INET, SOCK_STREAM, 0);
239*26814Skarels 			if (net < 0) {
240*26814Skarels 				perror("telnet: socket");
241*26814Skarels 				return;
242*26814Skarels 			}
24325425Skarels 			continue;
24425425Skarels 		}
2459217Ssam 		perror("telnet: connect");
24612989Ssam 		signal(SIGINT, SIG_DFL);
2476000Sroot 		return;
2486000Sroot 	}
2496000Sroot 	connected++;
2506000Sroot 	call(status, "status", 0);
2516000Sroot 	if (setjmp(peerdied) == 0)
2526000Sroot 		telnet(net);
2536000Sroot 	fprintf(stderr, "Connection closed by foreign host.\n");
2546000Sroot 	exit(1);
2556000Sroot }
2566000Sroot 
2576000Sroot /*
2586000Sroot  * Print status about the connection.
2596000Sroot  */
2606000Sroot /*VARARGS*/
2616000Sroot status()
2626000Sroot {
2636000Sroot 	if (connected)
2648377Ssam 		printf("Connected to %s.\n", hostname);
2656000Sroot 	else
2666000Sroot 		printf("No connection.\n");
2676000Sroot 	printf("Escape character is '%s'.\n", control(escape));
2689972Ssam 	fflush(stdout);
2696000Sroot }
2706000Sroot 
2716000Sroot makeargv()
2726000Sroot {
2736000Sroot 	register char *cp;
2746000Sroot 	register char **argp = margv;
2756000Sroot 
2766000Sroot 	margc = 0;
2776000Sroot 	for (cp = line; *cp;) {
2786000Sroot 		while (isspace(*cp))
2796000Sroot 			cp++;
2806000Sroot 		if (*cp == '\0')
2816000Sroot 			break;
2826000Sroot 		*argp++ = cp;
2836000Sroot 		margc += 1;
2846000Sroot 		while (*cp != '\0' && !isspace(*cp))
2856000Sroot 			cp++;
2866000Sroot 		if (*cp == '\0')
2876000Sroot 			break;
2886000Sroot 		*cp++ = '\0';
2896000Sroot 	}
2906000Sroot 	*argp++ = 0;
2916000Sroot }
2926000Sroot 
2936000Sroot /*VARARGS*/
2946000Sroot suspend()
2956000Sroot {
2966000Sroot 	register int save;
2976000Sroot 
2986000Sroot 	save = mode(0);
2998378Ssam 	kill(0, SIGTSTP);
3008378Ssam 	/* reget parameters in case they were changed */
30113076Ssam 	ioctl(0, TIOCGETP, (char *)&ottyb);
30213076Ssam 	ioctl(0, TIOCGETC, (char *)&otc);
30313076Ssam 	ioctl(0, TIOCGLTC, (char *)&oltc);
3048378Ssam 	(void) mode(save);
3056000Sroot }
3066000Sroot 
3076000Sroot /*VARARGS*/
3086000Sroot bye()
3096000Sroot {
3108378Ssam 	register char *op;
3116000Sroot 
3128378Ssam 	(void) mode(0);
3136000Sroot 	if (connected) {
31411758Ssam 		shutdown(net, 2);
3156000Sroot 		printf("Connection closed.\n");
3166000Sroot 		close(net);
3176000Sroot 		connected = 0;
3188378Ssam 		/* reset his options */
3198378Ssam 		for (op = hisopts; op < &hisopts[256]; op++)
3208378Ssam 			*op = 0;
3216000Sroot 	}
3226000Sroot }
3236000Sroot 
3246000Sroot /*VARARGS*/
3256000Sroot quit()
3266000Sroot {
3276000Sroot 	call(bye, "bye", 0);
3286000Sroot 	exit(0);
3296000Sroot }
3306000Sroot 
3316000Sroot /*
3326000Sroot  * Help command.
3336000Sroot  */
3346000Sroot help(argc, argv)
3356000Sroot 	int argc;
3366000Sroot 	char *argv[];
3376000Sroot {
3386000Sroot 	register struct cmd *c;
3396000Sroot 
3406000Sroot 	if (argc == 1) {
3416000Sroot 		printf("Commands may be abbreviated.  Commands are:\n\n");
3426000Sroot 		for (c = cmdtab; c->name; c++)
3436000Sroot 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
3446000Sroot 		return;
3456000Sroot 	}
3466000Sroot 	while (--argc > 0) {
3476000Sroot 		register char *arg;
3486000Sroot 		arg = *++argv;
3496000Sroot 		c = getcmd(arg);
3506000Sroot 		if (c == (struct cmd *)-1)
3516000Sroot 			printf("?Ambiguous help command %s\n", arg);
3526000Sroot 		else if (c == (struct cmd *)0)
3536000Sroot 			printf("?Invalid help command %s\n", arg);
3546000Sroot 		else
3556000Sroot 			printf("%s\n", c->help);
3566000Sroot 	}
3576000Sroot }
3586000Sroot 
3596000Sroot /*
3606000Sroot  * Call routine with argc, argv set from args (terminated by 0).
3616000Sroot  * VARARGS2
3626000Sroot  */
3636000Sroot call(routine, args)
3646000Sroot 	int (*routine)();
3656000Sroot 	int args;
3666000Sroot {
3676000Sroot 	register int *argp;
3686000Sroot 	register int argc;
3696000Sroot 
3706000Sroot 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
3716000Sroot 		;
3726000Sroot 	(*routine)(argc, &args);
3736000Sroot }
3746000Sroot 
37513076Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
37613076Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
3779972Ssam 
3786000Sroot mode(f)
3796000Sroot 	register int f;
3806000Sroot {
3818378Ssam 	static int prevmode = 0;
38213076Ssam 	struct tchars *tc;
38313076Ssam 	struct ltchars *ltc;
38413076Ssam 	struct sgttyb sb;
38513076Ssam 	int onoff, old;
3866000Sroot 
3878378Ssam 	if (prevmode == f)
3888378Ssam 		return (f);
3898378Ssam 	old = prevmode;
3908378Ssam 	prevmode = f;
39113076Ssam 	sb = ottyb;
3926000Sroot 	switch (f) {
3938378Ssam 
3946000Sroot 	case 0:
3956000Sroot 		onoff = 0;
3969972Ssam 		tc = &otc;
39713076Ssam 		ltc = &oltc;
3986000Sroot 		break;
3996000Sroot 
4006000Sroot 	case 1:
4016000Sroot 	case 2:
40213076Ssam 		sb.sg_flags |= CBREAK;
4038378Ssam 		if (f == 1)
40413076Ssam 			sb.sg_flags &= ~(ECHO|CRMOD);
4058378Ssam 		else
40613076Ssam 			sb.sg_flags |= ECHO|CRMOD;
40713076Ssam 		sb.sg_erase = sb.sg_kill = -1;
4089972Ssam 		tc = &notc;
40913076Ssam 		ltc = &noltc;
4106000Sroot 		onoff = 1;
4119972Ssam 		break;
4129972Ssam 
4139972Ssam 	default:
4149972Ssam 		return;
4156000Sroot 	}
41613076Ssam 	ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
41713076Ssam 	ioctl(fileno(stdin), TIOCSETC, (char *)tc);
41813076Ssam 	ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
4196000Sroot 	ioctl(fileno(stdin), FIONBIO, &onoff);
4206000Sroot 	ioctl(fileno(stdout), FIONBIO, &onoff);
4216000Sroot 	return (old);
4226000Sroot }
4236000Sroot 
4246000Sroot char	sibuf[BUFSIZ], *sbp;
4256000Sroot char	tibuf[BUFSIZ], *tbp;
4266000Sroot int	scc, tcc;
4276000Sroot 
4286000Sroot /*
4296000Sroot  * Select from tty and network...
4306000Sroot  */
4316000Sroot telnet(s)
4326000Sroot 	int s;
4336000Sroot {
4346000Sroot 	register int c;
4356000Sroot 	int tin = fileno(stdin), tout = fileno(stdout);
4366000Sroot 	int on = 1;
4376000Sroot 
4388378Ssam 	(void) mode(2);
4396000Sroot 	ioctl(s, FIONBIO, &on);
44025289Skarels 	if (telnetport && !hisopts[TELOPT_SGA])
44117922Sralph 		willoption(TELOPT_SGA);
4426000Sroot 	for (;;) {
4436000Sroot 		int ibits = 0, obits = 0;
4446000Sroot 
4456000Sroot 		if (nfrontp - nbackp)
4466000Sroot 			obits |= (1 << s);
4476000Sroot 		else
4486000Sroot 			ibits |= (1 << tin);
4496000Sroot 		if (tfrontp - tbackp)
4506000Sroot 			obits |= (1 << tout);
4516000Sroot 		else
4526000Sroot 			ibits |= (1 << s);
4536000Sroot 		if (scc < 0 && tcc < 0)
4546000Sroot 			break;
4559217Ssam 		select(16, &ibits, &obits, 0, 0);
4566000Sroot 		if (ibits == 0 && obits == 0) {
4576000Sroot 			sleep(5);
4586000Sroot 			continue;
4596000Sroot 		}
4606000Sroot 
4616000Sroot 		/*
4626000Sroot 		 * Something to read from the network...
4636000Sroot 		 */
4646000Sroot 		if (ibits & (1 << s)) {
4658378Ssam 			scc = read(s, sibuf, sizeof (sibuf));
4666000Sroot 			if (scc < 0 && errno == EWOULDBLOCK)
4676000Sroot 				scc = 0;
4686000Sroot 			else {
4696000Sroot 				if (scc <= 0)
4706000Sroot 					break;
4716000Sroot 				sbp = sibuf;
4726000Sroot 			}
4736000Sroot 		}
4746000Sroot 
4756000Sroot 		/*
4766000Sroot 		 * Something to read from the tty...
4776000Sroot 		 */
4786000Sroot 		if (ibits & (1 << tin)) {
4798378Ssam 			tcc = read(tin, tibuf, sizeof (tibuf));
4806000Sroot 			if (tcc < 0 && errno == EWOULDBLOCK)
4816000Sroot 				tcc = 0;
4826000Sroot 			else {
4836000Sroot 				if (tcc <= 0)
4846000Sroot 					break;
4856000Sroot 				tbp = tibuf;
4866000Sroot 			}
4876000Sroot 		}
4886000Sroot 
4896000Sroot 		while (tcc > 0) {
4906000Sroot 			register int c;
4916000Sroot 
4926000Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
4936000Sroot 				break;
4946000Sroot 			c = *tbp++ & 0377, tcc--;
4956000Sroot 			if (strip(c) == escape) {
4966000Sroot 				command(0);
4976000Sroot 				tcc = 0;
4986000Sroot 				break;
4996000Sroot 			}
50017922Sralph 			switch (c) {
50117922Sralph 			case '\n':
50217922Sralph 				if (!hisopts[TELOPT_ECHO])
50317922Sralph 					*nfrontp++ = '\r';
50417922Sralph 				*nfrontp++ = '\n';
50517922Sralph 				break;
50617922Sralph 			case '\r':
50717922Sralph 				*nfrontp++ = '\r';
50817922Sralph 				if (hisopts[TELOPT_ECHO])
50917922Sralph 					*nfrontp++ = '\n';
51017922Sralph 				else
51117922Sralph 					*nfrontp++ = '\0';
51217922Sralph 				break;
51317922Sralph 			case IAC:
51417922Sralph 				*nfrontp++ = IAC;
51517922Sralph 				/* fall into ... */
51617922Sralph 			default:
51711758Ssam 				*nfrontp++ = c;
51817922Sralph 				break;
51917922Sralph 			}
5206000Sroot 		}
5216000Sroot 		if ((obits & (1 << s)) && (nfrontp - nbackp) > 0)
5226000Sroot 			netflush(s);
5236000Sroot 		if (scc > 0)
5246000Sroot 			telrcv();
5256000Sroot 		if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0)
5266000Sroot 			ttyflush(tout);
5276000Sroot 	}
5288378Ssam 	(void) mode(0);
5296000Sroot }
5306000Sroot 
5316000Sroot command(top)
5326000Sroot 	int top;
5336000Sroot {
5346000Sroot 	register struct cmd *c;
5356000Sroot 	int oldmode, wasopen;
5366000Sroot 
5376000Sroot 	oldmode = mode(0);
5386000Sroot 	if (!top)
5396000Sroot 		putchar('\n');
5406000Sroot 	else
54112989Ssam 		signal(SIGINT, SIG_DFL);
5426000Sroot 	for (;;) {
5436000Sroot 		printf("%s> ", prompt);
54413997Ssam 		if (gets(line) == 0) {
54525800Slepreau 			if (feof(stdin))
54625800Slepreau 				quit();
5476000Sroot 			break;
54813997Ssam 		}
5496000Sroot 		if (line[0] == 0)
5506000Sroot 			break;
5516000Sroot 		makeargv();
5526000Sroot 		c = getcmd(margv[0]);
5536000Sroot 		if (c == (struct cmd *)-1) {
5546000Sroot 			printf("?Ambiguous command\n");
5556000Sroot 			continue;
5566000Sroot 		}
5576000Sroot 		if (c == 0) {
5586000Sroot 			printf("?Invalid command\n");
5596000Sroot 			continue;
5606000Sroot 		}
5616000Sroot 		(*c->handler)(margc, margv);
5626000Sroot 		if (c->handler != help)
5636000Sroot 			break;
5646000Sroot 	}
5656000Sroot 	if (!top) {
5666000Sroot 		if (!connected)
5676000Sroot 			longjmp(toplevel, 1);
5688378Ssam 		(void) mode(oldmode);
5696000Sroot 	}
5706000Sroot }
5716000Sroot 
5726000Sroot /*
5736000Sroot  * Telnet receiver states for fsm
5746000Sroot  */
5756000Sroot #define	TS_DATA		0
5766000Sroot #define	TS_IAC		1
5776000Sroot #define	TS_WILL		2
5786000Sroot #define	TS_WONT		3
5796000Sroot #define	TS_DO		4
5806000Sroot #define	TS_DONT		5
5816000Sroot 
5826000Sroot telrcv()
5836000Sroot {
5846000Sroot 	register int c;
5856000Sroot 	static int state = TS_DATA;
5866000Sroot 
5876000Sroot 	while (scc > 0) {
5886000Sroot 		c = *sbp++ & 0377, scc--;
5896000Sroot 		switch (state) {
5906000Sroot 
5916000Sroot 		case TS_DATA:
5929972Ssam 			if (c == IAC) {
5936000Sroot 				state = TS_IAC;
5949972Ssam 				continue;
5959972Ssam 			}
5969972Ssam 			*tfrontp++ = c;
5979972Ssam 			/*
5989972Ssam 			 * This hack is needed since we can't set
5999972Ssam 			 * CRMOD on output only.  Machines like MULTICS
6009972Ssam 			 * like to send \r without \n; since we must
6019972Ssam 			 * turn off CRMOD to get proper input, the mapping
6029972Ssam 			 * is done here (sigh).
6039972Ssam 			 */
6049972Ssam 			if (c == '\r' && crmod)
6059972Ssam 				*tfrontp++ = '\n';
6066000Sroot 			continue;
6076000Sroot 
6086000Sroot 		case TS_IAC:
6096000Sroot 			switch (c) {
6106000Sroot 
6116000Sroot 			case WILL:
6126000Sroot 				state = TS_WILL;
6136000Sroot 				continue;
6146000Sroot 
6156000Sroot 			case WONT:
6166000Sroot 				state = TS_WONT;
6176000Sroot 				continue;
6186000Sroot 
6196000Sroot 			case DO:
6206000Sroot 				state = TS_DO;
6216000Sroot 				continue;
6226000Sroot 
6236000Sroot 			case DONT:
6246000Sroot 				state = TS_DONT;
6256000Sroot 				continue;
6266000Sroot 
6276000Sroot 			case DM:
6286000Sroot 				ioctl(fileno(stdout), TIOCFLUSH, 0);
6296000Sroot 				break;
6306000Sroot 
6316000Sroot 			case NOP:
6326000Sroot 			case GA:
6336000Sroot 				break;
6346000Sroot 
6356000Sroot 			default:
6366000Sroot 				break;
6376000Sroot 			}
6386000Sroot 			state = TS_DATA;
6396000Sroot 			continue;
6406000Sroot 
6416000Sroot 		case TS_WILL:
6428345Ssam 			printoption("RCVD", will, c, !hisopts[c]);
6436000Sroot 			if (!hisopts[c])
6446000Sroot 				willoption(c);
6456000Sroot 			state = TS_DATA;
6466000Sroot 			continue;
6476000Sroot 
6486000Sroot 		case TS_WONT:
6498345Ssam 			printoption("RCVD", wont, c, hisopts[c]);
6506000Sroot 			if (hisopts[c])
6516000Sroot 				wontoption(c);
6526000Sroot 			state = TS_DATA;
6536000Sroot 			continue;
6546000Sroot 
6556000Sroot 		case TS_DO:
6568345Ssam 			printoption("RCVD", doopt, c, !myopts[c]);
6576000Sroot 			if (!myopts[c])
6586000Sroot 				dooption(c);
6596000Sroot 			state = TS_DATA;
6606000Sroot 			continue;
6616000Sroot 
6626000Sroot 		case TS_DONT:
6638345Ssam 			printoption("RCVD", dont, c, myopts[c]);
6646000Sroot 			if (myopts[c]) {
6656000Sroot 				myopts[c] = 0;
6666000Sroot 				sprintf(nfrontp, wont, c);
6678378Ssam 				nfrontp += sizeof (wont) - 2;
6688345Ssam 				printoption("SENT", wont, c);
6696000Sroot 			}
6706000Sroot 			state = TS_DATA;
6716000Sroot 			continue;
6726000Sroot 		}
6736000Sroot 	}
6746000Sroot }
6756000Sroot 
6766000Sroot willoption(option)
6776000Sroot 	int option;
6786000Sroot {
6796000Sroot 	char *fmt;
6806000Sroot 
6816000Sroot 	switch (option) {
6826000Sroot 
6836000Sroot 	case TELOPT_ECHO:
6848378Ssam 		(void) mode(1);
6856000Sroot 
6866000Sroot 	case TELOPT_SGA:
6876000Sroot 		hisopts[option] = 1;
6886000Sroot 		fmt = doopt;
6896000Sroot 		break;
6906000Sroot 
6916000Sroot 	case TELOPT_TM:
6926000Sroot 		fmt = dont;
6936000Sroot 		break;
6946000Sroot 
6956000Sroot 	default:
6966000Sroot 		fmt = dont;
6976000Sroot 		break;
6986000Sroot 	}
6996024Ssam 	sprintf(nfrontp, fmt, option);
7008378Ssam 	nfrontp += sizeof (dont) - 2;
7018345Ssam 	printoption("SENT", fmt, option);
7026000Sroot }
7036000Sroot 
7046000Sroot wontoption(option)
7056000Sroot 	int option;
7066000Sroot {
7076000Sroot 	char *fmt;
7086000Sroot 
7096000Sroot 	switch (option) {
7106000Sroot 
7116000Sroot 	case TELOPT_ECHO:
7128378Ssam 		(void) mode(2);
7136000Sroot 
7146000Sroot 	case TELOPT_SGA:
7156000Sroot 		hisopts[option] = 0;
7166000Sroot 		fmt = dont;
7176000Sroot 		break;
7186000Sroot 
7196000Sroot 	default:
7206000Sroot 		fmt = dont;
7216000Sroot 	}
7226000Sroot 	sprintf(nfrontp, fmt, option);
7238378Ssam 	nfrontp += sizeof (doopt) - 2;
7248345Ssam 	printoption("SENT", fmt, option);
7256000Sroot }
7266000Sroot 
7276000Sroot dooption(option)
7286000Sroot 	int option;
7296000Sroot {
7306000Sroot 	char *fmt;
7316000Sroot 
7326000Sroot 	switch (option) {
7336000Sroot 
7346000Sroot 	case TELOPT_TM:
7356000Sroot 		fmt = wont;
7366000Sroot 		break;
7376000Sroot 
73813231Ssam 	case TELOPT_ECHO:
73913231Ssam 		(void) mode(2);
74013231Ssam 		fmt = will;
74113231Ssam 		hisopts[option] = 0;
74213231Ssam 		break;
74313231Ssam 
7446000Sroot 	case TELOPT_SGA:
7456000Sroot 		fmt = will;
7466000Sroot 		break;
7476000Sroot 
7486000Sroot 	default:
7496000Sroot 		fmt = wont;
7506000Sroot 		break;
7516000Sroot 	}
7526000Sroot 	sprintf(nfrontp, fmt, option);
7538378Ssam 	nfrontp += sizeof (doopt) - 2;
7548345Ssam 	printoption("SENT", fmt, option);
7556000Sroot }
7566000Sroot 
7576000Sroot /*
7586000Sroot  * Set the escape character.
7596000Sroot  */
7606000Sroot setescape(argc, argv)
7616000Sroot 	int argc;
7626000Sroot 	char *argv[];
7636000Sroot {
7646000Sroot 	register char *arg;
7656000Sroot 	char buf[50];
7666000Sroot 
7676000Sroot 	if (argc > 2)
7686000Sroot 		arg = argv[1];
7696000Sroot 	else {
7706000Sroot 		printf("new escape character: ");
7716000Sroot 		gets(buf);
7726000Sroot 		arg = buf;
7736000Sroot 	}
7746000Sroot 	if (arg[0] != '\0')
7756000Sroot 		escape = arg[0];
7766000Sroot 	printf("Escape character is '%s'.\n", control(escape));
7779972Ssam 	fflush(stdout);
7786000Sroot }
7796000Sroot 
7806024Ssam /*VARARGS*/
7816024Ssam setoptions()
7826024Ssam {
7839972Ssam 
7846024Ssam 	showoptions = !showoptions;
78525289Skarels 	printf("%s show option processing.\n", showoptions ? "Will" : "Won't");
7869972Ssam 	fflush(stdout);
7876024Ssam }
7886024Ssam 
7899972Ssam /*VARARGS*/
7909972Ssam setcrmod()
7919972Ssam {
7929972Ssam 
7939972Ssam 	crmod = !crmod;
79425289Skarels 	printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
7959972Ssam 	fflush(stdout);
7969972Ssam }
7979972Ssam 
79810339Ssam /*VARARGS*/
79910339Ssam setdebug()
80010339Ssam {
80110339Ssam 
80217484Sleres 	debug = debug ? 0 : 1;
80310339Ssam 	printf("%s turn on socket level debugging.\n",
80425289Skarels 		debug ? "Will" : "Won't");
80510339Ssam 	fflush(stdout);
80617484Sleres 	if (net > 0 &&
80717484Sleres 	    setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0)
80811758Ssam 		perror("setsockopt (SO_DEBUG)");
80910339Ssam }
81010339Ssam 
81117922Sralph sendesc()
81217922Sralph {
81317922Sralph 	*nfrontp++ = escape;
81417922Sralph }
81517922Sralph 
81617922Sralph ayt()
81717922Sralph {
81817922Sralph 	*nfrontp++ = IAC;
81917922Sralph 	*nfrontp++ = AYT;
82017922Sralph }
82117922Sralph 
82217922Sralph intp()
82317922Sralph {
82417922Sralph 	*nfrontp++ = IAC;
82517922Sralph 	*nfrontp++ = IP;
82617922Sralph }
82717922Sralph 
8286000Sroot /*
8296000Sroot  * Construct a control character sequence
8306000Sroot  * for a special character.
8316000Sroot  */
8326000Sroot char *
8336000Sroot control(c)
8346000Sroot 	register int c;
8356000Sroot {
8366000Sroot 	static char buf[3];
8376000Sroot 
8386000Sroot 	if (c == 0177)
8396000Sroot 		return ("^?");
8406000Sroot 	if (c >= 040) {
8416000Sroot 		buf[0] = c;
8426000Sroot 		buf[1] = 0;
8436000Sroot 	} else {
8446000Sroot 		buf[0] = '^';
8456000Sroot 		buf[1] = '@'+c;
8466000Sroot 		buf[2] = 0;
8476000Sroot 	}
8486000Sroot 	return (buf);
8496000Sroot }
8506000Sroot 
8516000Sroot struct cmd *
8526000Sroot getcmd(name)
8536000Sroot 	register char *name;
8546000Sroot {
8556000Sroot 	register char *p, *q;
8566000Sroot 	register struct cmd *c, *found;
8576000Sroot 	register int nmatches, longest;
8586000Sroot 
8596000Sroot 	longest = 0;
8606000Sroot 	nmatches = 0;
8616000Sroot 	found = 0;
8626000Sroot 	for (c = cmdtab; p = c->name; c++) {
8636000Sroot 		for (q = name; *q == *p++; q++)
8646000Sroot 			if (*q == 0)		/* exact match? */
8656000Sroot 				return (c);
8666000Sroot 		if (!*q) {			/* the name was a prefix */
8676000Sroot 			if (q - name > longest) {
8686000Sroot 				longest = q - name;
8696000Sroot 				nmatches = 1;
8706000Sroot 				found = c;
8716000Sroot 			} else if (q - name == longest)
8726000Sroot 				nmatches++;
8736000Sroot 		}
8746000Sroot 	}
8756000Sroot 	if (nmatches > 1)
8766000Sroot 		return ((struct cmd *)-1);
8776000Sroot 	return (found);
8786000Sroot }
8796000Sroot 
8806000Sroot deadpeer()
8816000Sroot {
8828378Ssam 	(void) mode(0);
8836000Sroot 	longjmp(peerdied, -1);
8846000Sroot }
8856000Sroot 
8866000Sroot intr()
8876000Sroot {
8888378Ssam 	(void) mode(0);
8896000Sroot 	longjmp(toplevel, -1);
8906000Sroot }
8916000Sroot 
8926000Sroot ttyflush(fd)
8936000Sroot {
8946000Sroot 	int n;
8956000Sroot 
8966000Sroot 	if ((n = tfrontp - tbackp) > 0)
8976000Sroot 		n = write(fd, tbackp, n);
8988345Ssam 	if (n < 0)
8998345Ssam 		return;
9006000Sroot 	tbackp += n;
9016000Sroot 	if (tbackp == tfrontp)
9026000Sroot 		tbackp = tfrontp = ttyobuf;
9036000Sroot }
9046000Sroot 
9056000Sroot netflush(fd)
9066000Sroot {
9076000Sroot 	int n;
9086000Sroot 
9096000Sroot 	if ((n = nfrontp - nbackp) > 0)
9106000Sroot 		n = write(fd, nbackp, n);
9116501Ssam 	if (n < 0) {
9126501Ssam 		if (errno != ENOBUFS && errno != EWOULDBLOCK) {
9138378Ssam 			(void) mode(0);
9148377Ssam 			perror(hostname);
9156501Ssam 			close(fd);
9166501Ssam 			longjmp(peerdied, -1);
9176501Ssam 			/*NOTREACHED*/
9186501Ssam 		}
9196000Sroot 		n = 0;
9206501Ssam 	}
9216000Sroot 	nbackp += n;
9226000Sroot 	if (nbackp == nfrontp)
9236000Sroot 		nbackp = nfrontp = netobuf;
9246000Sroot }
9256024Ssam 
9266293Sroot /*VARARGS*/
9276293Sroot printoption(direction, fmt, option, what)
9286024Ssam 	char *direction, *fmt;
9296293Sroot 	int option, what;
9306024Ssam {
9318345Ssam 	if (!showoptions)
9328345Ssam 		return;
9336024Ssam 	printf("%s ", direction);
9346024Ssam 	if (fmt == doopt)
9356024Ssam 		fmt = "do";
9366024Ssam 	else if (fmt == dont)
9376024Ssam 		fmt = "dont";
9386024Ssam 	else if (fmt == will)
9396024Ssam 		fmt = "will";
9406024Ssam 	else if (fmt == wont)
9416024Ssam 		fmt = "wont";
9426024Ssam 	else
9436024Ssam 		fmt = "???";
9446024Ssam 	if (option < TELOPT_SUPDUP)
9456293Sroot 		printf("%s %s", fmt, telopts[option]);
9466024Ssam 	else
9476293Sroot 		printf("%s %d", fmt, option);
9486293Sroot 	if (*direction == '<') {
9496293Sroot 		printf("\r\n");
9506293Sroot 		return;
9516293Sroot 	}
9526293Sroot 	printf(" (%s)\r\n", what ? "reply" : "don't reply");
9536024Ssam }
954