xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 27021)
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*27021Sminshall static char sccsid[] = "@(#)telnet.c	5.6 (Berkeley) 04/09/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;
55*27021Sminshall int	printnet = 0;
56*27021Sminshall static FILE	*NetTrace;
5725289Skarels int	telnetport = 1;
586000Sroot char	*prompt;
599972Ssam char	escape = CTRL(]);
606000Sroot 
616000Sroot char	line[200];
626000Sroot int	margc;
636000Sroot char	*margv[20];
646000Sroot 
656000Sroot jmp_buf	toplevel;
666000Sroot jmp_buf	peerdied;
676000Sroot 
686000Sroot extern	int errno;
696000Sroot 
706000Sroot int	tn(), quit(), suspend(), bye(), help();
716024Ssam int	setescape(), status(), toggle(), setoptions();
7217922Sralph int	setcrmod(), setdebug(), sendesc(), ayt(), intp();
73*27021Sminshall int	setprintnet();
746000Sroot 
758378Ssam #define HELPINDENT (sizeof ("connect"))
766000Sroot 
776000Sroot struct cmd {
7811758Ssam 	char	*name;		/* command name */
7911758Ssam 	char	*help;		/* help string */
8011758Ssam 	int	(*handler)();	/* routine which executes command */
81*27021Sminshall 	int	dohelp;		/* Should we give general help information? */
826000Sroot };
836000Sroot 
8411758Ssam char	openhelp[] =	"connect to a site";
8511758Ssam char	closehelp[] =	"close current connection";
8611758Ssam char	quithelp[] =	"exit telnet";
8711758Ssam char	zhelp[] =	"suspend telnet";
8811758Ssam char	debughelp[] =	"toggle debugging";
8911758Ssam char	escapehelp[] =	"set escape character";
9011758Ssam char	statushelp[] =	"print status information";
9111758Ssam char	helphelp[] =	"print help information";
9211758Ssam char	optionshelp[] =	"toggle viewing of options processing";
9311758Ssam char	crmodhelp[] =	"toggle mapping of received carriage returns";
9417922Sralph char	sendeschelp[] =	"send escape character";
9525289Skarels char	aythelp[] =	"send \"Are You There\"";
9625289Skarels char	intphelp[] =	"send \"Interrupt Process\"";
97*27021Sminshall char	printnethelp[] ="toggle printing of raw network data";
986000Sroot 
996000Sroot struct cmd cmdtab[] = {
100*27021Sminshall 	{ "open",	openhelp,	tn, 1 },
101*27021Sminshall 	{ "close",	closehelp,	bye, 1 },
102*27021Sminshall 	{ "quit",	quithelp,	quit, 1 },
103*27021Sminshall 	{ "z",		zhelp,		suspend, 1 },
104*27021Sminshall 	{ "escape",	escapehelp,	setescape, 1 },
105*27021Sminshall 	{ "status",	statushelp,	status, 1 },
106*27021Sminshall 	{ "crmod",	crmodhelp,	setcrmod, 1 },
107*27021Sminshall 	{ "ayt",	aythelp,	ayt, 1 },
108*27021Sminshall 	{ "interrupt",	intphelp,	intp, 1 },
109*27021Sminshall 	{ "passthru",	sendeschelp,	sendesc, 1 },
110*27021Sminshall 	{ "help",	helphelp,	help, 1 },
111*27021Sminshall 	{ "?",		helphelp,	help, 0 },
112*27021Sminshall 	{ "options",	optionshelp,	setoptions, 0 },
113*27021Sminshall 	{ "debug",	debughelp,	setdebug, 0 },
114*27021Sminshall 	{ "printnet",	printnethelp,	setprintnet, 0 },
1156000Sroot 	0
1166000Sroot };
1176000Sroot 
1189972Ssam struct sockaddr_in sin;
1196000Sroot 
1206000Sroot int	intr(), deadpeer();
1216000Sroot char	*control();
1226000Sroot struct	cmd *getcmd();
1238345Ssam struct	servent *sp;
1246000Sroot 
12513076Ssam struct	tchars otc;
12613076Ssam struct	ltchars oltc;
12713076Ssam struct	sgttyb ottyb;
1288378Ssam 
1296000Sroot main(argc, argv)
1306000Sroot 	int argc;
1316000Sroot 	char *argv[];
1326000Sroot {
1338345Ssam 	sp = getservbyname("telnet", "tcp");
1348345Ssam 	if (sp == 0) {
1358345Ssam 		fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
1368345Ssam 		exit(1);
1378345Ssam 	}
138*27021Sminshall 	NetTrace = stdout;
13913076Ssam 	ioctl(0, TIOCGETP, (char *)&ottyb);
14013076Ssam 	ioctl(0, TIOCGETC, (char *)&otc);
14113076Ssam 	ioctl(0, TIOCGLTC, (char *)&oltc);
1426000Sroot 	setbuf(stdin, 0);
1436000Sroot 	setbuf(stdout, 0);
1446000Sroot 	prompt = argv[0];
14517484Sleres 	if (argc > 1 && !strcmp(argv[1], "-d")) {
14617484Sleres 		debug = 1;
14717484Sleres 		argv++;
14817484Sleres 		argc--;
14917484Sleres 	}
150*27021Sminshall 	if (argc > 1 && !strcmp(argv[1], "-n")) {
151*27021Sminshall 	    argv++;
152*27021Sminshall 	    argc--;
153*27021Sminshall 	    if (argc > 1) {		/* get file name */
154*27021Sminshall 		NetTrace = fopen(argv[1], "w");
155*27021Sminshall 		argv++;
156*27021Sminshall 		argc--;
157*27021Sminshall 		if (NetTrace == NULL) {
158*27021Sminshall 		    NetTrace = stdout;
159*27021Sminshall 		}
160*27021Sminshall 	    }
161*27021Sminshall 	}
1626000Sroot 	if (argc != 1) {
1636000Sroot 		if (setjmp(toplevel) != 0)
1646000Sroot 			exit(0);
1656000Sroot 		tn(argc, argv);
1666000Sroot 	}
1676000Sroot 	setjmp(toplevel);
1686000Sroot 	for (;;)
1696000Sroot 		command(1);
1706000Sroot }
1716000Sroot 
1728377Ssam char	*hostname;
1738377Ssam char	hnamebuf[32];
1746000Sroot 
1756000Sroot tn(argc, argv)
1766000Sroot 	int argc;
1776000Sroot 	char *argv[];
1786000Sroot {
1796000Sroot 	register int c;
18025425Skarels 	register struct hostent *host = 0;
1816000Sroot 
1826000Sroot 	if (connected) {
1838377Ssam 		printf("?Already connected to %s\n", hostname);
1846000Sroot 		return;
1856000Sroot 	}
1866000Sroot 	if (argc < 2) {
1876000Sroot 		strcpy(line, "Connect ");
1886000Sroot 		printf("(to) ");
1896000Sroot 		gets(&line[strlen(line)]);
1906000Sroot 		makeargv();
1916000Sroot 		argc = margc;
1926000Sroot 		argv = margv;
1936000Sroot 	}
1946000Sroot 	if (argc > 3) {
1956000Sroot 		printf("usage: %s host-name [port]\n", argv[0]);
1966000Sroot 		return;
1976000Sroot 	}
19825425Skarels 	sin.sin_addr.s_addr = inet_addr(argv[1]);
19925425Skarels 	if (sin.sin_addr.s_addr != -1) {
20025425Skarels 		sin.sin_family = AF_INET;
20125425Skarels 		strcpy(hnamebuf, argv[1]);
20225425Skarels 		hostname = hnamebuf;
2038377Ssam 	} else {
20425425Skarels 		host = gethostbyname(argv[1]);
20525425Skarels 		if (host) {
20625425Skarels 			sin.sin_family = host->h_addrtype;
20725425Skarels 			bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
20825425Skarels 				host->h_length);
20925425Skarels 			hostname = host->h_name;
21025425Skarels 		} else {
2118377Ssam 			printf("%s: unknown host\n", argv[1]);
2128377Ssam 			return;
2138377Ssam 		}
2146000Sroot 	}
2158345Ssam 	sin.sin_port = sp->s_port;
2166024Ssam 	if (argc == 3) {
2176024Ssam 		sin.sin_port = atoi(argv[2]);
21825289Skarels 		if (sin.sin_port <= 0) {
21925289Skarels 			sp = getservbyname(argv[2], "tcp");
22025289Skarels 			if (sp)
22125289Skarels 				sin.sin_port = sp->s_port;
22225289Skarels 			else {
22325289Skarels 				printf("%s: bad port number\n", argv[2]);
22425289Skarels 				return;
22525289Skarels 			}
22625289Skarels 		} else {
22725289Skarels 			sin.sin_port = atoi(argv[2]);
22825289Skarels 			sin.sin_port = htons(sin.sin_port);
2296024Ssam 		}
23025289Skarels 		telnetport = 0;
2316024Ssam 	}
23212989Ssam 	signal(SIGINT, intr);
23312989Ssam 	signal(SIGPIPE, deadpeer);
2346000Sroot 	printf("Trying...\n");
235*27021Sminshall 	do {
236*27021Sminshall 		net = socket(AF_INET, SOCK_STREAM, 0);
237*27021Sminshall 		if (net < 0) {
238*27021Sminshall 			perror("telnet: socket");
239*27021Sminshall 			return;
240*27021Sminshall 		}
241*27021Sminshall 		if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug,
242*27021Sminshall 		    sizeof(debug)) < 0)
243*27021Sminshall 			perror("setsockopt (SO_DEBUG)");
244*27021Sminshall 		if (connect(net, (caddr_t)&sin, sizeof (sin)) < 0) {
245*27021Sminshall 			if (host && host->h_addr_list[1]) {
246*27021Sminshall 				int oerrno = errno;
24725425Skarels 
248*27021Sminshall 				fprintf(stderr,
249*27021Sminshall 				    "telnet: connect to address %s: ",
250*27021Sminshall 				    inet_ntoa(sin.sin_addr));
251*27021Sminshall 				errno = oerrno;
252*27021Sminshall 				perror(0);
253*27021Sminshall 				host->h_addr_list++;
254*27021Sminshall 				bcopy(host->h_addr_list[0],
255*27021Sminshall 				    (caddr_t)&sin.sin_addr, host->h_length);
256*27021Sminshall 				fprintf(stderr, "Trying %s...\n",
257*27021Sminshall 					inet_ntoa(sin.sin_addr));
258*27021Sminshall 				(void) close(net);
259*27021Sminshall 				continue;
26026814Skarels 			}
261*27021Sminshall 			perror("telnet: connect");
262*27021Sminshall 			signal(SIGINT, SIG_DFL);
263*27021Sminshall 			return;
26425425Skarels 		}
265*27021Sminshall 		connected++;
266*27021Sminshall 	} while (connected == 0);
2676000Sroot 	call(status, "status", 0);
2686000Sroot 	if (setjmp(peerdied) == 0)
2696000Sroot 		telnet(net);
2706000Sroot 	fprintf(stderr, "Connection closed by foreign host.\n");
2716000Sroot 	exit(1);
2726000Sroot }
2736000Sroot 
2746000Sroot /*
2756000Sroot  * Print status about the connection.
2766000Sroot  */
2776000Sroot /*VARARGS*/
2786000Sroot status()
2796000Sroot {
2806000Sroot 	if (connected)
2818377Ssam 		printf("Connected to %s.\n", hostname);
2826000Sroot 	else
2836000Sroot 		printf("No connection.\n");
2846000Sroot 	printf("Escape character is '%s'.\n", control(escape));
2859972Ssam 	fflush(stdout);
2866000Sroot }
2876000Sroot 
2886000Sroot makeargv()
2896000Sroot {
2906000Sroot 	register char *cp;
2916000Sroot 	register char **argp = margv;
2926000Sroot 
2936000Sroot 	margc = 0;
2946000Sroot 	for (cp = line; *cp;) {
2956000Sroot 		while (isspace(*cp))
2966000Sroot 			cp++;
2976000Sroot 		if (*cp == '\0')
2986000Sroot 			break;
2996000Sroot 		*argp++ = cp;
3006000Sroot 		margc += 1;
3016000Sroot 		while (*cp != '\0' && !isspace(*cp))
3026000Sroot 			cp++;
3036000Sroot 		if (*cp == '\0')
3046000Sroot 			break;
3056000Sroot 		*cp++ = '\0';
3066000Sroot 	}
3076000Sroot 	*argp++ = 0;
3086000Sroot }
3096000Sroot 
3106000Sroot /*VARARGS*/
3116000Sroot suspend()
3126000Sroot {
3136000Sroot 	register int save;
3146000Sroot 
3156000Sroot 	save = mode(0);
3168378Ssam 	kill(0, SIGTSTP);
3178378Ssam 	/* reget parameters in case they were changed */
31813076Ssam 	ioctl(0, TIOCGETP, (char *)&ottyb);
31913076Ssam 	ioctl(0, TIOCGETC, (char *)&otc);
32013076Ssam 	ioctl(0, TIOCGLTC, (char *)&oltc);
3218378Ssam 	(void) mode(save);
3226000Sroot }
3236000Sroot 
3246000Sroot /*VARARGS*/
3256000Sroot bye()
3266000Sroot {
3278378Ssam 	register char *op;
3286000Sroot 
3298378Ssam 	(void) mode(0);
3306000Sroot 	if (connected) {
33111758Ssam 		shutdown(net, 2);
3326000Sroot 		printf("Connection closed.\n");
3336000Sroot 		close(net);
3346000Sroot 		connected = 0;
3358378Ssam 		/* reset his options */
3368378Ssam 		for (op = hisopts; op < &hisopts[256]; op++)
3378378Ssam 			*op = 0;
3386000Sroot 	}
3396000Sroot }
3406000Sroot 
3416000Sroot /*VARARGS*/
3426000Sroot quit()
3436000Sroot {
3446000Sroot 	call(bye, "bye", 0);
3456000Sroot 	exit(0);
3466000Sroot }
3476000Sroot 
3486000Sroot /*
3496000Sroot  * Help command.
3506000Sroot  */
3516000Sroot help(argc, argv)
3526000Sroot 	int argc;
3536000Sroot 	char *argv[];
3546000Sroot {
3556000Sroot 	register struct cmd *c;
3566000Sroot 
3576000Sroot 	if (argc == 1) {
3586000Sroot 		printf("Commands may be abbreviated.  Commands are:\n\n");
3596000Sroot 		for (c = cmdtab; c->name; c++)
360*27021Sminshall 			if (c->dohelp) {
361*27021Sminshall 				printf("%-*s\t%s\n", HELPINDENT, c->name,
362*27021Sminshall 								    c->help);
363*27021Sminshall 			}
3646000Sroot 		return;
3656000Sroot 	}
3666000Sroot 	while (--argc > 0) {
3676000Sroot 		register char *arg;
3686000Sroot 		arg = *++argv;
3696000Sroot 		c = getcmd(arg);
3706000Sroot 		if (c == (struct cmd *)-1)
3716000Sroot 			printf("?Ambiguous help command %s\n", arg);
3726000Sroot 		else if (c == (struct cmd *)0)
3736000Sroot 			printf("?Invalid help command %s\n", arg);
3746000Sroot 		else
3756000Sroot 			printf("%s\n", c->help);
3766000Sroot 	}
3776000Sroot }
3786000Sroot 
3796000Sroot /*
3806000Sroot  * Call routine with argc, argv set from args (terminated by 0).
3816000Sroot  * VARARGS2
3826000Sroot  */
3836000Sroot call(routine, args)
3846000Sroot 	int (*routine)();
3856000Sroot 	int args;
3866000Sroot {
3876000Sroot 	register int *argp;
3886000Sroot 	register int argc;
3896000Sroot 
3906000Sroot 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
3916000Sroot 		;
3926000Sroot 	(*routine)(argc, &args);
3936000Sroot }
3946000Sroot 
39513076Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
39613076Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
3979972Ssam 
3986000Sroot mode(f)
3996000Sroot 	register int f;
4006000Sroot {
4018378Ssam 	static int prevmode = 0;
40213076Ssam 	struct tchars *tc;
40313076Ssam 	struct ltchars *ltc;
40413076Ssam 	struct sgttyb sb;
40513076Ssam 	int onoff, old;
4066000Sroot 
4078378Ssam 	if (prevmode == f)
4088378Ssam 		return (f);
4098378Ssam 	old = prevmode;
4108378Ssam 	prevmode = f;
41113076Ssam 	sb = ottyb;
4126000Sroot 	switch (f) {
4138378Ssam 
4146000Sroot 	case 0:
4156000Sroot 		onoff = 0;
4169972Ssam 		tc = &otc;
41713076Ssam 		ltc = &oltc;
4186000Sroot 		break;
4196000Sroot 
4206000Sroot 	case 1:
4216000Sroot 	case 2:
42213076Ssam 		sb.sg_flags |= CBREAK;
4238378Ssam 		if (f == 1)
42413076Ssam 			sb.sg_flags &= ~(ECHO|CRMOD);
4258378Ssam 		else
42613076Ssam 			sb.sg_flags |= ECHO|CRMOD;
42713076Ssam 		sb.sg_erase = sb.sg_kill = -1;
4289972Ssam 		tc = &notc;
42913076Ssam 		ltc = &noltc;
4306000Sroot 		onoff = 1;
4319972Ssam 		break;
4329972Ssam 
4339972Ssam 	default:
4349972Ssam 		return;
4356000Sroot 	}
43613076Ssam 	ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
43713076Ssam 	ioctl(fileno(stdin), TIOCSETC, (char *)tc);
43813076Ssam 	ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
4396000Sroot 	ioctl(fileno(stdin), FIONBIO, &onoff);
4406000Sroot 	ioctl(fileno(stdout), FIONBIO, &onoff);
4416000Sroot 	return (old);
4426000Sroot }
4436000Sroot 
4446000Sroot char	sibuf[BUFSIZ], *sbp;
4456000Sroot char	tibuf[BUFSIZ], *tbp;
4466000Sroot int	scc, tcc;
4476000Sroot 
4486000Sroot /*
4496000Sroot  * Select from tty and network...
4506000Sroot  */
4516000Sroot telnet(s)
4526000Sroot 	int s;
4536000Sroot {
4546000Sroot 	register int c;
4556000Sroot 	int tin = fileno(stdin), tout = fileno(stdout);
4566000Sroot 	int on = 1;
4576000Sroot 
4588378Ssam 	(void) mode(2);
4596000Sroot 	ioctl(s, FIONBIO, &on);
46025289Skarels 	if (telnetport && !hisopts[TELOPT_SGA])
46117922Sralph 		willoption(TELOPT_SGA);
4626000Sroot 	for (;;) {
4636000Sroot 		int ibits = 0, obits = 0;
4646000Sroot 
4656000Sroot 		if (nfrontp - nbackp)
4666000Sroot 			obits |= (1 << s);
4676000Sroot 		else
4686000Sroot 			ibits |= (1 << tin);
4696000Sroot 		if (tfrontp - tbackp)
4706000Sroot 			obits |= (1 << tout);
4716000Sroot 		else
4726000Sroot 			ibits |= (1 << s);
4736000Sroot 		if (scc < 0 && tcc < 0)
4746000Sroot 			break;
4759217Ssam 		select(16, &ibits, &obits, 0, 0);
4766000Sroot 		if (ibits == 0 && obits == 0) {
4776000Sroot 			sleep(5);
4786000Sroot 			continue;
4796000Sroot 		}
4806000Sroot 
4816000Sroot 		/*
4826000Sroot 		 * Something to read from the network...
4836000Sroot 		 */
4846000Sroot 		if (ibits & (1 << s)) {
4858378Ssam 			scc = read(s, sibuf, sizeof (sibuf));
4866000Sroot 			if (scc < 0 && errno == EWOULDBLOCK)
4876000Sroot 				scc = 0;
4886000Sroot 			else {
4896000Sroot 				if (scc <= 0)
4906000Sroot 					break;
4916000Sroot 				sbp = sibuf;
492*27021Sminshall 				if (printnet) {
493*27021Sminshall 					Dump('<', sbp, scc);
494*27021Sminshall 				}
4956000Sroot 			}
4966000Sroot 		}
4976000Sroot 
4986000Sroot 		/*
4996000Sroot 		 * Something to read from the tty...
5006000Sroot 		 */
5016000Sroot 		if (ibits & (1 << tin)) {
5028378Ssam 			tcc = read(tin, tibuf, sizeof (tibuf));
5036000Sroot 			if (tcc < 0 && errno == EWOULDBLOCK)
5046000Sroot 				tcc = 0;
5056000Sroot 			else {
5066000Sroot 				if (tcc <= 0)
5076000Sroot 					break;
5086000Sroot 				tbp = tibuf;
5096000Sroot 			}
5106000Sroot 		}
5116000Sroot 
5126000Sroot 		while (tcc > 0) {
5136000Sroot 			register int c;
5146000Sroot 
5156000Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
5166000Sroot 				break;
5176000Sroot 			c = *tbp++ & 0377, tcc--;
5186000Sroot 			if (strip(c) == escape) {
5196000Sroot 				command(0);
5206000Sroot 				tcc = 0;
5216000Sroot 				break;
5226000Sroot 			}
52317922Sralph 			switch (c) {
524*27021Sminshall 			case '\n'|0x80:
52517922Sralph 			case '\n':
526*27021Sminshall 				/*
527*27021Sminshall 				 * If echoing is happening locally,
528*27021Sminshall 				 * then a newline (unix) is CRLF (TELNET).
529*27021Sminshall 				 */
53017922Sralph 				if (!hisopts[TELOPT_ECHO])
53117922Sralph 					*nfrontp++ = '\r';
53217922Sralph 				*nfrontp++ = '\n';
53317922Sralph 				break;
534*27021Sminshall 			case '\r'|0x80:
53517922Sralph 			case '\r':
53617922Sralph 				*nfrontp++ = '\r';
537*27021Sminshall 				*nfrontp++ = '\0';
53817922Sralph 				break;
53917922Sralph 			case IAC:
54017922Sralph 				*nfrontp++ = IAC;
541*27021Sminshall 				*nfrontp++ = IAC;
542*27021Sminshall 				break;
54317922Sralph 			default:
54411758Ssam 				*nfrontp++ = c;
54517922Sralph 				break;
54617922Sralph 			}
5476000Sroot 		}
5486000Sroot 		if ((obits & (1 << s)) && (nfrontp - nbackp) > 0)
5496000Sroot 			netflush(s);
5506000Sroot 		if (scc > 0)
5516000Sroot 			telrcv();
5526000Sroot 		if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0)
5536000Sroot 			ttyflush(tout);
5546000Sroot 	}
5558378Ssam 	(void) mode(0);
5566000Sroot }
5576000Sroot 
5586000Sroot command(top)
5596000Sroot 	int top;
5606000Sroot {
5616000Sroot 	register struct cmd *c;
5626000Sroot 	int oldmode, wasopen;
5636000Sroot 
5646000Sroot 	oldmode = mode(0);
5656000Sroot 	if (!top)
5666000Sroot 		putchar('\n');
5676000Sroot 	else
56812989Ssam 		signal(SIGINT, SIG_DFL);
5696000Sroot 	for (;;) {
5706000Sroot 		printf("%s> ", prompt);
57113997Ssam 		if (gets(line) == 0) {
57225800Slepreau 			if (feof(stdin))
57325800Slepreau 				quit();
5746000Sroot 			break;
57513997Ssam 		}
5766000Sroot 		if (line[0] == 0)
5776000Sroot 			break;
5786000Sroot 		makeargv();
5796000Sroot 		c = getcmd(margv[0]);
5806000Sroot 		if (c == (struct cmd *)-1) {
5816000Sroot 			printf("?Ambiguous command\n");
5826000Sroot 			continue;
5836000Sroot 		}
5846000Sroot 		if (c == 0) {
5856000Sroot 			printf("?Invalid command\n");
5866000Sroot 			continue;
5876000Sroot 		}
5886000Sroot 		(*c->handler)(margc, margv);
5896000Sroot 		if (c->handler != help)
5906000Sroot 			break;
5916000Sroot 	}
5926000Sroot 	if (!top) {
5936000Sroot 		if (!connected)
5946000Sroot 			longjmp(toplevel, 1);
5958378Ssam 		(void) mode(oldmode);
5966000Sroot 	}
5976000Sroot }
5986000Sroot 
5996000Sroot /*
6006000Sroot  * Telnet receiver states for fsm
6016000Sroot  */
6026000Sroot #define	TS_DATA		0
6036000Sroot #define	TS_IAC		1
6046000Sroot #define	TS_WILL		2
6056000Sroot #define	TS_WONT		3
6066000Sroot #define	TS_DO		4
6076000Sroot #define	TS_DONT		5
608*27021Sminshall #define	TS_CR		6
6096000Sroot 
6106000Sroot telrcv()
6116000Sroot {
6126000Sroot 	register int c;
6136000Sroot 	static int state = TS_DATA;
6146000Sroot 
6156000Sroot 	while (scc > 0) {
6166000Sroot 		c = *sbp++ & 0377, scc--;
6176000Sroot 		switch (state) {
6186000Sroot 
619*27021Sminshall 		case TS_CR:
620*27021Sminshall 			state = TS_DATA;
621*27021Sminshall 			if ((c == '\0') || (c == '\n')) {
622*27021Sminshall 			    break;	/* by now, we ignore \n */
623*27021Sminshall 			}
624*27021Sminshall 
6256000Sroot 		case TS_DATA:
6269972Ssam 			if (c == IAC) {
6276000Sroot 				state = TS_IAC;
6289972Ssam 				continue;
6299972Ssam 			}
630*27021Sminshall 			if (c == '\r') {
631*27021Sminshall 				if (scc > 0) {
632*27021Sminshall 					c = *sbp&0377;
633*27021Sminshall 					if (c == 0) {
634*27021Sminshall 						sbp++, scc--;
635*27021Sminshall 						*tfrontp++ = '\r';
636*27021Sminshall 				/*
637*27021Sminshall 				 * The following hack is needed since we can't
638*27021Sminshall 				 * set CRMOD on output only.  Machines like
639*27021Sminshall 				 * MULTICS like to send \r without \n; since
640*27021Sminshall 				 * we must turn off CRMOD to get proper input,
641*27021Sminshall 				 * the mapping is done here (sigh).
642*27021Sminshall 				 */
643*27021Sminshall 						if (crmod) {
644*27021Sminshall 							*tfrontp++ = '\n';
645*27021Sminshall 						}
646*27021Sminshall 					} else if (!hisopts[TELOPT_ECHO] &&
647*27021Sminshall 								(c == '\n')) {
648*27021Sminshall 						sbp++, scc--;
649*27021Sminshall 						*tfrontp++ = '\n';
650*27021Sminshall 					} else {
651*27021Sminshall 						*tfrontp++ = '\r';
652*27021Sminshall 					}
653*27021Sminshall 				} else {
654*27021Sminshall 					state = TS_CR;
655*27021Sminshall 					*tfrontp++ = '\r';
656*27021Sminshall 				}
657*27021Sminshall 			} else {
658*27021Sminshall 				*tfrontp++ = c;
659*27021Sminshall 			}
6606000Sroot 			continue;
6616000Sroot 
6626000Sroot 		case TS_IAC:
6636000Sroot 			switch (c) {
6646000Sroot 
6656000Sroot 			case WILL:
6666000Sroot 				state = TS_WILL;
6676000Sroot 				continue;
6686000Sroot 
6696000Sroot 			case WONT:
6706000Sroot 				state = TS_WONT;
6716000Sroot 				continue;
6726000Sroot 
6736000Sroot 			case DO:
6746000Sroot 				state = TS_DO;
6756000Sroot 				continue;
6766000Sroot 
6776000Sroot 			case DONT:
6786000Sroot 				state = TS_DONT;
6796000Sroot 				continue;
6806000Sroot 
6816000Sroot 			case DM:
6826000Sroot 				ioctl(fileno(stdout), TIOCFLUSH, 0);
6836000Sroot 				break;
6846000Sroot 
6856000Sroot 			case NOP:
6866000Sroot 			case GA:
6876000Sroot 				break;
6886000Sroot 
6896000Sroot 			default:
6906000Sroot 				break;
6916000Sroot 			}
6926000Sroot 			state = TS_DATA;
6936000Sroot 			continue;
6946000Sroot 
6956000Sroot 		case TS_WILL:
6968345Ssam 			printoption("RCVD", will, c, !hisopts[c]);
6976000Sroot 			if (!hisopts[c])
6986000Sroot 				willoption(c);
6996000Sroot 			state = TS_DATA;
7006000Sroot 			continue;
7016000Sroot 
7026000Sroot 		case TS_WONT:
7038345Ssam 			printoption("RCVD", wont, c, hisopts[c]);
7046000Sroot 			if (hisopts[c])
7056000Sroot 				wontoption(c);
7066000Sroot 			state = TS_DATA;
7076000Sroot 			continue;
7086000Sroot 
7096000Sroot 		case TS_DO:
7108345Ssam 			printoption("RCVD", doopt, c, !myopts[c]);
7116000Sroot 			if (!myopts[c])
7126000Sroot 				dooption(c);
7136000Sroot 			state = TS_DATA;
7146000Sroot 			continue;
7156000Sroot 
7166000Sroot 		case TS_DONT:
7178345Ssam 			printoption("RCVD", dont, c, myopts[c]);
7186000Sroot 			if (myopts[c]) {
7196000Sroot 				myopts[c] = 0;
7206000Sroot 				sprintf(nfrontp, wont, c);
7218378Ssam 				nfrontp += sizeof (wont) - 2;
7228345Ssam 				printoption("SENT", wont, c);
7236000Sroot 			}
7246000Sroot 			state = TS_DATA;
7256000Sroot 			continue;
7266000Sroot 		}
7276000Sroot 	}
7286000Sroot }
7296000Sroot 
7306000Sroot willoption(option)
7316000Sroot 	int option;
7326000Sroot {
7336000Sroot 	char *fmt;
7346000Sroot 
7356000Sroot 	switch (option) {
7366000Sroot 
7376000Sroot 	case TELOPT_ECHO:
7388378Ssam 		(void) mode(1);
7396000Sroot 
7406000Sroot 	case TELOPT_SGA:
7416000Sroot 		hisopts[option] = 1;
7426000Sroot 		fmt = doopt;
7436000Sroot 		break;
7446000Sroot 
7456000Sroot 	case TELOPT_TM:
7466000Sroot 		fmt = dont;
7476000Sroot 		break;
7486000Sroot 
7496000Sroot 	default:
7506000Sroot 		fmt = dont;
7516000Sroot 		break;
7526000Sroot 	}
7536024Ssam 	sprintf(nfrontp, fmt, option);
7548378Ssam 	nfrontp += sizeof (dont) - 2;
7558345Ssam 	printoption("SENT", fmt, option);
7566000Sroot }
7576000Sroot 
7586000Sroot wontoption(option)
7596000Sroot 	int option;
7606000Sroot {
7616000Sroot 	char *fmt;
7626000Sroot 
7636000Sroot 	switch (option) {
7646000Sroot 
7656000Sroot 	case TELOPT_ECHO:
7668378Ssam 		(void) mode(2);
7676000Sroot 
7686000Sroot 	case TELOPT_SGA:
7696000Sroot 		hisopts[option] = 0;
7706000Sroot 		fmt = dont;
7716000Sroot 		break;
7726000Sroot 
7736000Sroot 	default:
7746000Sroot 		fmt = dont;
7756000Sroot 	}
7766000Sroot 	sprintf(nfrontp, fmt, option);
7778378Ssam 	nfrontp += sizeof (doopt) - 2;
7788345Ssam 	printoption("SENT", fmt, option);
7796000Sroot }
7806000Sroot 
7816000Sroot dooption(option)
7826000Sroot 	int option;
7836000Sroot {
7846000Sroot 	char *fmt;
7856000Sroot 
7866000Sroot 	switch (option) {
7876000Sroot 
7886000Sroot 	case TELOPT_TM:
7896000Sroot 		fmt = wont;
7906000Sroot 		break;
7916000Sroot 
79213231Ssam 	case TELOPT_ECHO:
79313231Ssam 		(void) mode(2);
79413231Ssam 		fmt = will;
79513231Ssam 		hisopts[option] = 0;
79613231Ssam 		break;
79713231Ssam 
7986000Sroot 	case TELOPT_SGA:
7996000Sroot 		fmt = will;
8006000Sroot 		break;
8016000Sroot 
8026000Sroot 	default:
8036000Sroot 		fmt = wont;
8046000Sroot 		break;
8056000Sroot 	}
8066000Sroot 	sprintf(nfrontp, fmt, option);
8078378Ssam 	nfrontp += sizeof (doopt) - 2;
8088345Ssam 	printoption("SENT", fmt, option);
8096000Sroot }
8106000Sroot 
8116000Sroot /*
8126000Sroot  * Set the escape character.
8136000Sroot  */
8146000Sroot setescape(argc, argv)
8156000Sroot 	int argc;
8166000Sroot 	char *argv[];
8176000Sroot {
8186000Sroot 	register char *arg;
8196000Sroot 	char buf[50];
8206000Sroot 
8216000Sroot 	if (argc > 2)
8226000Sroot 		arg = argv[1];
8236000Sroot 	else {
8246000Sroot 		printf("new escape character: ");
8256000Sroot 		gets(buf);
8266000Sroot 		arg = buf;
8276000Sroot 	}
8286000Sroot 	if (arg[0] != '\0')
8296000Sroot 		escape = arg[0];
8306000Sroot 	printf("Escape character is '%s'.\n", control(escape));
8319972Ssam 	fflush(stdout);
8326000Sroot }
8336000Sroot 
8346024Ssam /*VARARGS*/
8356024Ssam setoptions()
8366024Ssam {
8379972Ssam 
8386024Ssam 	showoptions = !showoptions;
83925289Skarels 	printf("%s show option processing.\n", showoptions ? "Will" : "Won't");
8409972Ssam 	fflush(stdout);
8416024Ssam }
8426024Ssam 
8439972Ssam /*VARARGS*/
8449972Ssam setcrmod()
8459972Ssam {
8469972Ssam 
8479972Ssam 	crmod = !crmod;
84825289Skarels 	printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
8499972Ssam 	fflush(stdout);
8509972Ssam }
8519972Ssam 
85210339Ssam /*VARARGS*/
85310339Ssam setdebug()
85410339Ssam {
85510339Ssam 
85617484Sleres 	debug = debug ? 0 : 1;
85710339Ssam 	printf("%s turn on socket level debugging.\n",
85825289Skarels 		debug ? "Will" : "Won't");
85910339Ssam 	fflush(stdout);
86017484Sleres 	if (net > 0 &&
86117484Sleres 	    setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0)
86211758Ssam 		perror("setsockopt (SO_DEBUG)");
86310339Ssam }
86410339Ssam 
865*27021Sminshall /*VARARGS*/
866*27021Sminshall static
867*27021Sminshall setprintnet()
868*27021Sminshall {
869*27021Sminshall 
870*27021Sminshall 	printnet = !printnet;
871*27021Sminshall 	printf("%s turn on printing of raw network traffic.\n",
872*27021Sminshall 		printnet ? "Will" : "Wont");
873*27021Sminshall }
874*27021Sminshall 
87517922Sralph sendesc()
87617922Sralph {
87717922Sralph 	*nfrontp++ = escape;
87817922Sralph }
87917922Sralph 
88017922Sralph ayt()
88117922Sralph {
88217922Sralph 	*nfrontp++ = IAC;
88317922Sralph 	*nfrontp++ = AYT;
88417922Sralph }
88517922Sralph 
88617922Sralph intp()
88717922Sralph {
88817922Sralph 	*nfrontp++ = IAC;
88917922Sralph 	*nfrontp++ = IP;
89017922Sralph }
89117922Sralph 
8926000Sroot /*
8936000Sroot  * Construct a control character sequence
8946000Sroot  * for a special character.
8956000Sroot  */
8966000Sroot char *
8976000Sroot control(c)
8986000Sroot 	register int c;
8996000Sroot {
9006000Sroot 	static char buf[3];
9016000Sroot 
9026000Sroot 	if (c == 0177)
9036000Sroot 		return ("^?");
9046000Sroot 	if (c >= 040) {
9056000Sroot 		buf[0] = c;
9066000Sroot 		buf[1] = 0;
9076000Sroot 	} else {
9086000Sroot 		buf[0] = '^';
9096000Sroot 		buf[1] = '@'+c;
9106000Sroot 		buf[2] = 0;
9116000Sroot 	}
9126000Sroot 	return (buf);
9136000Sroot }
9146000Sroot 
9156000Sroot struct cmd *
9166000Sroot getcmd(name)
9176000Sroot 	register char *name;
9186000Sroot {
9196000Sroot 	register char *p, *q;
9206000Sroot 	register struct cmd *c, *found;
9216000Sroot 	register int nmatches, longest;
9226000Sroot 
9236000Sroot 	longest = 0;
9246000Sroot 	nmatches = 0;
9256000Sroot 	found = 0;
9266000Sroot 	for (c = cmdtab; p = c->name; c++) {
9276000Sroot 		for (q = name; *q == *p++; q++)
9286000Sroot 			if (*q == 0)		/* exact match? */
9296000Sroot 				return (c);
9306000Sroot 		if (!*q) {			/* the name was a prefix */
9316000Sroot 			if (q - name > longest) {
9326000Sroot 				longest = q - name;
9336000Sroot 				nmatches = 1;
9346000Sroot 				found = c;
9356000Sroot 			} else if (q - name == longest)
9366000Sroot 				nmatches++;
9376000Sroot 		}
9386000Sroot 	}
9396000Sroot 	if (nmatches > 1)
9406000Sroot 		return ((struct cmd *)-1);
9416000Sroot 	return (found);
9426000Sroot }
9436000Sroot 
9446000Sroot deadpeer()
9456000Sroot {
9468378Ssam 	(void) mode(0);
9476000Sroot 	longjmp(peerdied, -1);
9486000Sroot }
9496000Sroot 
9506000Sroot intr()
9516000Sroot {
9528378Ssam 	(void) mode(0);
9536000Sroot 	longjmp(toplevel, -1);
9546000Sroot }
9556000Sroot 
9566000Sroot ttyflush(fd)
9576000Sroot {
9586000Sroot 	int n;
9596000Sroot 
9606000Sroot 	if ((n = tfrontp - tbackp) > 0)
9616000Sroot 		n = write(fd, tbackp, n);
9628345Ssam 	if (n < 0)
9638345Ssam 		return;
9646000Sroot 	tbackp += n;
9656000Sroot 	if (tbackp == tfrontp)
9666000Sroot 		tbackp = tfrontp = ttyobuf;
9676000Sroot }
9686000Sroot 
9696000Sroot netflush(fd)
9706000Sroot {
9716000Sroot 	int n;
9726000Sroot 
9736000Sroot 	if ((n = nfrontp - nbackp) > 0)
9746000Sroot 		n = write(fd, nbackp, n);
9756501Ssam 	if (n < 0) {
9766501Ssam 		if (errno != ENOBUFS && errno != EWOULDBLOCK) {
9778378Ssam 			(void) mode(0);
9788377Ssam 			perror(hostname);
9796501Ssam 			close(fd);
9806501Ssam 			longjmp(peerdied, -1);
9816501Ssam 			/*NOTREACHED*/
9826501Ssam 		}
9836000Sroot 		n = 0;
9846501Ssam 	}
985*27021Sminshall 	if (printnet) {
986*27021Sminshall 		Dump('>', nbackp, n);
987*27021Sminshall 	}
9886000Sroot 	nbackp += n;
9896000Sroot 	if (nbackp == nfrontp)
9906000Sroot 		nbackp = nfrontp = netobuf;
9916000Sroot }
9926024Ssam 
993*27021Sminshall static
994*27021Sminshall Dump(direction, buffer, length)
995*27021Sminshall char	direction;
996*27021Sminshall char	*buffer;
997*27021Sminshall int	length;
998*27021Sminshall {
999*27021Sminshall #   define BYTES_PER_LINE	32
1000*27021Sminshall #   define min(x,y)	((x<y)? x:y)
1001*27021Sminshall     char *pThis;
1002*27021Sminshall     int offset;
1003*27021Sminshall 
1004*27021Sminshall     offset = 0;
1005*27021Sminshall 
1006*27021Sminshall     while (length) {
1007*27021Sminshall 	/* print one line */
1008*27021Sminshall 	fprintf(NetTrace, "%c 0x%x\t", direction, offset);
1009*27021Sminshall 	pThis = buffer;
1010*27021Sminshall 	buffer = buffer+min(length, BYTES_PER_LINE);
1011*27021Sminshall 	while (pThis < buffer) {
1012*27021Sminshall 	    fprintf(NetTrace, "%.2x", (*pThis)&0xff);
1013*27021Sminshall 	    pThis++;
1014*27021Sminshall 	}
1015*27021Sminshall 	fprintf(NetTrace, "\n");
1016*27021Sminshall 	length -= BYTES_PER_LINE;
1017*27021Sminshall 	offset += BYTES_PER_LINE;
1018*27021Sminshall 	if (length < 0) {
1019*27021Sminshall 	    return;
1020*27021Sminshall 	}
1021*27021Sminshall 	/* find next unique line */
1022*27021Sminshall     }
1023*27021Sminshall }
1024*27021Sminshall 
1025*27021Sminshall 
10266293Sroot /*VARARGS*/
10276293Sroot printoption(direction, fmt, option, what)
10286024Ssam 	char *direction, *fmt;
10296293Sroot 	int option, what;
10306024Ssam {
10318345Ssam 	if (!showoptions)
10328345Ssam 		return;
10336024Ssam 	printf("%s ", direction);
10346024Ssam 	if (fmt == doopt)
10356024Ssam 		fmt = "do";
10366024Ssam 	else if (fmt == dont)
10376024Ssam 		fmt = "dont";
10386024Ssam 	else if (fmt == will)
10396024Ssam 		fmt = "will";
10406024Ssam 	else if (fmt == wont)
10416024Ssam 		fmt = "wont";
10426024Ssam 	else
10436024Ssam 		fmt = "???";
10446024Ssam 	if (option < TELOPT_SUPDUP)
10456293Sroot 		printf("%s %s", fmt, telopts[option]);
10466024Ssam 	else
10476293Sroot 		printf("%s %d", fmt, option);
10486293Sroot 	if (*direction == '<') {
10496293Sroot 		printf("\r\n");
10506293Sroot 		return;
10516293Sroot 	}
10526293Sroot 	printf(" (%s)\r\n", what ? "reply" : "don't reply");
10536024Ssam }
1054