xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 32554)
111758Ssam #ifndef lint
232377Sminshall static char copyright[] =
332377Sminshall "@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
421580Sdist  All rights reserved.\n";
532377Sminshall #endif	/* not lint */
611758Ssam 
721580Sdist #ifndef lint
832377Sminshall static char sccsid[] = "@(#)telnet.c	1.2 (Berkeley) 9/25/87";
932377Sminshall #endif	/* not lint */
1021580Sdist 
119217Ssam #include <sys/types.h>
129217Ssam 
1332377Sminshall #if	defined(unix)
1432377Sminshall /* By the way, we need to include curses.h before telnet.h since,
1532377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
1632377Sminshall  * declared in curses.h.
1732377Sminshall  */
1832377Sminshall #include <curses.h>
1932377Sminshall #endif	/* defined(unix) */
2032377Sminshall 
2112212Ssam #include <arpa/telnet.h>
2232377Sminshall 
2332377Sminshall #if	defined(unix)
2427186Sminshall #include <strings.h>
2532377Sminshall #else	/* defined(unix) */
2632377Sminshall #include <string.h>
2732377Sminshall #endif	/* defined(unix) */
289217Ssam 
2932381Sminshall #include "ring.h"
3032381Sminshall 
3132377Sminshall #include "defines.h"
3232377Sminshall #include "externs.h"
3332377Sminshall #include "types.h"
3432377Sminshall #include "general.h"
3527178Sminshall 
3627178Sminshall 
3727228Sminshall #define	strip(x)	((x)&0x7f)
386000Sroot 
3927088Sminshall 
4032377Sminshall static char	subbuffer[SUBBUFSIZE],
4132377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
4227676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
4327676Sminshall #define	SB_TERM()	subend = subpointer;
4427676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4527676Sminshall 				*subpointer++ = (c); \
4627676Sminshall 			}
4727676Sminshall 
486000Sroot char	hisopts[256];
496000Sroot char	myopts[256];
506000Sroot 
516000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
526000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
536000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
546000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
556000Sroot 
5632377Sminshall int
5732377Sminshall 	connected,
5832377Sminshall 	showoptions,
5932377Sminshall 	In3270,		/* Are we in 3270 mode? */
6032377Sminshall 	ISend,		/* trying to send network data in */
6132377Sminshall 	debug = 0,
6232377Sminshall 	crmod,
6332377Sminshall 	netdata,	/* Print out network data flow */
6432377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
6532377Sminshall 	noasynch = 0,	/* User specified "-noasynch" on command line */
6632377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
6732531Sminshall 	telnetport = 1,
6832531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
6932531Sminshall 	flushout,	/* flush output */
7032531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
7132531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
7232531Sminshall 	localchars,	/* we recognize interrupt/quit */
7332531Sminshall 	donelclchars,	/* the user has set "localchars" */
7432531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
7532531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
7632531Sminshall 	globalmode;
7727088Sminshall 
7832377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
796000Sroot 
8032377Sminshall char
8132377Sminshall 	*prompt = 0,
8232377Sminshall 	escape,
8332377Sminshall 	echoc;
8427186Sminshall 
8527186Sminshall /*
866000Sroot  * Telnet receiver states for fsm
876000Sroot  */
886000Sroot #define	TS_DATA		0
896000Sroot #define	TS_IAC		1
906000Sroot #define	TS_WILL		2
916000Sroot #define	TS_WONT		3
926000Sroot #define	TS_DO		4
936000Sroot #define	TS_DONT		5
9427021Sminshall #define	TS_CR		6
9527676Sminshall #define	TS_SB		7		/* sub-option collection */
9627676Sminshall #define	TS_SE		8		/* looking for sub-option end */
976000Sroot 
9832377Sminshall static int	telrcv_state;
996000Sroot 
10032377Sminshall jmp_buf	toplevel = { 0 };
10132377Sminshall jmp_buf	peerdied;
1026000Sroot 
10332377Sminshall int	flushline;
10427021Sminshall 
10532377Sminshall /*
10632377Sminshall  * The following are some clocks used to decide how to interpret
10732377Sminshall  * the relationship between various variables.
10832377Sminshall  */
1096000Sroot 
11032377Sminshall Clocks clocks;
11132377Sminshall 
11232377Sminshall Modelist modelist[] = {
11332377Sminshall 	{ "telnet command mode", COMMAND_LINE },
11432377Sminshall 	{ "character-at-a-time mode", 0 },
11532377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
11632377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
11732377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
11832377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
11932377Sminshall 	{ "3270 mode", 0 },
12032377Sminshall };
1216000Sroot 
12232377Sminshall 
12332377Sminshall /*
12432377Sminshall  * Initialize telnet environment.
12532377Sminshall  */
1266000Sroot 
12732377Sminshall init_telnet()
12832377Sminshall {
12932377Sminshall     /* Don't change telnetport */
13032377Sminshall     SB_CLEAR();
13132377Sminshall     ClearArray(hisopts);
13232377Sminshall     ClearArray(myopts);
1336000Sroot 
134*32554Sminshall     connected = In3270 = ISend = donebinarytoggle = 0;
13532377Sminshall     telnetport = 0;
1366000Sroot 
13732377Sminshall #if	defined(unix) && defined(TN3270)
13832377Sminshall     HaveInput = 0;
13932377Sminshall #endif	/* defined(unix) && defined(TN3270) */
1406000Sroot 
14132377Sminshall     SYNCHing = 0;
1426000Sroot 
14332377Sminshall     /* Don't change NetTrace */
1446000Sroot 
14532377Sminshall     escape = CONTROL(']');
14632377Sminshall     echoc = CONTROL('E');
1476000Sroot 
14832377Sminshall     flushline = 1;
14932377Sminshall     telrcv_state = TS_DATA;
15032377Sminshall }
151*32554Sminshall 
1526000Sroot 
153*32554Sminshall #include <varargs.h>
1546000Sroot 
155*32554Sminshall static void
156*32554Sminshall printring(va_alist)
157*32554Sminshall va_dcl
158*32554Sminshall {
159*32554Sminshall     va_list ap;
160*32554Sminshall     char buffer[100];		/* where things go */
161*32554Sminshall     char *ptr;
162*32554Sminshall     char *format;
163*32554Sminshall     char *string;
164*32554Sminshall     Ring *ring;
165*32554Sminshall     int i;
166*32554Sminshall 
167*32554Sminshall     va_start(ap);
168*32554Sminshall 
169*32554Sminshall     ring = va_arg(ap, Ring *);
170*32554Sminshall     format = va_arg(ap, char *);
171*32554Sminshall     ptr = buffer;
172*32554Sminshall 
173*32554Sminshall     while ((i = *format++) != 0) {
174*32554Sminshall 	if (i == '%') {
175*32554Sminshall 	    i = *format++;
176*32554Sminshall 	    switch (i) {
177*32554Sminshall 	    case 'c':
178*32554Sminshall 		*ptr++ = va_arg(ap, int);
179*32554Sminshall 		break;
180*32554Sminshall 	    case 's':
181*32554Sminshall 		string = va_arg(ap, char *);
182*32554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
183*32554Sminshall 		ring_supply_data(ring, string, strlen(string));
184*32554Sminshall 		ptr = buffer;
185*32554Sminshall 		break;
186*32554Sminshall 	    case 0:
187*32554Sminshall 		ExitString("printring: trailing %%.\n", 1);
188*32554Sminshall 		/*NOTREACHED*/
189*32554Sminshall 	    default:
190*32554Sminshall 		ExitString("printring: unknown format character.\n", 1);
191*32554Sminshall 		/*NOTREACHED*/
192*32554Sminshall 	    }
193*32554Sminshall 	} else {
194*32554Sminshall 	    *ptr++ = i;
195*32554Sminshall 	}
196*32554Sminshall     }
197*32554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
198*32554Sminshall }
199*32554Sminshall 
200*32554Sminshall 
20132377Sminshall void
20227676Sminshall willoption(option, reply)
20327676Sminshall 	int option, reply;
2046000Sroot {
2056000Sroot 	char *fmt;
2066000Sroot 
2076000Sroot 	switch (option) {
2086000Sroot 
2096000Sroot 	case TELOPT_ECHO:
21032377Sminshall #	if defined(TN3270)
21132377Sminshall 	    /*
21232377Sminshall 	     * The following is a pain in the rear-end.
21332377Sminshall 	     * Various IBM servers (some versions of Wiscnet,
21432377Sminshall 	     * possibly Fibronics/Spartacus, and who knows who
21532377Sminshall 	     * else) will NOT allow us to send "DO SGA" too early
21632377Sminshall 	     * in the setup proceedings.  On the other hand,
21732377Sminshall 	     * 4.2 servers (telnetd) won't set SGA correctly.
21832377Sminshall 	     * So, we are stuck.  Empirically (but, based on
21932377Sminshall 	     * a VERY small sample), the IBM servers don't send
22032377Sminshall 	     * out anything about ECHO, so we postpone our sending
22132377Sminshall 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
22232377Sminshall 	     * DO send).
22332377Sminshall 	     */
22432377Sminshall 	    {
22532377Sminshall 		if (askedSGA == 0) {
22632377Sminshall 		    askedSGA = 1;
22732377Sminshall 		    if (!hisopts[TELOPT_SGA]) {
22832377Sminshall 			willoption(TELOPT_SGA, 0);
22932377Sminshall 		    }
23032377Sminshall 		}
23132377Sminshall 	    }
23232377Sminshall 		/* Fall through */
23332377Sminshall 	case TELOPT_EOR:
23432377Sminshall 	case TELOPT_BINARY:
23532377Sminshall #endif	/* defined(TN3270) */
2366000Sroot 	case TELOPT_SGA:
23727110Sminshall 		settimer(modenegotiated);
2386000Sroot 		hisopts[option] = 1;
2396000Sroot 		fmt = doopt;
24027110Sminshall 		setconnmode();		/* possibly set new tty mode */
2416000Sroot 		break;
2426000Sroot 
2436000Sroot 	case TELOPT_TM:
24427110Sminshall 		return;			/* Never reply to TM will's/wont's */
2456000Sroot 
2466000Sroot 	default:
2476000Sroot 		fmt = dont;
2486000Sroot 		break;
2496000Sroot 	}
250*32554Sminshall 	printring(&netoring, fmt, option);
25127676Sminshall 	if (reply)
25232377Sminshall 		printoption(">SENT", fmt, option, reply);
25327676Sminshall 	else
25432377Sminshall 		printoption("<SENT", fmt, option, reply);
2556000Sroot }
2566000Sroot 
25732377Sminshall void
25827676Sminshall wontoption(option, reply)
25927676Sminshall 	int option, reply;
2606000Sroot {
2616000Sroot 	char *fmt;
2626000Sroot 
2636000Sroot 	switch (option) {
2646000Sroot 
2656000Sroot 	case TELOPT_ECHO:
2666000Sroot 	case TELOPT_SGA:
26727110Sminshall 		settimer(modenegotiated);
2686000Sroot 		hisopts[option] = 0;
2696000Sroot 		fmt = dont;
27027110Sminshall 		setconnmode();			/* Set new tty mode */
2716000Sroot 		break;
2726000Sroot 
27327110Sminshall 	case TELOPT_TM:
27427110Sminshall 		return;		/* Never reply to TM will's/wont's */
27527110Sminshall 
2766000Sroot 	default:
2776000Sroot 		fmt = dont;
2786000Sroot 	}
279*32554Sminshall 	printring(&netoring, fmt, option);
28027676Sminshall 	if (reply)
28132377Sminshall 		printoption(">SENT", fmt, option, reply);
28227676Sminshall 	else
28332377Sminshall 		printoption("<SENT", fmt, option, reply);
2846000Sroot }
2856000Sroot 
28632377Sminshall static void
2876000Sroot dooption(option)
2886000Sroot 	int option;
2896000Sroot {
2906000Sroot 	char *fmt;
2916000Sroot 
2926000Sroot 	switch (option) {
2936000Sroot 
2946000Sroot 	case TELOPT_TM:
29513231Ssam 		fmt = will;
29613231Ssam 		break;
29713231Ssam 
29832377Sminshall #	if defined(TN3270)
29932377Sminshall 	case TELOPT_EOR:
30032377Sminshall 	case TELOPT_BINARY:
30132377Sminshall #	endif	/* defined(TN3270) */
30227676Sminshall 	case TELOPT_TTYPE:		/* terminal type option */
30327110Sminshall 	case TELOPT_SGA:		/* no big deal */
3046000Sroot 		fmt = will;
30527110Sminshall 		myopts[option] = 1;
3066000Sroot 		break;
3076000Sroot 
30827110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
3096000Sroot 	default:
3106000Sroot 		fmt = wont;
3116000Sroot 		break;
3126000Sroot 	}
313*32554Sminshall 	printring(&netoring, fmt, option);
31432377Sminshall 	printoption(">SENT", fmt, option, 0);
3156000Sroot }
31627676Sminshall 
31727676Sminshall /*
31827676Sminshall  * suboption()
31927676Sminshall  *
32027676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
32127676Sminshall  * side.
32227676Sminshall  *
32327676Sminshall  *	Currently we recognize:
32427676Sminshall  *
32527676Sminshall  *		Terminal type, send request.
32627676Sminshall  */
32727676Sminshall 
32832377Sminshall static void
32927676Sminshall suboption()
33027676Sminshall {
33132377Sminshall     printsub("<", subbuffer, subend-subbuffer+1);
33227676Sminshall     switch (subbuffer[0]&0xff) {
33327676Sminshall     case TELOPT_TTYPE:
33427676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
33527676Sminshall 	    ;
33627676Sminshall 	} else {
33727676Sminshall 	    char *name;
33827676Sminshall 	    char namebuf[41];
33932377Sminshall 	    extern char *getenv();
34027676Sminshall 	    int len;
34127676Sminshall 
34232377Sminshall #if	defined(TN3270)
34332531Sminshall 	    if (tn3270_ttype()) {
34432377Sminshall 		return;
34532377Sminshall 	    }
34632377Sminshall #endif	/* defined(TN3270) */
34727676Sminshall 	    name = getenv("TERM");
34827676Sminshall 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
34927676Sminshall 		name = "UNKNOWN";
35027676Sminshall 	    }
35127676Sminshall 	    if ((len + 4+2) < NETROOM()) {
35227676Sminshall 		strcpy(namebuf, name);
35327676Sminshall 		upcase(namebuf);
354*32554Sminshall 		printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
35527676Sminshall 				    TELQUAL_IS, namebuf, IAC, SE);
35632381Sminshall 		/* XXX */
35732381Sminshall 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
35832377Sminshall 	    } else {
35932381Sminshall 		ExitString("No room in buffer for terminal type.\n",
36032377Sminshall 							1);
36132377Sminshall 		/*NOTREACHED*/
36227676Sminshall 	    }
36327676Sminshall 	}
36427676Sminshall 
36527676Sminshall     default:
36627676Sminshall 	break;
36727676Sminshall     }
36827676Sminshall }
36932377Sminshall 
37027088Sminshall 
37132385Sminshall static int
37232377Sminshall telrcv()
37327110Sminshall {
37432377Sminshall     register int c;
37532385Sminshall     register int scc;
37632385Sminshall     register char *sbp;
37732385Sminshall     int count;
37832385Sminshall     int returnValue = 0;
37927088Sminshall 
38032385Sminshall     scc = 0;
38132385Sminshall     count = 0;
38232385Sminshall     while (TTYROOM() > 2) {
38332385Sminshall 	if (scc == 0) {
38432385Sminshall 	    if (count) {
38532528Sminshall 		ring_consumed(&netiring, count);
38632385Sminshall 		returnValue = 1;
38732385Sminshall 		count = 0;
38832385Sminshall 	    }
38932528Sminshall 	    sbp = netiring.consume;
39032528Sminshall 	    scc = ring_full_consecutive(&netiring);
39132385Sminshall 	    if (scc == 0) {
39232385Sminshall 		/* No more data coming in */
39332385Sminshall 		break;
39432385Sminshall 	    }
39532385Sminshall 	}
39632385Sminshall 
39732385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
39832385Sminshall 
39932377Sminshall 	switch (telrcv_state) {
40027110Sminshall 
40132377Sminshall 	case TS_CR:
40232377Sminshall 	    telrcv_state = TS_DATA;
40332377Sminshall 	    if (c == '\0') {
40432377Sminshall 		break;	/* Ignore \0 after CR */
40532377Sminshall 	    } else if (c == '\n') {
40632377Sminshall 		if (hisopts[TELOPT_ECHO] && !crmod) {
40732377Sminshall 		    TTYADD(c);
40832377Sminshall 		}
40932377Sminshall 		break;
41032377Sminshall 	    }
41132377Sminshall 	    /* Else, fall through */
41227088Sminshall 
41332377Sminshall 	case TS_DATA:
41432377Sminshall 	    if (c == IAC) {
41532377Sminshall 		telrcv_state = TS_IAC;
41632377Sminshall 		continue;
41732377Sminshall 	    }
41832377Sminshall #	    if defined(TN3270)
41932377Sminshall 	    if (In3270) {
42032377Sminshall 		*Ifrontp++ = c;
42132385Sminshall 		while (scc > 0) {
42232385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
42332377Sminshall 		    if (c == IAC) {
42432377Sminshall 			telrcv_state = TS_IAC;
42532377Sminshall 			break;
42632377Sminshall 		    }
42732377Sminshall 		    *Ifrontp++ = c;
42832377Sminshall 		}
42932377Sminshall 	    } else
43032377Sminshall #	    endif /* defined(TN3270) */
43132377Sminshall 		    /*
43232377Sminshall 		     * The 'crmod' hack (see following) is needed
43332377Sminshall 		     * since we can't * set CRMOD on output only.
43432377Sminshall 		     * Machines like MULTICS like to send \r without
43532377Sminshall 		     * \n; since we must turn off CRMOD to get proper
43632377Sminshall 		     * input, the mapping is done here (sigh).
43732377Sminshall 		     */
43832377Sminshall 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
43932377Sminshall 		if (scc > 0) {
44032377Sminshall 		    c = *sbp&0xff;
44132377Sminshall 		    if (c == 0) {
44232385Sminshall 			sbp++, scc--; count++;
44332377Sminshall 			/* a "true" CR */
44432377Sminshall 			TTYADD('\r');
44532377Sminshall 		    } else if (!hisopts[TELOPT_ECHO] &&
44632377Sminshall 					(c == '\n')) {
44732385Sminshall 			sbp++, scc--; count++;
44832377Sminshall 			TTYADD('\n');
44932377Sminshall 		    } else {
45032377Sminshall 			TTYADD('\r');
45132377Sminshall 			if (crmod) {
45232377Sminshall 				TTYADD('\n');
45332377Sminshall 			}
45432377Sminshall 		    }
45532377Sminshall 		} else {
45632377Sminshall 		    telrcv_state = TS_CR;
45732377Sminshall 		    TTYADD('\r');
45832377Sminshall 		    if (crmod) {
45932377Sminshall 			    TTYADD('\n');
46032377Sminshall 		    }
46132377Sminshall 		}
46232377Sminshall 	    } else {
46332377Sminshall 		TTYADD(c);
46432377Sminshall 	    }
46532377Sminshall 	    continue;
46627088Sminshall 
46732377Sminshall 	case TS_IAC:
46832377Sminshall 	    switch (c) {
46932377Sminshall 
47032377Sminshall 	    case WILL:
47132377Sminshall 		telrcv_state = TS_WILL;
47232377Sminshall 		continue;
47327261Sminshall 
47432377Sminshall 	    case WONT:
47532377Sminshall 		telrcv_state = TS_WONT;
47632377Sminshall 		continue;
47727261Sminshall 
47832377Sminshall 	    case DO:
47932377Sminshall 		telrcv_state = TS_DO;
48032377Sminshall 		continue;
48127261Sminshall 
48232377Sminshall 	    case DONT:
48332377Sminshall 		telrcv_state = TS_DONT;
48432377Sminshall 		continue;
48527261Sminshall 
48632377Sminshall 	    case DM:
48732377Sminshall 		    /*
48832377Sminshall 		     * We may have missed an urgent notification,
48932377Sminshall 		     * so make sure we flush whatever is in the
49032377Sminshall 		     * buffer currently.
49132377Sminshall 		     */
49232377Sminshall 		SYNCHing = 1;
49332377Sminshall 		ttyflush(1);
494*32554Sminshall 		SYNCHing = stilloob();
49532377Sminshall 		settimer(gotDM);
49632377Sminshall 		break;
49727088Sminshall 
49832377Sminshall 	    case NOP:
49932377Sminshall 	    case GA:
50032377Sminshall 		break;
50127088Sminshall 
50232377Sminshall 	    case SB:
50332377Sminshall 		SB_CLEAR();
50432377Sminshall 		telrcv_state = TS_SB;
50532377Sminshall 		continue;
50627261Sminshall 
50732377Sminshall #	    if defined(TN3270)
50832377Sminshall 	    case EOR:
50932377Sminshall 		if (In3270) {
51032377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
51132377Sminshall 		    if (Ibackp == Ifrontp) {
51232377Sminshall 			Ibackp = Ifrontp = Ibuf;
51332377Sminshall 			ISend = 0;	/* should have been! */
51432377Sminshall 		    } else {
51532377Sminshall 			ISend = 1;
51627088Sminshall 		    }
51727088Sminshall 		}
51827088Sminshall 		break;
51932377Sminshall #	    endif /* defined(TN3270) */
52032377Sminshall 
52132377Sminshall 	    case IAC:
52232377Sminshall #	    if !defined(TN3270)
52332377Sminshall 		TTYADD(IAC);
52432377Sminshall #	    else /* !defined(TN3270) */
52532377Sminshall 		if (In3270) {
52632377Sminshall 		    *Ifrontp++ = IAC;
52732377Sminshall 		} else {
52832377Sminshall 		    TTYADD(IAC);
52932377Sminshall 		}
53032377Sminshall #	    endif /* !defined(TN3270) */
53127088Sminshall 		break;
53232377Sminshall 
53327088Sminshall 	    default:
53427088Sminshall 		break;
53527088Sminshall 	    }
53632377Sminshall 	    telrcv_state = TS_DATA;
53732377Sminshall 	    continue;
53827088Sminshall 
53932377Sminshall 	case TS_WILL:
54032377Sminshall 	    printoption(">RCVD", will, c, !hisopts[c]);
54132377Sminshall 	    if (c == TELOPT_TM) {
54232377Sminshall 		if (flushout) {
54332377Sminshall 		    flushout = 0;
54432377Sminshall 		}
54532377Sminshall 	    } else if (!hisopts[c]) {
54632377Sminshall 		willoption(c, 1);
54732377Sminshall 	    }
54832377Sminshall 	    SetIn3270();
54932377Sminshall 	    telrcv_state = TS_DATA;
55032377Sminshall 	    continue;
55127110Sminshall 
55232377Sminshall 	case TS_WONT:
55332377Sminshall 	    printoption(">RCVD", wont, c, hisopts[c]);
55432377Sminshall 	    if (c == TELOPT_TM) {
55532377Sminshall 		if (flushout) {
55632377Sminshall 		    flushout = 0;
55732377Sminshall 		}
55832377Sminshall 	    } else if (hisopts[c]) {
55932377Sminshall 		wontoption(c, 1);
56032377Sminshall 	    }
56132377Sminshall 	    SetIn3270();
56232377Sminshall 	    telrcv_state = TS_DATA;
56332377Sminshall 	    continue;
56427088Sminshall 
56532377Sminshall 	case TS_DO:
56632377Sminshall 	    printoption(">RCVD", doopt, c, !myopts[c]);
56732377Sminshall 	    if (!myopts[c])
56832377Sminshall 		dooption(c);
56932377Sminshall 	    SetIn3270();
57032377Sminshall 	    telrcv_state = TS_DATA;
57132377Sminshall 	    continue;
57227088Sminshall 
57332377Sminshall 	case TS_DONT:
57432377Sminshall 	    printoption(">RCVD", dont, c, myopts[c]);
57532377Sminshall 	    if (myopts[c]) {
57632377Sminshall 		myopts[c] = 0;
577*32554Sminshall 		printring(&netoring, wont, c);
57832377Sminshall 		flushline = 1;
57932377Sminshall 		setconnmode();	/* set new tty mode (maybe) */
58032377Sminshall 		printoption(">SENT", wont, c, 0);
58132377Sminshall 	    }
58232377Sminshall 	    SetIn3270();
58332377Sminshall 	    telrcv_state = TS_DATA;
58432377Sminshall 	    continue;
58527088Sminshall 
58632377Sminshall 	case TS_SB:
58732377Sminshall 	    if (c == IAC) {
58832377Sminshall 		telrcv_state = TS_SE;
58932377Sminshall 	    } else {
59032377Sminshall 		SB_ACCUM(c);
59132377Sminshall 	    }
59232377Sminshall 	    continue;
59327088Sminshall 
59432377Sminshall 	case TS_SE:
59532377Sminshall 	    if (c != SE) {
59632377Sminshall 		if (c != IAC) {
59732377Sminshall 		    SB_ACCUM(IAC);
59832377Sminshall 		}
59932377Sminshall 		SB_ACCUM(c);
60032377Sminshall 		telrcv_state = TS_SB;
60132377Sminshall 	    } else {
60232377Sminshall 		SB_TERM();
60332377Sminshall 		suboption();	/* handle sub-option */
60432377Sminshall 		SetIn3270();
60532377Sminshall 		telrcv_state = TS_DATA;
60632377Sminshall 	    }
60727088Sminshall 	}
60827088Sminshall     }
60932528Sminshall     ring_consumed(&netiring, count);
61032385Sminshall     return returnValue||count;
61127088Sminshall }
61232385Sminshall 
61332385Sminshall static int
614*32554Sminshall telsnd()
61532385Sminshall {
61632385Sminshall     int tcc;
61732385Sminshall     int count;
61832385Sminshall     int returnValue = 0;
61932385Sminshall     char *tbp;
62032385Sminshall 
62132385Sminshall     tcc = 0;
62232385Sminshall     count = 0;
62332385Sminshall     while (NETROOM() > 2) {
62432385Sminshall 	register int sc;
62532385Sminshall 	register int c;
62632385Sminshall 
62732385Sminshall 	if (tcc == 0) {
62832385Sminshall 	    if (count) {
62932528Sminshall 		ring_consumed(&ttyiring, count);
63032385Sminshall 		returnValue = 1;
63132385Sminshall 		count = 0;
63232385Sminshall 	    }
63332528Sminshall 	    tbp = ttyiring.consume;
63432528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
63532385Sminshall 	    if (tcc == 0) {
63632385Sminshall 		break;
63732385Sminshall 	    }
63832385Sminshall 	}
63932385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
64032385Sminshall 	if (sc == escape) {
64132385Sminshall 	    command(0);
64232385Sminshall 	    tcc = 0;
64332385Sminshall 	    flushline = 1;
64432385Sminshall 	    break;
64532385Sminshall 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
64632385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
64732385Sminshall 		tcc--; tbp++; count++;
64832385Sminshall 	    } else {
64932385Sminshall 		dontlecho = !dontlecho;
65032385Sminshall 		settimer(echotoggle);
65132385Sminshall 		setconnmode();
65232385Sminshall 		flushline = 1;
65332385Sminshall 		break;
65432385Sminshall 	    }
65532385Sminshall 	}
65632385Sminshall 	if (localchars) {
65732385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
65832385Sminshall 		break;
65932385Sminshall 	    }
66032385Sminshall 	}
66132385Sminshall 	if (!myopts[TELOPT_BINARY]) {
66232385Sminshall 	    switch (c) {
66332385Sminshall 	    case '\n':
66432385Sminshall 		    /*
66532385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
66632385Sminshall 		     * on our local machine, then probably
66732385Sminshall 		     * a newline (unix) is CRLF (TELNET).
66832385Sminshall 		     */
66932385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
67032385Sminshall 		    NETADD('\r');
67132385Sminshall 		}
67232385Sminshall 		NETADD('\n');
67332385Sminshall 		flushline = 1;
67432385Sminshall 		break;
67532385Sminshall 	    case '\r':
67632385Sminshall 		if (!crlf) {
67732385Sminshall 		    NET2ADD('\r', '\0');
67832385Sminshall 		} else {
67932385Sminshall 		    NET2ADD('\r', '\n');
68032385Sminshall 		}
68132385Sminshall 		flushline = 1;
68232385Sminshall 		break;
68332385Sminshall 	    case IAC:
68432385Sminshall 		NET2ADD(IAC, IAC);
68532385Sminshall 		break;
68632385Sminshall 	    default:
68732385Sminshall 		NETADD(c);
68832385Sminshall 		break;
68932385Sminshall 	    }
69032385Sminshall 	} else if (c == IAC) {
69132385Sminshall 	    NET2ADD(IAC, IAC);
69232385Sminshall 	} else {
69332385Sminshall 	    NETADD(c);
69432385Sminshall 	}
69532385Sminshall     }
69632528Sminshall     ring_consumed(&ttyiring, count);
69732385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
69832385Sminshall }
69932377Sminshall 
70027088Sminshall /*
70132377Sminshall  * Scheduler()
70232377Sminshall  *
70332377Sminshall  * Try to do something.
70432377Sminshall  *
70532377Sminshall  * If we do something useful, return 1; else return 0.
70632377Sminshall  *
70727110Sminshall  */
70827110Sminshall 
70927110Sminshall 
71032377Sminshall int
71132377Sminshall Scheduler(block)
71232377Sminshall int	block;			/* should we block in the select ? */
71327110Sminshall {
71432377Sminshall     register int c;
71532377Sminshall 		/* One wants to be a bit careful about setting returnValue
71632377Sminshall 		 * to one, since a one implies we did some useful work,
71732377Sminshall 		 * and therefore probably won't be called to block next
71832377Sminshall 		 * time (TN3270 mode only).
71932377Sminshall 		 */
72032531Sminshall     int returnValue;
72132531Sminshall     int netin, netout, netex, ttyin, ttyout;
72227110Sminshall 
72332531Sminshall     /* Decide which rings should be processed */
72432531Sminshall 
72532531Sminshall     netout = ring_full_count(&netoring) &&
72632531Sminshall 	    (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]);
72732531Sminshall     ttyout = ring_full_count(&ttyoring);
72832531Sminshall 
72932377Sminshall #if	defined(TN3270)
73032531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
73132377Sminshall #else	/* defined(TN3270) */
73232531Sminshall     ttyin = ring_empty_count(&ttyiring);
73332377Sminshall #endif	/* defined(TN3270) */
73432531Sminshall 
73532531Sminshall #if	defined(TN3270)
73632531Sminshall     netin = ring_empty_count(&netiring);
73732377Sminshall #   else /* !defined(TN3270) */
73832531Sminshall     netin = !ISend && ring_empty_count(&netiring);
73932377Sminshall #   endif /* !defined(TN3270) */
74032531Sminshall 
74132531Sminshall     netex = !SYNCHing;
74232531Sminshall 
74332531Sminshall     /* If we have seen a signal recently, reset things */
74432377Sminshall #   if defined(TN3270) && defined(unix)
74532377Sminshall     if (HaveInput) {
74632377Sminshall 	HaveInput = 0;
74732377Sminshall 	signal(SIGIO, inputAvailable);
74832377Sminshall     }
74932377Sminshall #endif	/* defined(TN3270) && defined(unix) */
75032377Sminshall 
75132531Sminshall     /* Call to system code to process rings */
75227178Sminshall 
75332531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
75427178Sminshall 
75532531Sminshall     /* Now, look at the input rings, looking for work to do. */
75632377Sminshall 
75732531Sminshall     if (ring_full_count(&ttyiring)) {
75832377Sminshall #   if defined(TN3270)
75932377Sminshall 	if (In3270) {
76032385Sminshall 	    c = DataFromTerminal(ttyiring.send,
76132528Sminshall 					ring_full_consecutive(&ttyiring));
76232377Sminshall 	    if (c) {
76332377Sminshall 		returnValue = 1;
76432377Sminshall 	    }
76532528Sminshall 	    ring_consumed(&ttyiring, c);
76632377Sminshall 	} else {
76732377Sminshall #   endif /* defined(TN3270) */
768*32554Sminshall 	    returnValue |= telsnd();
76932377Sminshall #   if defined(TN3270)
77027178Sminshall 	}
77132531Sminshall #   endif /* defined(TN3270) */
77227178Sminshall     }
77332377Sminshall 
77432528Sminshall     if (ring_full_count(&netiring)) {
77532377Sminshall #	if !defined(TN3270)
77632385Sminshall 	returnValue |= telrcv();
77732377Sminshall #	else /* !defined(TN3270) */
77832377Sminshall 	returnValue = Push3270();
77932377Sminshall #	endif /* !defined(TN3270) */
78032377Sminshall     }
78132377Sminshall     return returnValue;
78227178Sminshall }
78327178Sminshall 
78427178Sminshall /*
78532377Sminshall  * Select from tty and network...
78627088Sminshall  */
78732377Sminshall void
78832377Sminshall telnet()
78927088Sminshall {
79032531Sminshall     sys_telnet_init();
79127088Sminshall 
79232377Sminshall #   if !defined(TN3270)
79332377Sminshall     if (telnetport) {
79432377Sminshall 	if (!hisopts[TELOPT_SGA]) {
79532377Sminshall 	    willoption(TELOPT_SGA, 0);
79627110Sminshall 	}
79732377Sminshall 	if (!myopts[TELOPT_TTYPE]) {
79832377Sminshall 	    dooption(TELOPT_TTYPE, 0);
79932377Sminshall 	}
80027178Sminshall     }
80132377Sminshall #   endif /* !defined(TN3270) */
80227088Sminshall 
80332377Sminshall #   if !defined(TN3270)
80432377Sminshall     for (;;) {
80532385Sminshall 	int schedValue;
80632385Sminshall 
80732385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
80832385Sminshall 	    if (schedValue == -1) {
80932385Sminshall 		setcommandmode();
81032385Sminshall 		return;
81132385Sminshall 	    }
81232385Sminshall 	}
81332385Sminshall 
81432531Sminshall 	if (Scheduler(1) == -1) {
81532377Sminshall 	    setcommandmode();
81632377Sminshall 	    return;
81732377Sminshall 	}
81832377Sminshall     }
81932377Sminshall #   else /* !defined(TN3270) */
82032377Sminshall     for (;;) {
82132377Sminshall 	int schedValue;
82227088Sminshall 
82332377Sminshall 	while (!In3270 && !shell_active) {
82432531Sminshall 	    if (Scheduler(1) == -1) {
82532377Sminshall 		setcommandmode();
82632377Sminshall 		return;
82732377Sminshall 	    }
82827088Sminshall 	}
82932377Sminshall 
83032377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
83132377Sminshall 	    if (schedValue == -1) {
83232377Sminshall 		setcommandmode();
83332377Sminshall 		return;
83432377Sminshall 	    }
83527088Sminshall 	}
83632377Sminshall 		/* If there is data waiting to go out to terminal, don't
83732377Sminshall 		 * schedule any more data for the terminal.
83832377Sminshall 		 */
83932377Sminshall 	if (tfrontp-tbackp) {
84032377Sminshall 	    schedValue = 1;
84127088Sminshall 	} else {
84232377Sminshall 	    if (shell_active) {
84332377Sminshall 		if (shell_continue() == 0) {
84432377Sminshall 		    ConnectScreen();
84527088Sminshall 		}
84632377Sminshall 	    } else if (In3270) {
84732377Sminshall 		schedValue = DoTerminalOutput();
84832377Sminshall 	    }
84927088Sminshall 	}
85032377Sminshall 	if (schedValue && (shell_active == 0)) {
85132531Sminshall 	    if (Scheduler(1) == -1) {
85232377Sminshall 		setcommandmode();
85332377Sminshall 		return;
85432377Sminshall 	    }
85527088Sminshall 	}
85632377Sminshall     }
85732377Sminshall #   endif /* !defined(TN3270) */
85827088Sminshall }
85932377Sminshall 
86027088Sminshall /*
861*32554Sminshall  * nextitem()
862*32554Sminshall  *
863*32554Sminshall  *	Return the address of the next "item" in the TELNET data
864*32554Sminshall  * stream.  This will be the address of the next character if
865*32554Sminshall  * the current address is a user data character, or it will
866*32554Sminshall  * be the address of the character following the TELNET command
867*32554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
868*32554Sminshall  * character.
869*32554Sminshall  */
870*32554Sminshall 
871*32554Sminshall static char *
872*32554Sminshall nextitem(current)
873*32554Sminshall char	*current;
874*32554Sminshall {
875*32554Sminshall     if ((*current&0xff) != IAC) {
876*32554Sminshall 	return current+1;
877*32554Sminshall     }
878*32554Sminshall     switch (*(current+1)&0xff) {
879*32554Sminshall     case DO:
880*32554Sminshall     case DONT:
881*32554Sminshall     case WILL:
882*32554Sminshall     case WONT:
883*32554Sminshall 	return current+3;
884*32554Sminshall     case SB:		/* loop forever looking for the SE */
885*32554Sminshall 	{
886*32554Sminshall 	    register char *look = current+2;
887*32554Sminshall 
888*32554Sminshall 	    for (;;) {
889*32554Sminshall 		if ((*look++&0xff) == IAC) {
890*32554Sminshall 		    if ((*look++&0xff) == SE) {
891*32554Sminshall 			return look;
892*32554Sminshall 		    }
893*32554Sminshall 		}
894*32554Sminshall 	    }
895*32554Sminshall 	}
896*32554Sminshall     default:
897*32554Sminshall 	return current+2;
898*32554Sminshall     }
899*32554Sminshall }
900*32554Sminshall 
901*32554Sminshall /*
902*32554Sminshall  * netclear()
903*32554Sminshall  *
904*32554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
905*32554Sminshall  * the path to the network.
906*32554Sminshall  *
907*32554Sminshall  *	Things are a bit tricky since we may have sent the first
908*32554Sminshall  * byte or so of a previous TELNET command into the network.
909*32554Sminshall  * So, we have to scan the network buffer from the beginning
910*32554Sminshall  * until we are up to where we want to be.
911*32554Sminshall  *
912*32554Sminshall  *	A side effect of what we do, just to keep things
913*32554Sminshall  * simple, is to clear the urgent data pointer.  The principal
914*32554Sminshall  * caller should be setting the urgent data pointer AFTER calling
915*32554Sminshall  * us in any case.
916*32554Sminshall  */
917*32554Sminshall 
918*32554Sminshall static void
919*32554Sminshall netclear()
920*32554Sminshall {
921*32554Sminshall #if	0	/* XXX */
922*32554Sminshall     register char *thisitem, *next;
923*32554Sminshall     char *good;
924*32554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
925*32554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
926*32554Sminshall 
927*32554Sminshall     thisitem = netobuf;
928*32554Sminshall 
929*32554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
930*32554Sminshall 	thisitem = next;
931*32554Sminshall     }
932*32554Sminshall 
933*32554Sminshall     /* Now, thisitem is first before/at boundary. */
934*32554Sminshall 
935*32554Sminshall     good = netobuf;	/* where the good bytes go */
936*32554Sminshall 
937*32554Sminshall     while (netoring.add > thisitem) {
938*32554Sminshall 	if (wewant(thisitem)) {
939*32554Sminshall 	    int length;
940*32554Sminshall 
941*32554Sminshall 	    next = thisitem;
942*32554Sminshall 	    do {
943*32554Sminshall 		next = nextitem(next);
944*32554Sminshall 	    } while (wewant(next) && (nfrontp > next));
945*32554Sminshall 	    length = next-thisitem;
946*32554Sminshall 	    memcpy(good, thisitem, length);
947*32554Sminshall 	    good += length;
948*32554Sminshall 	    thisitem = next;
949*32554Sminshall 	} else {
950*32554Sminshall 	    thisitem = nextitem(thisitem);
951*32554Sminshall 	}
952*32554Sminshall     }
953*32554Sminshall 
954*32554Sminshall #endif	/* 0 */
955*32554Sminshall }
956*32554Sminshall 
957*32554Sminshall /*
95832377Sminshall  * These routines add various telnet commands to the data stream.
95927088Sminshall  */
96032377Sminshall 
961*32554Sminshall static void
962*32554Sminshall doflush()
963*32554Sminshall {
964*32554Sminshall     NET2ADD(IAC, DO);
965*32554Sminshall     NETADD(TELOPT_TM);
966*32554Sminshall     flushline = 1;
967*32554Sminshall     flushout = 1;
968*32554Sminshall     ttyflush(1);			/* Flush/drop output */
969*32554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
970*32554Sminshall     printoption("<SENT", doopt, TELOPT_TM, 0);
971*32554Sminshall }
972*32554Sminshall 
97332377Sminshall void
97432377Sminshall xmitAO()
97527088Sminshall {
97632377Sminshall     NET2ADD(IAC, AO);
97732377Sminshall     if (autoflush) {
97832377Sminshall 	doflush();
97932377Sminshall     }
98032377Sminshall }
98127088Sminshall 
98232377Sminshall 
98332377Sminshall void
98432377Sminshall xmitEL()
98527088Sminshall {
98632377Sminshall     NET2ADD(IAC, EL);
98727088Sminshall }
98827088Sminshall 
98932377Sminshall void
99032377Sminshall xmitEC()
99127088Sminshall {
99232377Sminshall     NET2ADD(IAC, EC);
99327088Sminshall }
99427088Sminshall 
99532377Sminshall 
99632377Sminshall #if	defined(NOT43)
99732377Sminshall int
99832377Sminshall #else	/* defined(NOT43) */
99932377Sminshall void
100032377Sminshall #endif	/* defined(NOT43) */
100132377Sminshall dosynch()
100227088Sminshall {
100332377Sminshall     netclear();			/* clear the path to the network */
100432377Sminshall     NET2ADD(IAC, DM);
100527088Sminshall 
100632377Sminshall #if	defined(NOT43)
100732377Sminshall     return 0;
100832377Sminshall #endif	/* defined(NOT43) */
100927088Sminshall }
101027088Sminshall 
101132377Sminshall void
101232377Sminshall intp()
101327088Sminshall {
101432377Sminshall     NET2ADD(IAC, IP);
101532377Sminshall     flushline = 1;
101632377Sminshall     if (autoflush) {
101732377Sminshall 	doflush();
101832377Sminshall     }
101932377Sminshall     if (autosynch) {
102032377Sminshall 	dosynch();
102132377Sminshall     }
102227088Sminshall }
102327186Sminshall 
102432377Sminshall void
102532377Sminshall sendbrk()
102627186Sminshall {
102732377Sminshall     NET2ADD(IAC, BREAK);
102832377Sminshall     flushline = 1;
102932377Sminshall     if (autoflush) {
103032377Sminshall 	doflush();
103132377Sminshall     }
103232377Sminshall     if (autosynch) {
103332377Sminshall 	dosynch();
103432377Sminshall     }
103527186Sminshall }
1036