xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 27228)
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*27228Sminshall static char sccsid[] = "@(#)telnet.c	5.11 (Berkeley) 04/20/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>
2327178Sminshall #include <sys/time.h>
249217Ssam 
259217Ssam #include <netinet/in.h>
269217Ssam 
2712212Ssam #define	TELOPTS
2812212Ssam #include <arpa/telnet.h>
2927186Sminshall #include <arpa/inet.h>
3012212Ssam 
316000Sroot #include <stdio.h>
326000Sroot #include <ctype.h>
336000Sroot #include <errno.h>
346000Sroot #include <signal.h>
356000Sroot #include <setjmp.h>
368345Ssam #include <netdb.h>
3727186Sminshall #include <strings.h>
389217Ssam 
3927178Sminshall 
4027178Sminshall 
4127178Sminshall /*
4227178Sminshall  * The following is defined just in case someone should want to run
4327178Sminshall  * this telnet on a 4.2 system.
4427178Sminshall  *
4527178Sminshall  * This has never been tested, so good luck...
4627178Sminshall  */
4727178Sminshall #ifndef	FD_SETSIZE
4827178Sminshall 
4927178Sminshall typedef long	fd_set;
5027178Sminshall 
5127178Sminshall #define	FD_SET(n, p)	(*(p) |= (1<<(n)))
5227178Sminshall #define	FD_CLR(n, p)	(*(p) &= ~(1<<(n)))
5327178Sminshall #define	FD_ISSET(n, p)	(*(p) & (1<<(n)))
5427178Sminshall #define FD_ZERO(p)	(*(p) = 0)
5527178Sminshall 
5627178Sminshall #endif
5727178Sminshall 
58*27228Sminshall #define	strip(x)	((x)&0x7f)
596000Sroot 
60*27228Sminshall char	ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
61*27228Sminshall #define	TTYADD(c)	{ if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
62*27228Sminshall #define	TTYLOC()	(tfrontp)
63*27228Sminshall #define	TTYMAX()	(ttyobuf+sizeof ttyobuf-1)
64*27228Sminshall #define	TTYMIN()	(netobuf)
65*27228Sminshall #define	TTYBYTES()	(tfrontp-tbackp)
66*27228Sminshall #define	TTYROOM()	(TTYMAX()-TTYLOC()+1)
6727088Sminshall 
68*27228Sminshall char	netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
6927088Sminshall #define	NETADD(c)	{ *nfrontp++ = c; }
7027088Sminshall #define	NET2ADD(c1,c2)	{ NETADD(c1); NETADD(c2); }
7127088Sminshall #define NETLOC()	(nfrontp)
72*27228Sminshall #define	NETMAX()	(netobuf+sizeof netobuf-1)
73*27228Sminshall #define	NETBYTES()	(nfrontp-nbackp)
74*27228Sminshall #define	NETROOM()	(NETMAX()-NETLOC()+1)
7527088Sminshall char	*neturg = 0;		/* one past last byte of urgent data */
766000Sroot 
776000Sroot char	hisopts[256];
786000Sroot char	myopts[256];
796000Sroot 
806000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
816000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
826000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
836000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
846000Sroot 
8527088Sminshall struct cmd {
8627088Sminshall 	char	*name;		/* command name */
8727088Sminshall 	char	*help;		/* help string */
8827088Sminshall 	int	(*handler)();	/* routine which executes command */
8927088Sminshall 	int	dohelp;		/* Should we give general help information? */
9027088Sminshall 	int	needconnect;	/* Do we need to be connected to execute? */
9127088Sminshall };
9227088Sminshall 
936000Sroot int	connected;
946000Sroot int	net;
9527088Sminshall int	tout;
969972Ssam int	showoptions = 0;
9710339Ssam int	debug = 0;
989972Ssam int	crmod = 0;
9927088Sminshall int	netdata = 0;
10027021Sminshall static FILE	*NetTrace;
10125289Skarels int	telnetport = 1;
10227088Sminshall 
10327088Sminshall 
1046000Sroot char	*prompt;
1059972Ssam char	escape = CTRL(]);
10627110Sminshall char	echoc = CTRL(E);
1076000Sroot 
10827186Sminshall int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
10927186Sminshall int	flushout = 0;		/* flush output */
110*27228Sminshall int	autoflush = 0;		/* flush output when interrupting? */
11127186Sminshall int	autosynch = 0;		/* send interrupt characters with SYNCH? */
11227186Sminshall int	localsigs = 0;		/* we recognize interrupt/quit */
11327186Sminshall int	donelclsigs = 0;	/* the user has set "localsigs" */
11427186Sminshall int	doechocharrecognition = 1;	/* in line mode recognize echo toggle */
11527186Sminshall int	dontlecho = 0;		/* do we suppress local echoing right now? */
11627186Sminshall 
1176000Sroot char	line[200];
1186000Sroot int	margc;
1196000Sroot char	*margv[20];
1206000Sroot 
1216000Sroot jmp_buf	toplevel;
1226000Sroot jmp_buf	peerdied;
1236000Sroot 
1246000Sroot extern	int errno;
1256000Sroot 
1266000Sroot 
1279972Ssam struct sockaddr_in sin;
1286000Sroot 
1296000Sroot struct	cmd *getcmd();
1308345Ssam struct	servent *sp;
1316000Sroot 
13227110Sminshall struct	tchars otc, ntc;
133*27228Sminshall struct	ltchars oltc, nltc;
13427110Sminshall struct	sgttyb ottyb, nttyb;
13527110Sminshall int	globalmode = 0;
13627110Sminshall int	flushline = 1;
1378378Ssam 
13827110Sminshall char	*hostname;
13927110Sminshall char	hnamebuf[32];
14027110Sminshall 
14127110Sminshall /*
14227110Sminshall  * The following are some clocks used to decide how to interpret
14327178Sminshall  * the relationship between various variables.
14427110Sminshall  */
14527110Sminshall 
14627110Sminshall struct {
14727110Sminshall     int
14827110Sminshall 	system,			/* what the current time is */
14927110Sminshall 	echotoggle,		/* last time user entered echo character */
15027178Sminshall 	modenegotiated,		/* last time operating mode negotiated */
15127178Sminshall 	didnetreceive,		/* last time we read data from network */
15227178Sminshall 	gotDM;			/* when did we last see a data mark */
15327186Sminshall } clocks;
15427110Sminshall 
15527186Sminshall #define	settimer(x)	clocks.x = clocks.system++
15627110Sminshall 
15727110Sminshall /*
15827110Sminshall  * Various utility routines.
15927110Sminshall  */
1606000Sroot 
16127186Sminshall char *ambiguous;		/* special return value */
16227186Sminshall #define Ambiguous(t)	((t)&ambiguous)
16327186Sminshall 
16427186Sminshall 
16527088Sminshall char **
16627088Sminshall genget(name, table, next)
16727088Sminshall char	*name;		/* name to match */
16827088Sminshall char	**table;		/* name entry in table */
16927088Sminshall char	**(*next)();	/* routine to return next entry in table */
1706000Sroot {
17127088Sminshall 	register char *p, *q;
17227088Sminshall 	register char **c, **found;
17327088Sminshall 	register int nmatches, longest;
1746000Sroot 
17527088Sminshall 	longest = 0;
17627088Sminshall 	nmatches = 0;
17727088Sminshall 	found = 0;
17827088Sminshall 	for (c = table; p = *c; c = (*next)(c)) {
17927088Sminshall 		for (q = name; *q == *p++; q++)
18027088Sminshall 			if (*q == 0)		/* exact match? */
18127088Sminshall 				return (c);
18227088Sminshall 		if (!*q) {			/* the name was a prefix */
18327088Sminshall 			if (q - name > longest) {
18427088Sminshall 				longest = q - name;
18527088Sminshall 				nmatches = 1;
18627088Sminshall 				found = c;
18727088Sminshall 			} else if (q - name == longest)
18827088Sminshall 				nmatches++;
1898377Ssam 		}
1906000Sroot 	}
19127088Sminshall 	if (nmatches > 1)
19227186Sminshall 		return Ambiguous(char **);
19327088Sminshall 	return (found);
1946000Sroot }
1956000Sroot 
19627110Sminshall /*
19727110Sminshall  * Make a character string into a number.
19827110Sminshall  *
19927186Sminshall  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
20027110Sminshall  */
2016000Sroot 
20227110Sminshall special(s)
20327110Sminshall register char *s;
20427110Sminshall {
20527110Sminshall 	register char c;
20627110Sminshall 	char b;
20727110Sminshall 
20827110Sminshall 	switch (*s) {
20927110Sminshall 	case '^':
21027110Sminshall 		b = *++s;
21127110Sminshall 		if (b == '?') {
212*27228Sminshall 		    c = b | 0x40;		/* DEL */
21327110Sminshall 		} else {
21427110Sminshall 		    c = b & 0x1f;
21527110Sminshall 		}
21627110Sminshall 		break;
21727110Sminshall 	default:
21827110Sminshall 		c = *s;
21927110Sminshall 		break;
22027110Sminshall 	}
22127110Sminshall 	return c;
22227110Sminshall }
22327186Sminshall 
22427186Sminshall /*
22527186Sminshall  * Construct a control character sequence
22627186Sminshall  * for a special character.
22727186Sminshall  */
22827186Sminshall char *
22927186Sminshall control(c)
23027186Sminshall 	register int c;
23127186Sminshall {
23227186Sminshall 	static char buf[3];
23327186Sminshall 
234*27228Sminshall 	if (c == 0x7f)
23527186Sminshall 		return ("^?");
23627186Sminshall 	if (c == '\377') {
23727186Sminshall 		return "off";
23827186Sminshall 	}
23927186Sminshall 	if (c >= 0x20) {
24027186Sminshall 		buf[0] = c;
24127186Sminshall 		buf[1] = 0;
24227186Sminshall 	} else {
24327186Sminshall 		buf[0] = '^';
24427186Sminshall 		buf[1] = '@'+c;
24527186Sminshall 		buf[2] = 0;
24627186Sminshall 	}
24727186Sminshall 	return (buf);
24827186Sminshall }
24927110Sminshall 
25027110Sminshall /*
25127186Sminshall  * Check to see if any out-of-band data exists on a socket (for
25227186Sminshall  * Telnet "synch" processing).
25327186Sminshall  */
25427186Sminshall 
25527186Sminshall int
25627186Sminshall stilloob(s)
25727186Sminshall int	s;		/* socket number */
25827186Sminshall {
25927186Sminshall     static struct timeval timeout = { 0 };
26027186Sminshall     fd_set	excepts;
26127186Sminshall     int value;
26227186Sminshall 
26327186Sminshall     do {
26427186Sminshall 	FD_ZERO(&excepts);
26527186Sminshall 	FD_SET(s, &excepts);
26627186Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
26727186Sminshall     } while ((value == -1) && (errno = EINTR));
26827186Sminshall 
26927186Sminshall     if (value < 0) {
27027186Sminshall 	perror("select");
27127186Sminshall 	quit();
27227186Sminshall     }
27327186Sminshall     if (FD_ISSET(s, &excepts)) {
27427186Sminshall 	return 1;
27527186Sminshall     } else {
27627186Sminshall 	return 0;
27727186Sminshall     }
27827186Sminshall }
27927186Sminshall 
28027186Sminshall 
28127186Sminshall /*
28227186Sminshall  *  netflush
28327186Sminshall  *		Send as much data as possible to the network,
28427186Sminshall  *	handling requests for urgent data.
28527186Sminshall  */
28627186Sminshall 
28727186Sminshall 
28827186Sminshall netflush(fd)
28927186Sminshall {
29027186Sminshall     int n;
29127186Sminshall 
29227186Sminshall     if ((n = nfrontp - nbackp) > 0) {
29327186Sminshall 	if (!neturg) {
29427186Sminshall 	    n = write(fd, nbackp, n);	/* normal write */
29527186Sminshall 	} else {
29627186Sminshall 	    n = neturg - nbackp;
29727186Sminshall 	    /*
29827186Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
29927186Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
30027186Sminshall 	     * To make ourselves compatible, we only send ONE byte
30127186Sminshall 	     * out of band, the one WE THINK should be OOB (though
30227186Sminshall 	     * we really have more the TCP philosophy of urgent data
30327186Sminshall 	     * rather than the Unix philosophy of OOB data).
30427186Sminshall 	     */
30527186Sminshall 	    if (n > 1) {
30627186Sminshall 		n = send(fd, nbackp, n-1, 0);	/* send URGENT all by itself */
30727186Sminshall 	    } else {
30827186Sminshall 		n = send(fd, nbackp, n, MSG_OOB);	/* URGENT data */
30927186Sminshall 	    }
31027186Sminshall 	}
31127186Sminshall     }
31227186Sminshall     if (n < 0) {
31327186Sminshall 	if (errno != ENOBUFS && errno != EWOULDBLOCK) {
31427186Sminshall 	    setcommandmode();
31527186Sminshall 	    perror(hostname);
31627186Sminshall 	    close(fd);
31727186Sminshall 	    neturg = 0;
31827186Sminshall 	    longjmp(peerdied, -1);
31927186Sminshall 	    /*NOTREACHED*/
32027186Sminshall 	}
32127186Sminshall 	n = 0;
32227186Sminshall     }
32327186Sminshall     if (netdata && n) {
32427186Sminshall 	Dump('>', nbackp, n);
32527186Sminshall     }
32627186Sminshall     nbackp += n;
32727186Sminshall     if (nbackp >= neturg) {
32827186Sminshall 	neturg = 0;
32927186Sminshall     }
33027186Sminshall     if (nbackp == nfrontp) {
33127186Sminshall 	nbackp = nfrontp = netobuf;
33227186Sminshall     }
33327186Sminshall }
33427186Sminshall 
33527186Sminshall /*
33627186Sminshall  * Send as much data as possible to the terminal.
33727186Sminshall  */
33827186Sminshall 
33927186Sminshall 
34027186Sminshall ttyflush()
34127186Sminshall {
34227186Sminshall     int n;
34327186Sminshall 
34427186Sminshall     if ((n = tfrontp - tbackp) > 0) {
345*27228Sminshall 	if (!(SYNCHing||flushout)) {
34627186Sminshall 	    n = write(tout, tbackp, n);
34727186Sminshall 	} else {
34827186Sminshall 	    ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
349*27228Sminshall 	    /* we leave 'n' alone! */
35027186Sminshall 	}
35127186Sminshall     }
35227186Sminshall     if (n < 0) {
35327186Sminshall 	return;
35427186Sminshall     }
35527186Sminshall     tbackp += n;
35627186Sminshall     if (tbackp == tfrontp) {
35727186Sminshall 	tbackp = tfrontp = ttyobuf;
35827186Sminshall     }
35927186Sminshall }
36027186Sminshall 
36127186Sminshall /*
36227110Sminshall  * Various signal handling routines.
36327110Sminshall  */
36427110Sminshall 
36527110Sminshall deadpeer()
36627110Sminshall {
36727110Sminshall 	setcommandmode();
36827110Sminshall 	longjmp(peerdied, -1);
36927110Sminshall }
37027110Sminshall 
37127110Sminshall intr()
37227110Sminshall {
37327110Sminshall     if (localsigs) {
37427110Sminshall 	intp();
37527110Sminshall 	return;
37627110Sminshall     }
37727110Sminshall     setcommandmode();
37827110Sminshall     longjmp(toplevel, -1);
37927110Sminshall }
38027110Sminshall 
38127110Sminshall intr2()
38227110Sminshall {
38327110Sminshall     if (localsigs) {
38427110Sminshall 	sendbrk();
38527110Sminshall 	return;
38627110Sminshall     }
38727110Sminshall }
38827110Sminshall 
38927110Sminshall doescape()
39027110Sminshall {
39127110Sminshall     command(0);
39227110Sminshall }
39327110Sminshall 
39427110Sminshall /*
39527186Sminshall  * The following are routines used to print out debugging information.
39627186Sminshall  */
39727186Sminshall 
39827186Sminshall 
39927186Sminshall static
40027186Sminshall Dump(direction, buffer, length)
40127186Sminshall char	direction;
40227186Sminshall char	*buffer;
40327186Sminshall int	length;
40427186Sminshall {
40527186Sminshall #   define BYTES_PER_LINE	32
40627186Sminshall #   define min(x,y)	((x<y)? x:y)
40727186Sminshall     char *pThis;
40827186Sminshall     int offset;
40927186Sminshall 
41027186Sminshall     offset = 0;
41127186Sminshall 
41227186Sminshall     while (length) {
41327186Sminshall 	/* print one line */
41427186Sminshall 	fprintf(NetTrace, "%c 0x%x\t", direction, offset);
41527186Sminshall 	pThis = buffer;
41627186Sminshall 	buffer = buffer+min(length, BYTES_PER_LINE);
41727186Sminshall 	while (pThis < buffer) {
41827186Sminshall 	    fprintf(NetTrace, "%.2x", (*pThis)&0xff);
41927186Sminshall 	    pThis++;
42027186Sminshall 	}
42127186Sminshall 	fprintf(NetTrace, "\n");
42227186Sminshall 	length -= BYTES_PER_LINE;
42327186Sminshall 	offset += BYTES_PER_LINE;
42427186Sminshall 	if (length < 0) {
42527186Sminshall 	    return;
42627186Sminshall 	}
42727186Sminshall 	/* find next unique line */
42827186Sminshall     }
42927186Sminshall }
43027186Sminshall 
43127186Sminshall 
43227186Sminshall /*VARARGS*/
43327186Sminshall printoption(direction, fmt, option, what)
43427186Sminshall 	char *direction, *fmt;
43527186Sminshall 	int option, what;
43627186Sminshall {
43727186Sminshall 	if (!showoptions)
43827186Sminshall 		return;
43927186Sminshall 	printf("%s ", direction);
44027186Sminshall 	if (fmt == doopt)
44127186Sminshall 		fmt = "do";
44227186Sminshall 	else if (fmt == dont)
44327186Sminshall 		fmt = "dont";
44427186Sminshall 	else if (fmt == will)
44527186Sminshall 		fmt = "will";
44627186Sminshall 	else if (fmt == wont)
44727186Sminshall 		fmt = "wont";
44827186Sminshall 	else
44927186Sminshall 		fmt = "???";
45027186Sminshall 	if (option < TELOPT_SUPDUP)
45127186Sminshall 		printf("%s %s", fmt, telopts[option]);
45227186Sminshall 	else
45327186Sminshall 		printf("%s %d", fmt, option);
45427186Sminshall 	if (*direction == '<') {
45527186Sminshall 		printf("\r\n");
45627186Sminshall 		return;
45727186Sminshall 	}
45827186Sminshall 	printf(" (%s)\r\n", what ? "reply" : "don't reply");
45927186Sminshall }
46027186Sminshall 
46127186Sminshall /*
46227110Sminshall  * Mode - set up terminal to a specific mode.
46327110Sminshall  */
46427110Sminshall 
4659972Ssam 
4666000Sroot mode(f)
4676000Sroot 	register int f;
4686000Sroot {
4698378Ssam 	static int prevmode = 0;
47013076Ssam 	struct tchars *tc;
47113076Ssam 	struct ltchars *ltc;
47213076Ssam 	struct sgttyb sb;
47313076Ssam 	int onoff, old;
474*27228Sminshall 	struct	tchars notc2;
475*27228Sminshall 	struct	ltchars noltc2;
476*27228Sminshall 	static struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
477*27228Sminshall 	static struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
4786000Sroot 
47927110Sminshall 	globalmode = f;
4808378Ssam 	if (prevmode == f)
48127186Sminshall 		return;
4828378Ssam 	old = prevmode;
4838378Ssam 	prevmode = f;
48427110Sminshall 	sb = nttyb;
4856000Sroot 	switch (f) {
4868378Ssam 
4876000Sroot 	case 0:
4886000Sroot 		onoff = 0;
4899972Ssam 		tc = &otc;
49013076Ssam 		ltc = &oltc;
4916000Sroot 		break;
4926000Sroot 
49327110Sminshall 	case 1:		/* remote character processing, remote echo */
49427110Sminshall 	case 2:		/* remote character processing, local echo */
49513076Ssam 		sb.sg_flags |= CBREAK;
4968378Ssam 		if (f == 1)
49713076Ssam 			sb.sg_flags &= ~(ECHO|CRMOD);
4988378Ssam 		else
49913076Ssam 			sb.sg_flags |= ECHO|CRMOD;
50013076Ssam 		sb.sg_erase = sb.sg_kill = -1;
5019972Ssam 		tc = &notc;
50227110Sminshall 		/*
50327110Sminshall 		 * If user hasn't specified one way or the other,
50427110Sminshall 		 * then default to not trapping signals.
50527110Sminshall 		 */
506*27228Sminshall 		if (!donelclsigs) {
50727110Sminshall 			localsigs = 0;
508*27228Sminshall 		}
50927110Sminshall 		if (localsigs) {
51027110Sminshall 			notc2 = notc;
51127110Sminshall 			notc2.t_intrc = ntc.t_intrc;
51227110Sminshall 			notc2.t_quitc = ntc.t_quitc;
51327110Sminshall 			tc = &notc2;
51427110Sminshall 		} else
51527110Sminshall 			tc = &notc;
51613076Ssam 		ltc = &noltc;
5176000Sroot 		onoff = 1;
5189972Ssam 		break;
51927110Sminshall 	case 3:		/* local character processing, remote echo */
52027110Sminshall 	case 4:		/* local character processing, local echo */
52127110Sminshall 	case 5:		/* local character processing, no echo */
52227110Sminshall 		sb.sg_flags &= ~CBREAK;
52327110Sminshall 		sb.sg_flags |= CRMOD;
52427110Sminshall 		if (f == 4)
52527110Sminshall 			sb.sg_flags |= ECHO;
52627110Sminshall 		else
52727110Sminshall 			sb.sg_flags &= ~ECHO;
528*27228Sminshall 		notc2 = ntc;
529*27228Sminshall 		tc = &notc2;
530*27228Sminshall 		noltc2 = oltc;
531*27228Sminshall 		ltc = &noltc2;
53227110Sminshall 		/*
53327110Sminshall 		 * If user hasn't specified one way or the other,
53427110Sminshall 		 * then default to trapping signals.
53527110Sminshall 		 */
536*27228Sminshall 		if (!donelclsigs) {
53727110Sminshall 			localsigs = 1;
538*27228Sminshall 		}
539*27228Sminshall 		if (localsigs) {
540*27228Sminshall 			notc2.t_brkc = nltc.t_flushc;
541*27228Sminshall 			noltc2.t_flushc = -1;
542*27228Sminshall 		} else {
54327110Sminshall 			notc2.t_intrc = notc2.t_quitc = -1;
54427110Sminshall 		}
54527110Sminshall 		noltc2.t_suspc = escape;
54627110Sminshall 		noltc2.t_dsuspc = -1;
54727110Sminshall 		onoff = 1;
54827110Sminshall 		break;
5499972Ssam 
5509972Ssam 	default:
5519972Ssam 		return;
5526000Sroot 	}
55313076Ssam 	ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
55413076Ssam 	ioctl(fileno(stdin), TIOCSETC, (char *)tc);
55513076Ssam 	ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
55627186Sminshall 	ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
55727186Sminshall 	ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
55827110Sminshall 	if (f >= 3)
55927110Sminshall 		signal(SIGTSTP, doescape);
56027110Sminshall 	else if (old >= 3) {
56127110Sminshall 		signal(SIGTSTP, SIG_DFL);
56227110Sminshall 		sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
56327110Sminshall 	}
5646000Sroot }
56527186Sminshall 
56627110Sminshall /*
56727110Sminshall  * These routines decides on what the mode should be (based on the values
56827110Sminshall  * of various global variables).
56927110Sminshall  */
57027110Sminshall 
57127178Sminshall char *modedescriptions[] = {
57227178Sminshall 	"telnet command mode",					/* 0 */
57327178Sminshall 	"character-at-a-time mode",				/* 1 */
57427178Sminshall 	"character-at-a-time mode (local echo)",		/* 2 */
57527178Sminshall 	"line-by-line mode (remote echo)",			/* 3 */
57627178Sminshall 	"line-by-line mode",					/* 4 */
57727178Sminshall 	"line-by-line mode (local echoing suppressed)",		/* 5 */
57827178Sminshall };
57927178Sminshall 
58027178Sminshall getconnmode()
58127110Sminshall {
58227110Sminshall     static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
58327186Sminshall     int modeindex = 0;
58427110Sminshall 
58527110Sminshall     if (hisopts[TELOPT_ECHO]) {
58627186Sminshall 	modeindex += 2;
58727110Sminshall     }
58827110Sminshall     if (hisopts[TELOPT_SGA]) {
58927186Sminshall 	modeindex += 4;
59027110Sminshall     }
59127186Sminshall     if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
59227186Sminshall 	modeindex += 1;
59327110Sminshall     }
59427186Sminshall     return newmode[modeindex];
59527110Sminshall }
59627110Sminshall 
59727178Sminshall setconnmode()
59827178Sminshall {
59927178Sminshall     mode(getconnmode());
60027178Sminshall }
60127110Sminshall 
60227178Sminshall 
60327110Sminshall setcommandmode()
60427110Sminshall {
60527110Sminshall     mode(0);
60627110Sminshall }
60727110Sminshall 
6086000Sroot char	sibuf[BUFSIZ], *sbp;
6096000Sroot char	tibuf[BUFSIZ], *tbp;
6106000Sroot int	scc, tcc;
6116000Sroot 
612*27228Sminshall 
6136000Sroot /*
6146000Sroot  * Select from tty and network...
6156000Sroot  */
61627088Sminshall telnet()
6176000Sroot {
6186000Sroot 	register int c;
61927088Sminshall 	int tin = fileno(stdin);
6206000Sroot 	int on = 1;
6216000Sroot 
62227088Sminshall 	tout = fileno(stdout);
62327110Sminshall 	setconnmode();
624*27228Sminshall 	scc = 0;
625*27228Sminshall 	tcc = 0;
62627186Sminshall 	ioctl(net, FIONBIO, (char *)&on);
627*27228Sminshall #if	defined(xxxSO_OOBINLINE)
628*27228Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, on, sizeof on);
629*27228Sminshall #endif	/* defined(xxxSO_OOBINLINE) */
63027088Sminshall 	if (telnetport && !hisopts[TELOPT_SGA]) {
63117922Sralph 		willoption(TELOPT_SGA);
63227088Sminshall 	}
6336000Sroot 	for (;;) {
63427110Sminshall 		fd_set ibits, obits, xbits;
6356000Sroot 
63627110Sminshall 		if (scc < 0 && tcc < 0) {
6376000Sroot 			break;
63827110Sminshall 		}
63927110Sminshall 
64027110Sminshall 		FD_ZERO(&ibits);
64127110Sminshall 		FD_ZERO(&obits);
64227110Sminshall 		FD_ZERO(&xbits);
64327110Sminshall 
644*27228Sminshall 		if (((globalmode < 4) || flushline) && NETBYTES()) {
64527110Sminshall 			FD_SET(net, &obits);
64627088Sminshall 		} else {
64727110Sminshall 			FD_SET(tin, &ibits);
64827088Sminshall 		}
649*27228Sminshall 		if (TTYBYTES()) {
65027110Sminshall 			FD_SET(tout, &obits);
65127110Sminshall 		} else {
65227110Sminshall 			FD_SET(net, &ibits);
65327110Sminshall 		}
65427186Sminshall 		if (!SYNCHing) {
65527110Sminshall 			FD_SET(net, &xbits);
65627110Sminshall 		}
65727186Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
65827186Sminshall 						(struct timeval *)0)) < 1) {
65927110Sminshall 			if (c == -1) {
66027110Sminshall 				/*
66127110Sminshall 				 * we can get EINTR if we are in line mode,
66227110Sminshall 				 * and the user does an escape (TSTP), or
66327110Sminshall 				 * some other signal generator.
66427110Sminshall 				 */
66527110Sminshall 				if (errno == EINTR) {
66627110Sminshall 					continue;
66727110Sminshall 				}
66827110Sminshall 			}
6696000Sroot 			sleep(5);
6706000Sroot 			continue;
6716000Sroot 		}
6726000Sroot 
6736000Sroot 		/*
67427088Sminshall 		 * Any urgent data?
67527088Sminshall 		 */
67627110Sminshall 		if (FD_ISSET(net, &xbits)) {
67727186Sminshall 		    SYNCHing = 1;
67827088Sminshall 		    ttyflush();	/* flush already enqueued data */
67927088Sminshall 		}
68027088Sminshall 
68127088Sminshall 		/*
6826000Sroot 		 * Something to read from the network...
6836000Sroot 		 */
68427110Sminshall 		if (FD_ISSET(net, &ibits)) {
685*27228Sminshall 			int canread;
686*27228Sminshall 
687*27228Sminshall 			if (scc == 0) {
688*27228Sminshall 			    sbp = sibuf;
689*27228Sminshall 			}
690*27228Sminshall 			canread = sibuf + sizeof sibuf - sbp;
691*27228Sminshall #if	!defined(xxxSO_OOBINLINE)
69227178Sminshall 			/*
69327178Sminshall 			 * In 4.2 (and some early 4.3) systems, the
69427178Sminshall 			 * OOB indication and data handling in the kernel
69527178Sminshall 			 * is such that if two separate TCP Urgent requests
69627178Sminshall 			 * come in, one byte of TCP data will be overlaid.
69727178Sminshall 			 * This is fatal for Telnet, but we try to live
69827178Sminshall 			 * with it.
69927178Sminshall 			 *
70027178Sminshall 			 * In addition, in 4.2 (and...), a special protocol
70127178Sminshall 			 * is needed to pick up the TCP Urgent data in
70227178Sminshall 			 * the correct sequence.
70327178Sminshall 			 *
70427178Sminshall 			 * What we do is:  if we think we are in urgent
70527178Sminshall 			 * mode, we look to see if we are "at the mark".
70627178Sminshall 			 * If we are, we do an OOB receive.  If we run
70727178Sminshall 			 * this twice, we will do the OOB receive twice,
70827178Sminshall 			 * but the second will fail, since the second
70927178Sminshall 			 * time we were "at the mark", but there wasn't
71027178Sminshall 			 * any data there (the kernel doesn't reset
71127178Sminshall 			 * "at the mark" until we do a normal read).
71227178Sminshall 			 * Once we've read the OOB data, we go ahead
71327178Sminshall 			 * and do normal reads.
71427178Sminshall 			 *
71527178Sminshall 			 * There is also another problem, which is that
71627178Sminshall 			 * since the OOB byte we read doesn't put us
71727178Sminshall 			 * out of OOB state, and since that byte is most
71827178Sminshall 			 * likely the TELNET DM (data mark), we would
71927186Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
72027178Sminshall 			 * So, clocks to the rescue.  If we've "just"
72127178Sminshall 			 * received a DM, then we test for the
72227178Sminshall 			 * presence of OOB data when the receive OOB
72327178Sminshall 			 * fails (and AFTER we did the normal mode read
72427178Sminshall 			 * to clear "at the mark").
72527178Sminshall 			 */
72627186Sminshall 		    if (SYNCHing) {
72727178Sminshall 			int atmark;
72827178Sminshall 
72927186Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
73027178Sminshall 			if (atmark) {
731*27228Sminshall 			    c = recv(net, sibuf, canread, MSG_OOB);
732*27228Sminshall 			    if ((c == -1) && (errno == EINVAL)) {
733*27228Sminshall 				c = read(net, sibuf, canread);
73427186Sminshall 				if (clocks.didnetreceive < clocks.gotDM) {
73527186Sminshall 				    SYNCHing = stilloob(net);
73627021Sminshall 				}
73727178Sminshall 			    }
73827178Sminshall 			} else {
739*27228Sminshall 			    c = read(net, sibuf, canread);
7406000Sroot 			}
74127178Sminshall 		    } else {
742*27228Sminshall 			c = read(net, sibuf, canread);
74327178Sminshall 		    }
74427178Sminshall 		    settimer(didnetreceive);
745*27228Sminshall #else	/* !defined(xxxSO_OOBINLINE) */
746*27228Sminshall 		    c = read(net, sbp, canread);
747*27228Sminshall #endif	/* !defined(xxxSO_OOBINLINE) */
748*27228Sminshall 		    if (c < 0 && errno == EWOULDBLOCK) {
749*27228Sminshall 			c = 0;
750*27228Sminshall 		    } else if (c <= 0) {
751*27228Sminshall 			break;
75227178Sminshall 		    }
753*27228Sminshall 		    if (netdata) {
754*27228Sminshall 			Dump('<', sbp, c);
755*27228Sminshall 		    }
756*27228Sminshall 		    scc += c;
7576000Sroot 		}
7586000Sroot 
7596000Sroot 		/*
7606000Sroot 		 * Something to read from the tty...
7616000Sroot 		 */
76227110Sminshall 		if (FD_ISSET(tin, &ibits)) {
763*27228Sminshall 			if (tcc == 0) {
764*27228Sminshall 			    tbp = tibuf;	/* nothing left, reset */
7656000Sroot 			}
766*27228Sminshall 			c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
767*27228Sminshall 			if (c < 0 && errno == EWOULDBLOCK) {
768*27228Sminshall 				c = 0;
769*27228Sminshall 			} else if (c <= 0) {
770*27228Sminshall 				tcc = c;
771*27228Sminshall 				break;
772*27228Sminshall 			}
773*27228Sminshall 			tcc += c;
7746000Sroot 		}
7756000Sroot 
7766000Sroot 		while (tcc > 0) {
77727186Sminshall 			register int sc;
7786000Sroot 
779*27228Sminshall 			if (NETROOM() < 2) {
78027110Sminshall 				flushline = 1;
7816000Sroot 				break;
78227110Sminshall 			}
78327186Sminshall 			c = *tbp++ & 0xff, sc = strip(c), tcc--;
78427186Sminshall 			if (sc == escape) {
7856000Sroot 				command(0);
7866000Sroot 				tcc = 0;
78727110Sminshall 				flushline = 1;
7886000Sroot 				break;
78927110Sminshall 			} else if ((globalmode >= 4) && doechocharrecognition &&
79027186Sminshall 							(sc == echoc)) {
79127110Sminshall 				if (tcc > 0 && strip(*tbp) == echoc) {
79227110Sminshall 					tbp++;
79327110Sminshall 					tcc--;
79427110Sminshall 				} else {
79527110Sminshall 					dontlecho = !dontlecho;
79627110Sminshall 					settimer(echotoggle);
79727110Sminshall 					setconnmode();
79827110Sminshall 					tcc = 0;
79927110Sminshall 					flushline = 1;
80027110Sminshall 					break;
80127110Sminshall 				}
8026000Sroot 			}
80327110Sminshall 			if (localsigs) {
80427186Sminshall 				if (sc == ntc.t_intrc) {
80527110Sminshall 					intp();
80627110Sminshall 					break;
80727186Sminshall 				} else if (sc == ntc.t_quitc) {
80827110Sminshall 					sendbrk();
80927110Sminshall 					break;
810*27228Sminshall 				} else if (sc == nltc.t_flushc) {
811*27228Sminshall 					NET2ADD(IAC, AO);
812*27228Sminshall 					if (autoflush) {
813*27228Sminshall 					    doflush();
814*27228Sminshall 					}
815*27228Sminshall 					break;
81627110Sminshall 				} else if (globalmode > 2) {
81727110Sminshall 					;
81827186Sminshall 				} else if (sc == nttyb.sg_kill) {
81927110Sminshall 					NET2ADD(IAC, EL);
82027110Sminshall 					break;
82127186Sminshall 				} else if (sc == nttyb.sg_erase) {
82227110Sminshall 					NET2ADD(IAC, EC);
82327110Sminshall 					break;
82427110Sminshall 				}
82527110Sminshall 			}
82617922Sralph 			switch (c) {
82717922Sralph 			case '\n':
82827021Sminshall 				/*
82927021Sminshall 				 * If echoing is happening locally,
83027021Sminshall 				 * then a newline (unix) is CRLF (TELNET).
83127021Sminshall 				 */
83227088Sminshall 				if (!hisopts[TELOPT_ECHO]) {
83327088Sminshall 					NETADD('\r');
83427088Sminshall 				}
83527088Sminshall 				NETADD('\n');
83627110Sminshall 				flushline = 1;
83717922Sralph 				break;
83817922Sralph 			case '\r':
83927088Sminshall 				NET2ADD('\r', '\0');
84027110Sminshall 				flushline = 1;
84117922Sralph 				break;
84217922Sralph 			case IAC:
84327088Sminshall 				NET2ADD(IAC, IAC);
84427021Sminshall 				break;
84517922Sralph 			default:
84627088Sminshall 				NETADD(c);
84717922Sralph 				break;
84817922Sralph 			}
8496000Sroot 		}
85027110Sminshall 		if (((globalmode < 4) || flushline) &&
851*27228Sminshall 		    FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
85227088Sminshall 			netflush(net);
85327110Sminshall 		}
8546000Sroot 		if (scc > 0)
8556000Sroot 			telrcv();
856*27228Sminshall 		if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0))
85727088Sminshall 			ttyflush();
8586000Sroot 	}
85927110Sminshall 	setcommandmode();
8606000Sroot }
86127110Sminshall 
8626000Sroot /*
8636000Sroot  * Telnet receiver states for fsm
8646000Sroot  */
8656000Sroot #define	TS_DATA		0
8666000Sroot #define	TS_IAC		1
8676000Sroot #define	TS_WILL		2
8686000Sroot #define	TS_WONT		3
8696000Sroot #define	TS_DO		4
8706000Sroot #define	TS_DONT		5
87127021Sminshall #define	TS_CR		6
8726000Sroot 
8736000Sroot telrcv()
8746000Sroot {
8756000Sroot 	register int c;
8766000Sroot 	static int state = TS_DATA;
8776000Sroot 
878*27228Sminshall 	while ((scc > 0) && (TTYROOM() > 2)) {
87927186Sminshall 		c = *sbp++ & 0xff, scc--;
8806000Sroot 		switch (state) {
8816000Sroot 
88227021Sminshall 		case TS_CR:
88327021Sminshall 			state = TS_DATA;
884*27228Sminshall 			if (c == '\0') {
885*27228Sminshall 			    break;	/* Ignore \0 after CR */
886*27228Sminshall 			} else if (c == '\n') {
887*27228Sminshall 			    if (hisopts[TELOPT_ECHO] && !crmod) {
888*27228Sminshall 				TTYADD(c);
889*27228Sminshall 			    }
890*27228Sminshall 			    break;
89127021Sminshall 			}
892*27228Sminshall 			/* Else, fall through */
89327021Sminshall 
8946000Sroot 		case TS_DATA:
8959972Ssam 			if (c == IAC) {
8966000Sroot 				state = TS_IAC;
8979972Ssam 				continue;
8989972Ssam 			}
899*27228Sminshall 			    /*
900*27228Sminshall 			     * The 'crmod' hack (see following) is needed
901*27228Sminshall 			     * since we can't * set CRMOD on output only.
902*27228Sminshall 			     * Machines like MULTICS like to send \r without
903*27228Sminshall 			     * \n; since we must turn off CRMOD to get proper
904*27228Sminshall 			     * input, the mapping is done here (sigh).
905*27228Sminshall 			     */
90627021Sminshall 			if (c == '\r') {
90727021Sminshall 				if (scc > 0) {
90827186Sminshall 					c = *sbp&0xff;
90927021Sminshall 					if (c == 0) {
91027021Sminshall 						sbp++, scc--;
911*27228Sminshall 						/* a "true" CR */
91227088Sminshall 						TTYADD('\r');
91327021Sminshall 					} else if (!hisopts[TELOPT_ECHO] &&
91427021Sminshall 								(c == '\n')) {
91527021Sminshall 						sbp++, scc--;
91627088Sminshall 						TTYADD('\n');
91727021Sminshall 					} else {
91827088Sminshall 						TTYADD('\r');
919*27228Sminshall 						if (crmod) {
920*27228Sminshall 							TTYADD('\n');
921*27228Sminshall 						}
92227021Sminshall 					}
92327021Sminshall 				} else {
92427021Sminshall 					state = TS_CR;
92527088Sminshall 					TTYADD('\r');
926*27228Sminshall 					if (crmod) {
927*27228Sminshall 						TTYADD('\n');
928*27228Sminshall 					}
92927021Sminshall 				}
93027021Sminshall 			} else {
93127088Sminshall 				TTYADD(c);
93227021Sminshall 			}
9336000Sroot 			continue;
9346000Sroot 
9356000Sroot 		case TS_IAC:
9366000Sroot 			switch (c) {
9376000Sroot 
9386000Sroot 			case WILL:
9396000Sroot 				state = TS_WILL;
9406000Sroot 				continue;
9416000Sroot 
9426000Sroot 			case WONT:
9436000Sroot 				state = TS_WONT;
9446000Sroot 				continue;
9456000Sroot 
9466000Sroot 			case DO:
9476000Sroot 				state = TS_DO;
9486000Sroot 				continue;
9496000Sroot 
9506000Sroot 			case DONT:
9516000Sroot 				state = TS_DONT;
9526000Sroot 				continue;
9536000Sroot 
9546000Sroot 			case DM:
95527088Sminshall 				/*
95627088Sminshall 				 * We may have missed an urgent notification,
95727088Sminshall 				 * so make sure we flush whatever is in the
95827088Sminshall 				 * buffer currently.
95927088Sminshall 				 */
96027186Sminshall 				SYNCHing = 1;
96127088Sminshall 				ttyflush();
96227186Sminshall 				SYNCHing = stilloob(net);
96327178Sminshall 				settimer(gotDM);
9646000Sroot 				break;
9656000Sroot 
9666000Sroot 			case NOP:
9676000Sroot 			case GA:
9686000Sroot 				break;
9696000Sroot 
9706000Sroot 			default:
9716000Sroot 				break;
9726000Sroot 			}
9736000Sroot 			state = TS_DATA;
9746000Sroot 			continue;
9756000Sroot 
9766000Sroot 		case TS_WILL:
9778345Ssam 			printoption("RCVD", will, c, !hisopts[c]);
97827110Sminshall 			if (c == TELOPT_TM) {
97927110Sminshall 				if (flushout) {
98027186Sminshall 					flushout = 0;
98127110Sminshall 				}
98227110Sminshall 			} else if (!hisopts[c]) {
9836000Sroot 				willoption(c);
98427110Sminshall 			}
9856000Sroot 			state = TS_DATA;
9866000Sroot 			continue;
9876000Sroot 
9886000Sroot 		case TS_WONT:
9898345Ssam 			printoption("RCVD", wont, c, hisopts[c]);
99027110Sminshall 			if (c == TELOPT_TM) {
99127110Sminshall 				if (flushout) {
99227186Sminshall 					flushout = 0;
99327110Sminshall 				}
99427110Sminshall 			} else if (hisopts[c]) {
9956000Sroot 				wontoption(c);
99627110Sminshall 			}
9976000Sroot 			state = TS_DATA;
9986000Sroot 			continue;
9996000Sroot 
10006000Sroot 		case TS_DO:
10018345Ssam 			printoption("RCVD", doopt, c, !myopts[c]);
10026000Sroot 			if (!myopts[c])
10036000Sroot 				dooption(c);
10046000Sroot 			state = TS_DATA;
10056000Sroot 			continue;
10066000Sroot 
10076000Sroot 		case TS_DONT:
10088345Ssam 			printoption("RCVD", dont, c, myopts[c]);
10096000Sroot 			if (myopts[c]) {
10106000Sroot 				myopts[c] = 0;
10116000Sroot 				sprintf(nfrontp, wont, c);
10128378Ssam 				nfrontp += sizeof (wont) - 2;
101327110Sminshall 				flushline = 1;
101427110Sminshall 				setconnmode();	/* set new tty mode (maybe) */
10158345Ssam 				printoption("SENT", wont, c);
10166000Sroot 			}
10176000Sroot 			state = TS_DATA;
10186000Sroot 			continue;
10196000Sroot 		}
10206000Sroot 	}
10216000Sroot }
102227110Sminshall 
10236000Sroot willoption(option)
10246000Sroot 	int option;
10256000Sroot {
10266000Sroot 	char *fmt;
10276000Sroot 
10286000Sroot 	switch (option) {
10296000Sroot 
10306000Sroot 	case TELOPT_ECHO:
10316000Sroot 	case TELOPT_SGA:
103227110Sminshall 		settimer(modenegotiated);
10336000Sroot 		hisopts[option] = 1;
10346000Sroot 		fmt = doopt;
103527110Sminshall 		setconnmode();		/* possibly set new tty mode */
10366000Sroot 		break;
10376000Sroot 
10386000Sroot 	case TELOPT_TM:
103927110Sminshall 		return;			/* Never reply to TM will's/wont's */
10406000Sroot 
10416000Sroot 	default:
10426000Sroot 		fmt = dont;
10436000Sroot 		break;
10446000Sroot 	}
10456024Ssam 	sprintf(nfrontp, fmt, option);
10468378Ssam 	nfrontp += sizeof (dont) - 2;
10478345Ssam 	printoption("SENT", fmt, option);
10486000Sroot }
10496000Sroot 
10506000Sroot wontoption(option)
10516000Sroot 	int option;
10526000Sroot {
10536000Sroot 	char *fmt;
10546000Sroot 
10556000Sroot 	switch (option) {
10566000Sroot 
10576000Sroot 	case TELOPT_ECHO:
10586000Sroot 	case TELOPT_SGA:
105927110Sminshall 		settimer(modenegotiated);
10606000Sroot 		hisopts[option] = 0;
10616000Sroot 		fmt = dont;
106227110Sminshall 		setconnmode();			/* Set new tty mode */
10636000Sroot 		break;
10646000Sroot 
106527110Sminshall 	case TELOPT_TM:
106627110Sminshall 		return;		/* Never reply to TM will's/wont's */
106727110Sminshall 
10686000Sroot 	default:
10696000Sroot 		fmt = dont;
10706000Sroot 	}
10716000Sroot 	sprintf(nfrontp, fmt, option);
10728378Ssam 	nfrontp += sizeof (doopt) - 2;
10738345Ssam 	printoption("SENT", fmt, option);
10746000Sroot }
10756000Sroot 
10766000Sroot dooption(option)
10776000Sroot 	int option;
10786000Sroot {
10796000Sroot 	char *fmt;
10806000Sroot 
10816000Sroot 	switch (option) {
10826000Sroot 
10836000Sroot 	case TELOPT_TM:
108413231Ssam 		fmt = will;
108513231Ssam 		break;
108613231Ssam 
108727110Sminshall 	case TELOPT_SGA:		/* no big deal */
10886000Sroot 		fmt = will;
108927110Sminshall 		myopts[option] = 1;
10906000Sroot 		break;
10916000Sroot 
109227110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
10936000Sroot 	default:
10946000Sroot 		fmt = wont;
10956000Sroot 		break;
10966000Sroot 	}
10976000Sroot 	sprintf(nfrontp, fmt, option);
10988378Ssam 	nfrontp += sizeof (doopt) - 2;
10998345Ssam 	printoption("SENT", fmt, option);
11006000Sroot }
110127110Sminshall 
11026000Sroot /*
110327088Sminshall  *	The following are data structures and routines for
110427088Sminshall  *	the "send" command.
110527088Sminshall  *
110627088Sminshall  */
110727088Sminshall 
110827088Sminshall struct sendlist {
110927088Sminshall     char	*name;		/* How user refers to it (case independent) */
111027088Sminshall     int		what;		/* Character to be sent (<0 ==> special) */
111127088Sminshall     char	*help;		/* Help information (0 ==> no help) */
111227088Sminshall     int		(*routine)();	/* Routine to perform (for special ops) */
111327088Sminshall };
111427088Sminshall 
111527186Sminshall /*ARGSUSED*/
111627088Sminshall dosynch(s)
111727088Sminshall struct sendlist *s;
111827088Sminshall {
111927088Sminshall     /* XXX We really should purge the buffer to the network */
112027088Sminshall     NET2ADD(IAC, DM);
112127186Sminshall     neturg = NETLOC()-1;	/* Some systems are off by one XXX */
112227088Sminshall }
112327088Sminshall 
1124*27228Sminshall doflush()
1125*27228Sminshall {
1126*27228Sminshall     /* This shouldn't really be here... */
1127*27228Sminshall     NET2ADD(IAC, DO);
1128*27228Sminshall     NETADD(TELOPT_TM);
1129*27228Sminshall     printoption("SENT", doopt, TELOPT_TM);
1130*27228Sminshall     flushline = 1;
1131*27228Sminshall     flushout = 1;
1132*27228Sminshall     ttyflush();
1133*27228Sminshall }
1134*27228Sminshall 
113527088Sminshall intp()
113627088Sminshall {
113727110Sminshall     NET2ADD(IAC, IP);
1138*27228Sminshall     if (autoflush) {
1139*27228Sminshall 	doflush();
1140*27228Sminshall     }
1141*27228Sminshall     if (autosynch) {
1142*27228Sminshall 	dosynch();
1143*27228Sminshall     }
114427088Sminshall }
114527088Sminshall 
114627110Sminshall sendbrk()
114727110Sminshall {
114827186Sminshall     NET2ADD(IAC, BREAK);
1149*27228Sminshall     if (autoflush) {
1150*27228Sminshall 	doflush();
1151*27228Sminshall     }
1152*27228Sminshall     if (autosynch) {
1153*27228Sminshall 	dosynch();
1154*27228Sminshall     }
115527110Sminshall }
115627088Sminshall 
115727110Sminshall 
115827088Sminshall #define	SENDQUESTION	-1
115927088Sminshall #define	SEND2QUESTION	-2
116027088Sminshall #define	SENDESCAPE	-3
116127088Sminshall 
116227088Sminshall struct sendlist Sendlist[] = {
116327088Sminshall     { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
116427088Sminshall     { "brk", BREAK, "Send Telnet Break" },
116527088Sminshall 	{ "break", BREAK, 0 },
116627088Sminshall     { "ip", IP, "Send Telnet Interrupt Process" },
116727088Sminshall 	{ "intp", IP, 0 },
116827088Sminshall 	{ "interrupt", IP, 0 },
116927088Sminshall 	{ "intr", IP, 0 },
117027088Sminshall     { "ao", AO, "Send Telnet Abort output" },
117127088Sminshall 	{ "abort", AO, 0 },
117227088Sminshall     { "ayt", AYT, "Send Telnet 'Are You There'" },
117327088Sminshall 	{ "are", AYT, 0 },
117427088Sminshall 	{ "hello", AYT, 0 },
117527088Sminshall     { "ec", EC, "Send Telnet Erase Character" },
117627088Sminshall     { "el", EL, "Send Telnet Erase Line" },
117727088Sminshall     { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
117827088Sminshall 	{ "go", GA, 0 },
117927088Sminshall     { "nop", NOP, "Send Telnet 'No operation'" },
118027088Sminshall     { "escape", SENDESCAPE, "Send current escape character" },
118127088Sminshall     { "?", SENDQUESTION, "Display send options" },
118227088Sminshall 	{ "help", SENDQUESTION, 0 },
118327088Sminshall     { "??", SEND2QUESTION, "Display all send options (including aliases)" },
118427088Sminshall     { 0 }
118527088Sminshall };
118627088Sminshall 
118727088Sminshall char **
118827088Sminshall getnextsend(name)
118927088Sminshall char *name;
119027088Sminshall {
119127088Sminshall     struct sendlist *c = (struct sendlist *) name;
119227088Sminshall 
119327088Sminshall     return (char **) (c+1);
119427088Sminshall }
119527088Sminshall 
119627088Sminshall struct sendlist *
119727088Sminshall getsend(name)
119827088Sminshall char *name;
119927088Sminshall {
120027088Sminshall     return (struct sendlist *) genget(name, (char **) Sendlist, getnextsend);
120127088Sminshall }
120227088Sminshall 
120327088Sminshall sendcmd(argc, argv)
120427088Sminshall int	argc;
120527088Sminshall char	**argv;
120627088Sminshall {
120727088Sminshall     int what;		/* what we are sending this time */
120827088Sminshall     int count;		/* how many bytes we are going to need to send */
120927088Sminshall     int hadsynch;	/* are we going to process a "synch"? */
121027088Sminshall     int i;
121127088Sminshall     struct sendlist *s;	/* pointer to current command */
121227088Sminshall 
121327088Sminshall     if (argc < 2) {
121427088Sminshall 	printf("need at least one argument for 'send' command\n");
121527088Sminshall 	printf("'send ?' for help\n");
121627088Sminshall 	return;
121727088Sminshall     }
121827088Sminshall     /*
121927088Sminshall      * First, validate all the send arguments.
122027088Sminshall      * In addition, we see how much space we are going to need, and
122127088Sminshall      * whether or not we will be doing a "SYNCH" operation (which
122227088Sminshall      * flushes the network queue).
122327088Sminshall      */
122427088Sminshall     count = 0;
122527088Sminshall     hadsynch = 0;
122627088Sminshall     for (i = 1; i < argc; i++) {
122727088Sminshall 	s = getsend(argv[i]);
122827088Sminshall 	if (s == 0) {
122927088Sminshall 	    printf("Unknown send argument '%s'\n'send ?' for help.\n",
123027088Sminshall 			argv[i]);
123127088Sminshall 	    return;
123227186Sminshall 	} else if (s == Ambiguous(struct sendlist *)) {
123327088Sminshall 	    printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
123427088Sminshall 			argv[i]);
123527088Sminshall 	    return;
123627088Sminshall 	}
123727088Sminshall 	switch (s->what) {
123827088Sminshall 	case SENDQUESTION:
123927088Sminshall 	case SEND2QUESTION:
124027088Sminshall 	    break;
124127088Sminshall 	case SENDESCAPE:
124227088Sminshall 	    count += 1;
124327088Sminshall 	    break;
124427088Sminshall 	case SYNCH:
124527088Sminshall 	    hadsynch = 1;
124627088Sminshall 	    count += 2;
124727088Sminshall 	    break;
124827088Sminshall 	default:
124927088Sminshall 	    count += 2;
125027088Sminshall 	    break;
125127088Sminshall 	}
125227088Sminshall     }
125327088Sminshall     /* Now, do we have enough room? */
1254*27228Sminshall     if (NETROOM() < count) {
125527088Sminshall 	printf("There is not enough room in the buffer TO the network\n");
125627088Sminshall 	printf("to process your request.  Nothing will be done.\n");
125727088Sminshall 	printf("('send synch' will throw away most data in the network\n");
125827088Sminshall 	printf("buffer, if this might help.)\n");
125927088Sminshall 	return;
126027088Sminshall     }
126127088Sminshall     /* OK, they are all OK, now go through again and actually send */
126227088Sminshall     for (i = 1; i < argc; i++) {
126327088Sminshall 	if (!(s = getsend(argv[i]))) {
126427088Sminshall 	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
126527088Sminshall 	    quit();
126627088Sminshall 	    /*NOTREACHED*/
126727088Sminshall 	}
126827088Sminshall 	if (s->routine) {
126927088Sminshall 	    (*s->routine)(s);
127027088Sminshall 	} else {
127127088Sminshall 	    switch (what = s->what) {
127227088Sminshall 	    case SENDQUESTION:
127327088Sminshall 	    case SEND2QUESTION:
127427088Sminshall 		for (s = Sendlist; s->name; s++) {
127527088Sminshall 		    if (s->help || (what == SEND2QUESTION)) {
127627088Sminshall 			printf(s->name);
127727088Sminshall 			if (s->help) {
127827088Sminshall 			    printf("\t%s", s->help);
127927088Sminshall 			}
128027088Sminshall 			printf("\n");
128127088Sminshall 		    }
128227088Sminshall 		}
128327088Sminshall 		break;
128427088Sminshall 	    case SENDESCAPE:
128527088Sminshall 		NETADD(escape);
128627088Sminshall 		break;
128727088Sminshall 	    default:
128827088Sminshall 		NET2ADD(IAC, what);
128927088Sminshall 		break;
129027088Sminshall 	    }
129127088Sminshall 	}
129227088Sminshall     }
129327088Sminshall }
129427088Sminshall 
129527088Sminshall /*
129627088Sminshall  * The following are the routines and data structures referred
129727088Sminshall  * to by the arguments to the "toggle" command.
129827088Sminshall  */
129927088Sminshall 
130027110Sminshall lclsigs()
130127110Sminshall {
130227110Sminshall     donelclsigs = 1;
130327110Sminshall }
130427110Sminshall 
130527110Sminshall /*VARARGS*/
130627178Sminshall togcrmod()
130727110Sminshall {
130827110Sminshall     crmod = !crmod;
130927186Sminshall     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
131027110Sminshall     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
131127110Sminshall     fflush(stdout);
131227110Sminshall }
131327110Sminshall 
131427178Sminshall togdebug()
131527088Sminshall {
131627110Sminshall     if (net > 0 &&
131727186Sminshall 	setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
131827186Sminshall 									< 0) {
131927110Sminshall 	    perror("setsockopt (SO_DEBUG)");
132027186Sminshall     }
132127088Sminshall }
132227088Sminshall 
132327088Sminshall 
132427088Sminshall 
132527088Sminshall int togglehelp();
132627088Sminshall 
132727110Sminshall char	crmodhelp[] =	"toggle mapping of received carriage returns";
132827110Sminshall 
132927178Sminshall struct togglelist {
133027178Sminshall     char	*name;		/* name of toggle */
133127178Sminshall     char	*help;		/* help message */
133227186Sminshall     int		(*handler)();	/* routine to do actual setting */
133327178Sminshall     int		dohelp;		/* should we display help information */
133427178Sminshall     int		*variable;
133527178Sminshall     char	*actionexplanation;
133627178Sminshall };
133727178Sminshall 
133827178Sminshall struct togglelist Togglelist[] = {
133927178Sminshall     { "localchars",
134027178Sminshall 	"toggle local recognition of control characters",
134127178Sminshall 	    lclsigs,
134227178Sminshall 		1,
134327178Sminshall 		    &localsigs,
134427178Sminshall 			"recognize interrupt/quit characters" },
134527178Sminshall     { "echochar",
134627178Sminshall 	"toggle recognition of echo toggle character",
134727186Sminshall 	    0,
134827178Sminshall 		1,
134927178Sminshall 		    &doechocharrecognition,
135027178Sminshall 			"recognize echo toggle character" },
135127186Sminshall     { "autosynch",
135227186Sminshall 	"toggle automatic sending of interrupt characters in urgent mode",
135327186Sminshall 	    0,
135427186Sminshall 		1,
135527186Sminshall 		    &autosynch,
135627186Sminshall 			"send interrupt characters in urgent mode" },
1357*27228Sminshall     { "autoflush",
1358*27228Sminshall 	"toggle automatic flushing of output when sending interrupt characters",
1359*27228Sminshall 	    0,
1360*27228Sminshall 		1,
1361*27228Sminshall 		    &autoflush,
1362*27228Sminshall 			"flush output when sending interrupt characters" },
136327178Sminshall     { "crmod",
136427178Sminshall 	crmodhelp,
136527186Sminshall 	    0,
136627178Sminshall 		1,
136727178Sminshall 		    &crmod,
136827178Sminshall 			"map carriage return on output" },
136927110Sminshall     { " ", "", 0, 1 },		/* empty line */
137027178Sminshall     { "debug",
137127178Sminshall 	"(debugging) toggle debugging",
137227178Sminshall 	    togdebug,
137327178Sminshall 		1,
137427178Sminshall 		    &debug,
137527178Sminshall 			"turn on socket level debugging" },
137627178Sminshall     { "options",
137727178Sminshall 	"(debugging) toggle viewing of options processing",
137827186Sminshall 	    0,
137927178Sminshall 		1,
138027178Sminshall 		    &showoptions,
138127178Sminshall 			"show option processing" },
138227178Sminshall     { "netdata",
138327178Sminshall 	"(debugging) toggle printing of hexadecimal network data",
138427186Sminshall 	    0,
138527178Sminshall 		1,
138627178Sminshall 		    &netdata,
138727178Sminshall 			"print hexadecimal representation of network traffic" },
138827178Sminshall     { "?",
138927178Sminshall 	"display help information",
139027178Sminshall 	    togglehelp,
139127178Sminshall 		1 },
139227178Sminshall     { "help",
139327178Sminshall 	"display help information",
139427178Sminshall 	    togglehelp,
139527178Sminshall 		0 },
139627088Sminshall     { 0 }
139727088Sminshall };
139827088Sminshall 
139927088Sminshall togglehelp()
140027088Sminshall {
140127178Sminshall     struct togglelist *c;
140227088Sminshall 
140327178Sminshall     for (c = Togglelist; c->name; c++) {
140427088Sminshall 	if (c->dohelp) {
140527088Sminshall 	    printf("%s\t%s\n", c->name, c->help);
140627088Sminshall 	}
140727088Sminshall     }
140827088Sminshall }
140927088Sminshall 
141027088Sminshall char **
141127088Sminshall getnexttoggle(name)
141227088Sminshall char *name;
141327088Sminshall {
141427178Sminshall     struct togglelist *c = (struct togglelist *) name;
141527088Sminshall 
141627088Sminshall     return (char **) (c+1);
141727088Sminshall }
141827088Sminshall 
141927178Sminshall struct togglelist *
142027088Sminshall gettoggle(name)
142127088Sminshall char *name;
142227088Sminshall {
142327178Sminshall     return (struct togglelist *)
142427178Sminshall 			genget(name, (char **) Togglelist, getnexttoggle);
142527088Sminshall }
142627088Sminshall 
142727088Sminshall toggle(argc, argv)
142827088Sminshall int	argc;
142927088Sminshall char	*argv[];
143027088Sminshall {
143127088Sminshall     char *name;
143227178Sminshall     struct togglelist *c;
143327088Sminshall 
143427088Sminshall     if (argc < 2) {
143527088Sminshall 	fprintf(stderr,
143627088Sminshall 	    "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
143727088Sminshall 	return;
143827088Sminshall     }
143927088Sminshall     argc--;
144027088Sminshall     argv++;
144127088Sminshall     while (argc--) {
144227088Sminshall 	name = *argv++;
144327088Sminshall 	c = gettoggle(name);
144427186Sminshall 	if (c == Ambiguous(struct togglelist *)) {
144527088Sminshall 	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
144627088Sminshall 					name);
144727088Sminshall 	} else if (c == 0) {
144827088Sminshall 	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
144927088Sminshall 					name);
145027088Sminshall 	} else {
145127186Sminshall 	    if (c->variable) {
145227186Sminshall 		*c->variable = !*c->variable;		/* invert it */
145327186Sminshall 		printf("%s %s.\n", *c->variable? "Will" : "Won't",
145427186Sminshall 							c->actionexplanation);
145527186Sminshall 	    }
145627186Sminshall 	    if (c->handler) {
145727186Sminshall 		(*c->handler)(c);
145827186Sminshall 	    }
145927088Sminshall 	}
146027088Sminshall     }
146127088Sminshall }
146227088Sminshall 
146327088Sminshall /*
146427110Sminshall  * The following perform the "set" command.
146527110Sminshall  */
146627110Sminshall 
146727178Sminshall struct setlist {
146827178Sminshall     char *name;				/* name */
146927110Sminshall     char *help;				/* help information */
147027110Sminshall     char *charp;			/* where it is located at */
147127110Sminshall };
147227110Sminshall 
147327178Sminshall struct setlist Setlist[] = {
147427110Sminshall     { "echo", 	"character to toggle local echoing on/off", &echoc },
147527110Sminshall     { "escape",	"character to escape back to telnet command mode", &escape },
1476*27228Sminshall     { "\200", "" },
1477*27228Sminshall     { "\200", "The following need 'localsigs' to be toggled true", 0 },
147827110Sminshall     { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
147927110Sminshall     { "quit",	"character to cause a Break", &ntc.t_quitc },
1480*27228Sminshall     { "flush output", "character to cause an Abort Oubput", &nltc.t_flushc },
148127110Sminshall     { "erase",	"character to cause an Erase Character", &nttyb.sg_erase },
148227110Sminshall     { "kill",	"character to cause an Erase Line", &nttyb.sg_kill },
148327110Sminshall     { 0 }
148427110Sminshall };
148527110Sminshall 
148627110Sminshall char **
148727178Sminshall getnextset(name)
148827110Sminshall char *name;
148927110Sminshall {
149027178Sminshall     struct setlist *c = (struct setlist *)name;
149127110Sminshall 
149227110Sminshall     return (char **) (c+1);
149327110Sminshall }
149427110Sminshall 
149527178Sminshall struct setlist *
149627178Sminshall getset(name)
149727110Sminshall char *name;
149827110Sminshall {
149927178Sminshall     return (struct setlist *) genget(name, (char **) Setlist, getnextset);
150027110Sminshall }
150127110Sminshall 
150227110Sminshall setcmd(argc, argv)
150327110Sminshall int	argc;
150427110Sminshall char	*argv[];
150527110Sminshall {
150627110Sminshall     int value;
150727178Sminshall     struct setlist *ct;
150827110Sminshall 
150927110Sminshall     /* XXX back we go... sigh */
151027110Sminshall     if (argc != 3) {
151127110Sminshall 	printf("Format is 'set Name Value', where 'Name' is one of:\n\n");
151227178Sminshall 	for (ct = Setlist; ct->name; ct++) {
151327178Sminshall 	    printf("%s\t%s\n", ct->name, ct->help);
151427110Sminshall 	}
151527110Sminshall 	return;
151627110Sminshall     }
151727110Sminshall 
151827178Sminshall     ct = getset(argv[1]);
151927110Sminshall     if (ct == 0) {
152027110Sminshall 	fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
152127110Sminshall 			argv[1]);
152227186Sminshall     } else if (ct == Ambiguous(struct setlist *)) {
152327110Sminshall 	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
152427110Sminshall 			argv[1]);
152527110Sminshall     } else {
152627110Sminshall 	if (strcmp("off", argv[2])) {
152727110Sminshall 	    value = special(argv[2]);
152827110Sminshall 	} else {
152927110Sminshall 	    value = -1;
153027110Sminshall 	}
153127110Sminshall 	*(ct->charp) = value;
153227178Sminshall 	printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
153327110Sminshall     }
153427110Sminshall }
153527110Sminshall 
153627110Sminshall /*
153727110Sminshall  * The following are the data structures and routines for the
153827110Sminshall  * 'mode' command.
153927110Sminshall  */
154027110Sminshall 
154127110Sminshall dolinemode()
154227110Sminshall {
154327110Sminshall     if (hisopts[TELOPT_SGA]) {
154427186Sminshall 	wontoption(TELOPT_SGA);
154527110Sminshall     }
154627110Sminshall     if (hisopts[TELOPT_ECHO]) {
154727186Sminshall 	wontoption(TELOPT_ECHO);
154827110Sminshall     }
154927110Sminshall }
155027110Sminshall 
155127110Sminshall docharmode()
155227110Sminshall {
155327110Sminshall     if (!hisopts[TELOPT_SGA]) {
155427186Sminshall 	willoption(TELOPT_SGA);
155527110Sminshall     }
155627110Sminshall     if (!hisopts[TELOPT_ECHO]) {
155727186Sminshall 	willoption(TELOPT_ECHO);
155827110Sminshall     }
155927110Sminshall }
156027110Sminshall 
156127110Sminshall struct cmd Modelist[] = {
156227110Sminshall     { "line",		"line-by-line mode",		dolinemode, 1, 1 },
156327110Sminshall     { "character",	"character-at-a-time mode",	docharmode, 1, 1 },
156427110Sminshall     { 0 },
156527110Sminshall };
156627110Sminshall 
156727110Sminshall char **
156827110Sminshall getnextmode(name)
156927110Sminshall char *name;
157027110Sminshall {
157127110Sminshall     struct cmd *c = (struct cmd *) name;
157227110Sminshall 
157327110Sminshall     return (char **) (c+1);
157427110Sminshall }
157527110Sminshall 
157627110Sminshall struct cmd *
157727110Sminshall getmodecmd(name)
157827110Sminshall char *name;
157927110Sminshall {
158027110Sminshall     return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
158127110Sminshall }
158227110Sminshall 
158327110Sminshall modecmd(argc, argv)
158427110Sminshall int	argc;
158527110Sminshall char	*argv[];
158627110Sminshall {
158727110Sminshall     struct cmd *mt;
158827110Sminshall 
158927110Sminshall     if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
159027110Sminshall 	printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
159127110Sminshall 	for (mt = Modelist; mt->name; mt++) {
159227110Sminshall 	    printf("%s\t%s\n", mt->name, mt->help);
159327110Sminshall 	}
159427110Sminshall 	return;
159527110Sminshall     }
159627110Sminshall     mt = getmodecmd(argv[1]);
159727110Sminshall     if (mt == 0) {
159827110Sminshall 	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
159927186Sminshall     } else if (mt == Ambiguous(struct cmd *)) {
160027110Sminshall 	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
160127110Sminshall     } else {
160227110Sminshall 	(*mt->handler)();
160327110Sminshall     }
160427110Sminshall }
160527110Sminshall 
160627110Sminshall /*
160727178Sminshall  * The following data structures and routines implement the
160827178Sminshall  * "display" command.
160927178Sminshall  */
161027178Sminshall 
161127178Sminshall display(argc, argv)
161227178Sminshall int	argc;
161327178Sminshall char	*argv[];
161427178Sminshall {
161527178Sminshall #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
161627178Sminshall 			    if (*tl->variable) { \
161727178Sminshall 				printf("will"); \
161827178Sminshall 			    } else { \
161927178Sminshall 				printf("won't"); \
162027178Sminshall 			    } \
162127178Sminshall 			    printf(" %s.\n", tl->actionexplanation); \
162227178Sminshall 			}
162327178Sminshall 
162427178Sminshall #define	doset(sl)	printf("[%s]\t%s.\n", control(*sl->charp), sl->name);
162527178Sminshall 
162627178Sminshall     struct togglelist *tl;
162727178Sminshall     struct setlist *sl;
162827178Sminshall 
162927178Sminshall     if (argc == 1) {
163027178Sminshall 	for (tl = Togglelist; tl->name; tl++) {
163127178Sminshall 	    dotog(tl);
163227178Sminshall 	}
163327178Sminshall 	for (sl = Setlist; sl->name; sl++) {
163427178Sminshall 	    doset(sl);
163527178Sminshall 	}
163627178Sminshall     } else {
163727178Sminshall 	int i;
163827178Sminshall 
163927178Sminshall 	for (i = 1; i < argc; i++) {
164027178Sminshall 	    sl = getset(argv[i]);
164127178Sminshall 	    tl = gettoggle(argv[i]);
164227186Sminshall 	    if ((sl == Ambiguous(struct setlist *)) ||
164327186Sminshall 				(tl == Ambiguous(struct togglelist *))) {
164427178Sminshall 		printf("?Ambiguous argument '%s'.\n", argv[i]);
164527178Sminshall 	    } else if (!sl && !tl) {
164627178Sminshall 		printf("?Unknown argument '%s'.\n", argv[i]);
164727178Sminshall 	    } else {
164827186Sminshall 		if (tl) {
164927186Sminshall 		    dotog(tl);
165027186Sminshall 		}
165127186Sminshall 		if (sl) {
165227186Sminshall 		    doset(sl);
165327186Sminshall 		}
165427178Sminshall 	    }
165527178Sminshall 	}
165627178Sminshall     }
165727178Sminshall #undef	doset(sl)
165827178Sminshall #undef	dotog(tl)
165927178Sminshall }
166027178Sminshall 
166127178Sminshall /*
166227088Sminshall  * The following are the data structures, and many of the routines,
166327088Sminshall  * relating to command processing.
166427088Sminshall  */
166527088Sminshall 
166627088Sminshall /*
166727088Sminshall  * Set the escape character.
166827088Sminshall  */
166927088Sminshall setescape(argc, argv)
167027088Sminshall 	int argc;
167127088Sminshall 	char *argv[];
167227088Sminshall {
167327088Sminshall 	register char *arg;
167427088Sminshall 	char buf[50];
167527088Sminshall 
167627186Sminshall 	printf(
167727186Sminshall 	    "Deprecated usage - please use 'set escape%s%s' in the future.\n",
167827186Sminshall 				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
167927088Sminshall 	if (argc > 2)
168027088Sminshall 		arg = argv[1];
168127088Sminshall 	else {
168227088Sminshall 		printf("new escape character: ");
168327088Sminshall 		gets(buf);
168427088Sminshall 		arg = buf;
168527088Sminshall 	}
168627088Sminshall 	if (arg[0] != '\0')
168727088Sminshall 		escape = arg[0];
168827088Sminshall 	printf("Escape character is '%s'.\n", control(escape));
168927088Sminshall 	fflush(stdout);
169027088Sminshall }
169127088Sminshall 
169227088Sminshall /*VARARGS*/
169327088Sminshall suspend()
169427088Sminshall {
169527110Sminshall 	setcommandmode();
169627088Sminshall 	kill(0, SIGTSTP);
169727088Sminshall 	/* reget parameters in case they were changed */
169827088Sminshall 	ioctl(0, TIOCGETP, (char *)&ottyb);
169927088Sminshall 	ioctl(0, TIOCGETC, (char *)&otc);
170027088Sminshall 	ioctl(0, TIOCGLTC, (char *)&oltc);
170127088Sminshall }
170227088Sminshall 
170327088Sminshall /*VARARGS*/
170427088Sminshall bye()
170527088Sminshall {
170627088Sminshall 	register char *op;
170727088Sminshall 
170827088Sminshall 	if (connected) {
170927088Sminshall 		shutdown(net, 2);
171027088Sminshall 		printf("Connection closed.\n");
171127088Sminshall 		close(net);
171227088Sminshall 		connected = 0;
171327088Sminshall 		/* reset his options */
171427088Sminshall 		for (op = hisopts; op < &hisopts[256]; op++)
171527088Sminshall 			*op = 0;
171627088Sminshall 	}
171727088Sminshall }
171827088Sminshall 
171927088Sminshall /*VARARGS*/
172027088Sminshall quit()
172127088Sminshall {
172227088Sminshall 	call(bye, "bye", 0);
172327088Sminshall 	exit(0);
172427088Sminshall }
172527088Sminshall 
172627088Sminshall /*
172727088Sminshall  * Print status about the connection.
172827088Sminshall  */
172927186Sminshall /*ARGSUSED*/
173027178Sminshall status(argc, argv)
173127178Sminshall int	argc;
173227178Sminshall char	*argv[];
173327088Sminshall {
173427178Sminshall     if (connected) {
173527178Sminshall 	printf("Connected to %s.\n", hostname);
173627178Sminshall 	if (argc < 2) {
173727178Sminshall 	    printf("Operating in %s.\n", modedescriptions[getconnmode()]);
1738*27228Sminshall 	    if (localsigs) {
173927178Sminshall 		printf("Catching signals locally.\n");
174027178Sminshall 	    }
174127110Sminshall 	}
174227178Sminshall     } else {
174327178Sminshall 	printf("No connection.\n");
174427178Sminshall     }
174527178Sminshall     printf("Escape character is '%s'.\n", control(escape));
174627178Sminshall     fflush(stdout);
174727088Sminshall }
174827088Sminshall 
174927088Sminshall tn(argc, argv)
175027088Sminshall 	int argc;
175127088Sminshall 	char *argv[];
175227088Sminshall {
175327088Sminshall 	register struct hostent *host = 0;
175427088Sminshall 
175527088Sminshall 	if (connected) {
175627088Sminshall 		printf("?Already connected to %s\n", hostname);
175727088Sminshall 		return;
175827088Sminshall 	}
175927088Sminshall 	if (argc < 2) {
176027186Sminshall 		(void) strcpy(line, "Connect ");
176127088Sminshall 		printf("(to) ");
176227088Sminshall 		gets(&line[strlen(line)]);
176327088Sminshall 		makeargv();
176427088Sminshall 		argc = margc;
176527088Sminshall 		argv = margv;
176627088Sminshall 	}
176727088Sminshall 	if (argc > 3) {
176827088Sminshall 		printf("usage: %s host-name [port]\n", argv[0]);
176927088Sminshall 		return;
177027088Sminshall 	}
177127088Sminshall 	sin.sin_addr.s_addr = inet_addr(argv[1]);
177227088Sminshall 	if (sin.sin_addr.s_addr != -1) {
177327088Sminshall 		sin.sin_family = AF_INET;
177427186Sminshall 		(void) strcpy(hnamebuf, argv[1]);
177527088Sminshall 		hostname = hnamebuf;
177627088Sminshall 	} else {
177727088Sminshall 		host = gethostbyname(argv[1]);
177827088Sminshall 		if (host) {
177927088Sminshall 			sin.sin_family = host->h_addrtype;
178027088Sminshall 			bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
178127088Sminshall 				host->h_length);
178227088Sminshall 			hostname = host->h_name;
178327088Sminshall 		} else {
178427088Sminshall 			printf("%s: unknown host\n", argv[1]);
178527088Sminshall 			return;
178627088Sminshall 		}
178727088Sminshall 	}
178827088Sminshall 	sin.sin_port = sp->s_port;
178927088Sminshall 	if (argc == 3) {
179027088Sminshall 		sin.sin_port = atoi(argv[2]);
179127186Sminshall 		if (sin.sin_port == 0) {
179227088Sminshall 			sp = getservbyname(argv[2], "tcp");
179327088Sminshall 			if (sp)
179427088Sminshall 				sin.sin_port = sp->s_port;
179527088Sminshall 			else {
179627088Sminshall 				printf("%s: bad port number\n", argv[2]);
179727088Sminshall 				return;
179827088Sminshall 			}
179927088Sminshall 		} else {
180027088Sminshall 			sin.sin_port = atoi(argv[2]);
180127088Sminshall 			sin.sin_port = htons(sin.sin_port);
180227088Sminshall 		}
180327088Sminshall 		telnetport = 0;
180427110Sminshall 	} else {
180527110Sminshall 		telnetport = 1;
180627088Sminshall 	}
180727088Sminshall 	signal(SIGINT, intr);
180827110Sminshall 	signal(SIGQUIT, intr2);
180927088Sminshall 	signal(SIGPIPE, deadpeer);
181027088Sminshall 	printf("Trying...\n");
181127088Sminshall 	do {
181227088Sminshall 		net = socket(AF_INET, SOCK_STREAM, 0);
181327088Sminshall 		if (net < 0) {
181427088Sminshall 			perror("telnet: socket");
181527088Sminshall 			return;
181627088Sminshall 		}
181727186Sminshall 		if (debug &&
181827186Sminshall 				setsockopt(net, SOL_SOCKET, SO_DEBUG,
181927186Sminshall 					(char *)&debug, sizeof(debug)) < 0) {
182027088Sminshall 			perror("setsockopt (SO_DEBUG)");
182127186Sminshall 		}
182227186Sminshall 		if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
182327088Sminshall 			if (host && host->h_addr_list[1]) {
182427088Sminshall 				int oerrno = errno;
182527088Sminshall 
182627088Sminshall 				fprintf(stderr,
182727088Sminshall 				    "telnet: connect to address %s: ",
182827088Sminshall 				    inet_ntoa(sin.sin_addr));
182927088Sminshall 				errno = oerrno;
183027186Sminshall 				perror((char *)0);
183127088Sminshall 				host->h_addr_list++;
183227088Sminshall 				bcopy(host->h_addr_list[0],
183327088Sminshall 				    (caddr_t)&sin.sin_addr, host->h_length);
183427088Sminshall 				fprintf(stderr, "Trying %s...\n",
183527088Sminshall 					inet_ntoa(sin.sin_addr));
183627088Sminshall 				(void) close(net);
183727088Sminshall 				continue;
183827088Sminshall 			}
183927088Sminshall 			perror("telnet: connect");
184027088Sminshall 			signal(SIGINT, SIG_DFL);
184127088Sminshall 			return;
184227088Sminshall 		}
184327088Sminshall 		connected++;
184427088Sminshall 	} while (connected == 0);
184527178Sminshall 	call(status, "status", "notmuch", 0);
184627088Sminshall 	if (setjmp(peerdied) == 0)
184727088Sminshall 		telnet();
184827088Sminshall 	fprintf(stderr, "Connection closed by foreign host.\n");
184927088Sminshall 	exit(1);
185027088Sminshall }
185127088Sminshall 
185227088Sminshall 
185327088Sminshall #define HELPINDENT (sizeof ("connect"))
185427088Sminshall 
185527088Sminshall char	openhelp[] =	"connect to a site";
185627088Sminshall char	closehelp[] =	"close current connection";
185727088Sminshall char	quithelp[] =	"exit telnet";
185827088Sminshall char	zhelp[] =	"suspend telnet";
185927088Sminshall char	escapehelp[] =	"set escape character";
186027088Sminshall char	statushelp[] =	"print status information";
186127088Sminshall char	helphelp[] =	"print help information";
186227110Sminshall char	sendhelp[] =	"transmit special characters ('send ?' for more)";
186327178Sminshall char	sethelp[] = 	"set operating parameters ('set ?' for more)";
186427178Sminshall char	togglestring[] ="toggle operating parameters ('toggle ?' for more)";
186527178Sminshall char	displayhelp[] =	"display operating parameters";
186627178Sminshall char	modehelp[] =
186727178Sminshall 		"try to enter line-by-line or character-at-a-time mode";
186827088Sminshall 
186927088Sminshall int	help();
187027088Sminshall 
187127088Sminshall struct cmd cmdtab[] = {
187227110Sminshall 	{ "open",	openhelp,	tn,		1, 0 },
187327110Sminshall 	{ "close",	closehelp,	bye,		1, 1 },
187427110Sminshall 	{ "quit",	quithelp,	quit,		1, 0 },
187527110Sminshall 	{ "z",		zhelp,		suspend,	1, 0 },
187627110Sminshall 	{ "escape",	escapehelp,	setescape,	1, 0 },
187727110Sminshall 	{ "status",	statushelp,	status,		1, 0 },
187827178Sminshall 	{ "crmod",	crmodhelp,	togcrmod,	1, 0 },
187927110Sminshall 	{ "send",	sendhelp,	sendcmd,	1, 1 },
188027110Sminshall 	{ "transmit",	sendhelp,	sendcmd,	0, 1 },
188127110Sminshall 	{ "xmit",	sendhelp,	sendcmd,	0, 1 },
188227178Sminshall 	{ "set",	sethelp,	setcmd,		1, 0 },
188327110Sminshall 	{ "toggle",	togglestring,	toggle,		1, 0 },
188427178Sminshall 	{ "display",	displayhelp,	display,	1, 0 },
188527178Sminshall 	{ "mode",	modehelp,	modecmd,	1, 1 },
188627110Sminshall 	{ "?",		helphelp,	help,		1, 0 },
188727110Sminshall 	{ "help",	helphelp,	help,		0, 0 },
188827088Sminshall 	0
188927088Sminshall };
189027088Sminshall 
189127088Sminshall 
189227088Sminshall /*
189327088Sminshall  * Help command.
189427088Sminshall  */
189527088Sminshall help(argc, argv)
189627088Sminshall 	int argc;
189727088Sminshall 	char *argv[];
189827088Sminshall {
189927088Sminshall 	register struct cmd *c;
190027088Sminshall 
190127088Sminshall 	if (argc == 1) {
190227088Sminshall 		printf("Commands may be abbreviated.  Commands are:\n\n");
190327088Sminshall 		for (c = cmdtab; c->name; c++)
190427088Sminshall 			if (c->dohelp) {
190527088Sminshall 				printf("%-*s\t%s\n", HELPINDENT, c->name,
190627088Sminshall 								    c->help);
190727088Sminshall 			}
190827088Sminshall 		return;
190927088Sminshall 	}
191027088Sminshall 	while (--argc > 0) {
191127088Sminshall 		register char *arg;
191227088Sminshall 		arg = *++argv;
191327088Sminshall 		c = getcmd(arg);
191427186Sminshall 		if (c == Ambiguous(struct cmd *))
191527088Sminshall 			printf("?Ambiguous help command %s\n", arg);
191627088Sminshall 		else if (c == (struct cmd *)0)
191727088Sminshall 			printf("?Invalid help command %s\n", arg);
191827088Sminshall 		else
191927088Sminshall 			printf("%s\n", c->help);
192027088Sminshall 	}
192127088Sminshall }
192227088Sminshall /*
192327088Sminshall  * Call routine with argc, argv set from args (terminated by 0).
192427088Sminshall  * VARARGS2
192527088Sminshall  */
192627088Sminshall call(routine, args)
192727088Sminshall 	int (*routine)();
192827186Sminshall 	char *args;
192927088Sminshall {
193027186Sminshall 	register char **argp;
193127088Sminshall 	register int argc;
193227088Sminshall 
193327088Sminshall 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
193427088Sminshall 		;
193527088Sminshall 	(*routine)(argc, &args);
193627088Sminshall }
193727088Sminshall 
193827088Sminshall makeargv()
193927088Sminshall {
194027088Sminshall 	register char *cp;
194127088Sminshall 	register char **argp = margv;
194227088Sminshall 
194327088Sminshall 	margc = 0;
194427088Sminshall 	for (cp = line; *cp;) {
194527088Sminshall 		while (isspace(*cp))
194627088Sminshall 			cp++;
194727088Sminshall 		if (*cp == '\0')
194827088Sminshall 			break;
194927088Sminshall 		*argp++ = cp;
195027088Sminshall 		margc += 1;
195127088Sminshall 		while (*cp != '\0' && !isspace(*cp))
195227088Sminshall 			cp++;
195327088Sminshall 		if (*cp == '\0')
195427088Sminshall 			break;
195527088Sminshall 		*cp++ = '\0';
195627088Sminshall 	}
195727088Sminshall 	*argp++ = 0;
195827088Sminshall }
195927088Sminshall 
196027088Sminshall char **
196127088Sminshall getnextcmd(name)
196227088Sminshall char *name;
196327088Sminshall {
196427088Sminshall     struct cmd *c = (struct cmd *) name;
196527088Sminshall 
196627088Sminshall     return (char **) (c+1);
196727088Sminshall }
196827088Sminshall 
196927088Sminshall struct cmd *
197027088Sminshall getcmd(name)
197127088Sminshall char *name;
197227088Sminshall {
197327088Sminshall     return (struct cmd *) genget(name, (char **) cmdtab, getnextcmd);
197427088Sminshall }
197527088Sminshall 
197627088Sminshall command(top)
197727088Sminshall 	int top;
197827088Sminshall {
197927088Sminshall 	register struct cmd *c;
198027088Sminshall 
198127110Sminshall 	setcommandmode();
198227088Sminshall 	if (!top)
198327088Sminshall 		putchar('\n');
198427088Sminshall 	else
198527088Sminshall 		signal(SIGINT, SIG_DFL);
198627088Sminshall 	for (;;) {
198727088Sminshall 		printf("%s> ", prompt);
198827088Sminshall 		if (gets(line) == 0) {
198927088Sminshall 			if (feof(stdin))
199027088Sminshall 				quit();
199127088Sminshall 			break;
199227088Sminshall 		}
199327088Sminshall 		if (line[0] == 0)
199427088Sminshall 			break;
199527088Sminshall 		makeargv();
199627088Sminshall 		c = getcmd(margv[0]);
199727186Sminshall 		if (c == Ambiguous(struct cmd *)) {
199827088Sminshall 			printf("?Ambiguous command\n");
199927088Sminshall 			continue;
200027088Sminshall 		}
200127088Sminshall 		if (c == 0) {
200227088Sminshall 			printf("?Invalid command\n");
200327088Sminshall 			continue;
200427088Sminshall 		}
200527088Sminshall 		if (c->needconnect && !connected) {
200627088Sminshall 			printf("?Need to be connected first.\n");
200727088Sminshall 			continue;
200827088Sminshall 		}
200927088Sminshall 		(*c->handler)(margc, margv);
201027088Sminshall 		if (c->handler != help)
201127088Sminshall 			break;
201227088Sminshall 	}
201327088Sminshall 	if (!top) {
201427110Sminshall 		if (!connected) {
201527088Sminshall 			longjmp(toplevel, 1);
201627110Sminshall 			/*NOTREACHED*/
201727110Sminshall 		}
201827110Sminshall 		setconnmode();
201927088Sminshall 	}
202027088Sminshall }
202127186Sminshall 
202227186Sminshall /*
202327186Sminshall  * main.  Parse arguments, invoke the protocol or command parser.
202427186Sminshall  */
202527186Sminshall 
202627186Sminshall 
202727186Sminshall main(argc, argv)
202827186Sminshall 	int argc;
202927186Sminshall 	char *argv[];
203027186Sminshall {
203127186Sminshall 	sp = getservbyname("telnet", "tcp");
203227186Sminshall 	if (sp == 0) {
203327186Sminshall 		fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
203427186Sminshall 		exit(1);
203527186Sminshall 	}
203627186Sminshall 	NetTrace = stdout;
203727186Sminshall 	ioctl(0, TIOCGETP, (char *)&ottyb);
203827186Sminshall 	ioctl(0, TIOCGETC, (char *)&otc);
203927186Sminshall 	ioctl(0, TIOCGLTC, (char *)&oltc);
2040*27228Sminshall #if	defined(LNOFLSH)
2041*27228Sminshall 	ioctl(0, TIOCLGET, (char *)&autoflush);
2042*27228Sminshall 	autoflush &= LNOFLSH;
2043*27228Sminshall #endif	/* LNOFLSH */
204427186Sminshall 	ntc = otc;
204527186Sminshall 	ntc.t_eofc = -1;		/* we don't want to use EOF */
2046*27228Sminshall 	nltc = oltc;
204727186Sminshall 	nttyb = ottyb;
204827186Sminshall 	setbuf(stdin, (char *)0);
204927186Sminshall 	setbuf(stdout, (char *)0);
205027186Sminshall 	prompt = argv[0];
205127186Sminshall 	if (argc > 1 && !strcmp(argv[1], "-d")) {
205227186Sminshall 		debug = 1;
205327186Sminshall 		argv++;
205427186Sminshall 		argc--;
205527186Sminshall 	}
205627186Sminshall 	if (argc > 1 && !strcmp(argv[1], "-n")) {
205727186Sminshall 	    argv++;
205827186Sminshall 	    argc--;
205927186Sminshall 	    if (argc > 1) {		/* get file name */
206027186Sminshall 		NetTrace = fopen(argv[1], "w");
206127186Sminshall 		argv++;
206227186Sminshall 		argc--;
206327186Sminshall 		if (NetTrace == NULL) {
206427186Sminshall 		    NetTrace = stdout;
206527186Sminshall 		}
206627186Sminshall 	    }
206727186Sminshall 	}
206827186Sminshall 	if (argc != 1) {
206927186Sminshall 		if (setjmp(toplevel) != 0)
207027186Sminshall 			exit(0);
207127186Sminshall 		tn(argc, argv);
207227186Sminshall 	}
207327186Sminshall 	setjmp(toplevel);
207427186Sminshall 	for (;;)
207527186Sminshall 		command(1);
207627186Sminshall }
2077