xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 33804)
133685Sbostic /*
233685Sbostic  * Copyright (c) 1988 Regents of the University of California.
333685Sbostic  * All rights reserved.
433685Sbostic  *
533685Sbostic  * Redistribution and use in source and binary forms are permitted
633685Sbostic  * provided that this notice is preserved and that due credit is given
733685Sbostic  * to the University of California at Berkeley. The name of the University
833685Sbostic  * may not be used to endorse or promote products derived from this
933685Sbostic  * software without specific prior written permission. This software
1033685Sbostic  * is provided ``as is'' without express or implied warranty.
1133685Sbostic  */
1211758Ssam 
1321580Sdist #ifndef lint
14*33804Sminshall static char sccsid[] = "@(#)telnet.c	5.30 (Berkeley) 03/27/88";
1533685Sbostic #endif /* not lint */
1621580Sdist 
179217Ssam #include <sys/types.h>
189217Ssam 
1932377Sminshall #if	defined(unix)
20*33804Sminshall #include <signal.h>
2132377Sminshall /* By the way, we need to include curses.h before telnet.h since,
2232377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
2332377Sminshall  * declared in curses.h.
2432377Sminshall  */
2532377Sminshall #include <curses.h>
2632377Sminshall #endif	/* defined(unix) */
2732377Sminshall 
2812212Ssam #include <arpa/telnet.h>
2932377Sminshall 
3032377Sminshall #if	defined(unix)
3127186Sminshall #include <strings.h>
3232377Sminshall #else	/* defined(unix) */
3332377Sminshall #include <string.h>
3432377Sminshall #endif	/* defined(unix) */
359217Ssam 
3632381Sminshall #include "ring.h"
3732381Sminshall 
3832377Sminshall #include "defines.h"
3932377Sminshall #include "externs.h"
4032377Sminshall #include "types.h"
4132377Sminshall #include "general.h"
4227178Sminshall 
4327178Sminshall 
4427228Sminshall #define	strip(x)	((x)&0x7f)
456000Sroot 
4627088Sminshall 
4732377Sminshall static char	subbuffer[SUBBUFSIZE],
4832377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
4927676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
5027676Sminshall #define	SB_TERM()	subend = subpointer;
5127676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5227676Sminshall 				*subpointer++ = (c); \
5327676Sminshall 			}
5427676Sminshall 
556000Sroot char	hisopts[256];
566000Sroot char	myopts[256];
576000Sroot 
586000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
596000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
606000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
616000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
626000Sroot 
6332377Sminshall int
6432377Sminshall 	connected,
6532377Sminshall 	showoptions,
6632377Sminshall 	In3270,		/* Are we in 3270 mode? */
6732377Sminshall 	ISend,		/* trying to send network data in */
6832377Sminshall 	debug = 0,
6932377Sminshall 	crmod,
7032377Sminshall 	netdata,	/* Print out network data flow */
7132377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
7232377Sminshall 	noasynch = 0,	/* User specified "-noasynch" on command line */
7332377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
7433286Sminshall 	telnetport,
7532531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
7632531Sminshall 	flushout,	/* flush output */
7732531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
7832531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
7932531Sminshall 	localchars,	/* we recognize interrupt/quit */
8032531Sminshall 	donelclchars,	/* the user has set "localchars" */
8132531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
8232531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
8332531Sminshall 	globalmode;
8427088Sminshall 
8532377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
866000Sroot 
8732377Sminshall char
8832377Sminshall 	*prompt = 0,
8932377Sminshall 	escape,
9032377Sminshall 	echoc;
9127186Sminshall 
9227186Sminshall /*
936000Sroot  * Telnet receiver states for fsm
946000Sroot  */
956000Sroot #define	TS_DATA		0
966000Sroot #define	TS_IAC		1
976000Sroot #define	TS_WILL		2
986000Sroot #define	TS_WONT		3
996000Sroot #define	TS_DO		4
1006000Sroot #define	TS_DONT		5
10127021Sminshall #define	TS_CR		6
10227676Sminshall #define	TS_SB		7		/* sub-option collection */
10327676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1046000Sroot 
10532377Sminshall static int	telrcv_state;
1066000Sroot 
10732377Sminshall jmp_buf	toplevel = { 0 };
10832377Sminshall jmp_buf	peerdied;
1096000Sroot 
11032377Sminshall int	flushline;
11127021Sminshall 
11232377Sminshall /*
11332377Sminshall  * The following are some clocks used to decide how to interpret
11432377Sminshall  * the relationship between various variables.
11532377Sminshall  */
1166000Sroot 
11732377Sminshall Clocks clocks;
11832377Sminshall 
11932377Sminshall Modelist modelist[] = {
12032377Sminshall 	{ "telnet command mode", COMMAND_LINE },
12132377Sminshall 	{ "character-at-a-time mode", 0 },
12232377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
12332377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
12432377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
12532377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
12632377Sminshall 	{ "3270 mode", 0 },
12732377Sminshall };
1286000Sroot 
12932377Sminshall 
13032377Sminshall /*
13132377Sminshall  * Initialize telnet environment.
13232377Sminshall  */
1336000Sroot 
13432377Sminshall init_telnet()
13532377Sminshall {
13632377Sminshall     SB_CLEAR();
13732377Sminshall     ClearArray(hisopts);
13832377Sminshall     ClearArray(myopts);
1396000Sroot 
14032554Sminshall     connected = In3270 = ISend = donebinarytoggle = 0;
1416000Sroot 
14232377Sminshall #if	defined(unix) && defined(TN3270)
14332377Sminshall     HaveInput = 0;
14432377Sminshall #endif	/* defined(unix) && defined(TN3270) */
1456000Sroot 
14632377Sminshall     SYNCHing = 0;
1476000Sroot 
14832377Sminshall     /* Don't change NetTrace */
1496000Sroot 
15032377Sminshall     escape = CONTROL(']');
15132377Sminshall     echoc = CONTROL('E');
1526000Sroot 
15332377Sminshall     flushline = 1;
15432377Sminshall     telrcv_state = TS_DATA;
15532377Sminshall }
15632554Sminshall 
1576000Sroot 
15832554Sminshall #include <varargs.h>
1596000Sroot 
16032554Sminshall static void
16132554Sminshall printring(va_alist)
16232554Sminshall va_dcl
16332554Sminshall {
16432554Sminshall     va_list ap;
16532554Sminshall     char buffer[100];		/* where things go */
16632554Sminshall     char *ptr;
16732554Sminshall     char *format;
16832554Sminshall     char *string;
16932554Sminshall     Ring *ring;
17032554Sminshall     int i;
17132554Sminshall 
17232554Sminshall     va_start(ap);
17332554Sminshall 
17432554Sminshall     ring = va_arg(ap, Ring *);
17532554Sminshall     format = va_arg(ap, char *);
17632554Sminshall     ptr = buffer;
17732554Sminshall 
17832554Sminshall     while ((i = *format++) != 0) {
17932554Sminshall 	if (i == '%') {
18032554Sminshall 	    i = *format++;
18132554Sminshall 	    switch (i) {
18232554Sminshall 	    case 'c':
18332554Sminshall 		*ptr++ = va_arg(ap, int);
18432554Sminshall 		break;
18532554Sminshall 	    case 's':
18632554Sminshall 		string = va_arg(ap, char *);
18732554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
18832554Sminshall 		ring_supply_data(ring, string, strlen(string));
18932554Sminshall 		ptr = buffer;
19032554Sminshall 		break;
19132554Sminshall 	    case 0:
19232554Sminshall 		ExitString("printring: trailing %%.\n", 1);
19332554Sminshall 		/*NOTREACHED*/
19432554Sminshall 	    default:
19532554Sminshall 		ExitString("printring: unknown format character.\n", 1);
19632554Sminshall 		/*NOTREACHED*/
19732554Sminshall 	    }
19832554Sminshall 	} else {
19932554Sminshall 	    *ptr++ = i;
20032554Sminshall 	}
20132554Sminshall     }
20232554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
20332554Sminshall }
20432554Sminshall 
20532554Sminshall 
20632377Sminshall void
20727676Sminshall willoption(option, reply)
20827676Sminshall 	int option, reply;
2096000Sroot {
2106000Sroot 	char *fmt;
2116000Sroot 
2126000Sroot 	switch (option) {
2136000Sroot 
2146000Sroot 	case TELOPT_ECHO:
21532377Sminshall #	if defined(TN3270)
21632377Sminshall 	    /*
21732377Sminshall 	     * The following is a pain in the rear-end.
21832377Sminshall 	     * Various IBM servers (some versions of Wiscnet,
21932377Sminshall 	     * possibly Fibronics/Spartacus, and who knows who
22032377Sminshall 	     * else) will NOT allow us to send "DO SGA" too early
22132377Sminshall 	     * in the setup proceedings.  On the other hand,
22232377Sminshall 	     * 4.2 servers (telnetd) won't set SGA correctly.
22332377Sminshall 	     * So, we are stuck.  Empirically (but, based on
22432377Sminshall 	     * a VERY small sample), the IBM servers don't send
22532377Sminshall 	     * out anything about ECHO, so we postpone our sending
22632377Sminshall 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
22732377Sminshall 	     * DO send).
22832377Sminshall 	     */
22932377Sminshall 	    {
23032377Sminshall 		if (askedSGA == 0) {
23132377Sminshall 		    askedSGA = 1;
23232377Sminshall 		    if (!hisopts[TELOPT_SGA]) {
23332377Sminshall 			willoption(TELOPT_SGA, 0);
23432377Sminshall 		    }
23532377Sminshall 		}
23632377Sminshall 	    }
23732377Sminshall 		/* Fall through */
23832377Sminshall 	case TELOPT_EOR:
23932377Sminshall 	case TELOPT_BINARY:
24032377Sminshall #endif	/* defined(TN3270) */
2416000Sroot 	case TELOPT_SGA:
24227110Sminshall 		settimer(modenegotiated);
2436000Sroot 		hisopts[option] = 1;
2446000Sroot 		fmt = doopt;
24527110Sminshall 		setconnmode();		/* possibly set new tty mode */
2466000Sroot 		break;
2476000Sroot 
2486000Sroot 	case TELOPT_TM:
24927110Sminshall 		return;			/* Never reply to TM will's/wont's */
2506000Sroot 
2516000Sroot 	default:
2526000Sroot 		fmt = dont;
2536000Sroot 		break;
2546000Sroot 	}
25532554Sminshall 	printring(&netoring, fmt, option);
25627676Sminshall 	if (reply)
25732377Sminshall 		printoption(">SENT", fmt, option, reply);
25827676Sminshall 	else
25932377Sminshall 		printoption("<SENT", fmt, option, reply);
2606000Sroot }
2616000Sroot 
26232377Sminshall void
26327676Sminshall wontoption(option, reply)
26427676Sminshall 	int option, reply;
2656000Sroot {
2666000Sroot 	char *fmt;
2676000Sroot 
2686000Sroot 	switch (option) {
2696000Sroot 
2706000Sroot 	case TELOPT_ECHO:
2716000Sroot 	case TELOPT_SGA:
27227110Sminshall 		settimer(modenegotiated);
2736000Sroot 		hisopts[option] = 0;
2746000Sroot 		fmt = dont;
27527110Sminshall 		setconnmode();			/* Set new tty mode */
2766000Sroot 		break;
2776000Sroot 
27827110Sminshall 	case TELOPT_TM:
27927110Sminshall 		return;		/* Never reply to TM will's/wont's */
28027110Sminshall 
2816000Sroot 	default:
2826000Sroot 		fmt = dont;
2836000Sroot 	}
28432554Sminshall 	printring(&netoring, fmt, option);
28527676Sminshall 	if (reply)
28632377Sminshall 		printoption(">SENT", fmt, option, reply);
28727676Sminshall 	else
28832377Sminshall 		printoption("<SENT", fmt, option, reply);
2896000Sroot }
2906000Sroot 
29132377Sminshall static void
2926000Sroot dooption(option)
2936000Sroot 	int option;
2946000Sroot {
2956000Sroot 	char *fmt;
2966000Sroot 
2976000Sroot 	switch (option) {
2986000Sroot 
2996000Sroot 	case TELOPT_TM:
30013231Ssam 		fmt = will;
30113231Ssam 		break;
30213231Ssam 
30332377Sminshall #	if defined(TN3270)
30432377Sminshall 	case TELOPT_EOR:
30532377Sminshall 	case TELOPT_BINARY:
30632377Sminshall #	endif	/* defined(TN3270) */
30727676Sminshall 	case TELOPT_TTYPE:		/* terminal type option */
30827110Sminshall 	case TELOPT_SGA:		/* no big deal */
3096000Sroot 		fmt = will;
31027110Sminshall 		myopts[option] = 1;
3116000Sroot 		break;
3126000Sroot 
31327110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
3146000Sroot 	default:
3156000Sroot 		fmt = wont;
3166000Sroot 		break;
3176000Sroot 	}
31832554Sminshall 	printring(&netoring, fmt, option);
31932377Sminshall 	printoption(">SENT", fmt, option, 0);
3206000Sroot }
32127676Sminshall 
32227676Sminshall /*
32327676Sminshall  * suboption()
32427676Sminshall  *
32527676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
32627676Sminshall  * side.
32727676Sminshall  *
32827676Sminshall  *	Currently we recognize:
32927676Sminshall  *
33027676Sminshall  *		Terminal type, send request.
33127676Sminshall  */
33227676Sminshall 
33332377Sminshall static void
33427676Sminshall suboption()
33527676Sminshall {
33632377Sminshall     printsub("<", subbuffer, subend-subbuffer+1);
33727676Sminshall     switch (subbuffer[0]&0xff) {
33827676Sminshall     case TELOPT_TTYPE:
33927676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
34027676Sminshall 	    ;
34127676Sminshall 	} else {
34227676Sminshall 	    char *name;
34327676Sminshall 	    char namebuf[41];
34432377Sminshall 	    extern char *getenv();
34527676Sminshall 	    int len;
34627676Sminshall 
34732377Sminshall #if	defined(TN3270)
34832531Sminshall 	    if (tn3270_ttype()) {
34932377Sminshall 		return;
35032377Sminshall 	    }
35132377Sminshall #endif	/* defined(TN3270) */
35227676Sminshall 	    name = getenv("TERM");
35327676Sminshall 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
35427676Sminshall 		name = "UNKNOWN";
35533492Sminshall 		len = strlen(name);
35627676Sminshall 	    }
35727676Sminshall 	    if ((len + 4+2) < NETROOM()) {
35827676Sminshall 		strcpy(namebuf, name);
35927676Sminshall 		upcase(namebuf);
36032554Sminshall 		printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
36127676Sminshall 				    TELQUAL_IS, namebuf, IAC, SE);
36232381Sminshall 		/* XXX */
36332381Sminshall 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
36432377Sminshall 	    } else {
36532381Sminshall 		ExitString("No room in buffer for terminal type.\n",
36632377Sminshall 							1);
36732377Sminshall 		/*NOTREACHED*/
36827676Sminshall 	    }
36927676Sminshall 	}
37027676Sminshall 
37127676Sminshall     default:
37227676Sminshall 	break;
37327676Sminshall     }
37427676Sminshall }
37532377Sminshall 
37627088Sminshall 
377*33804Sminshall int
37832377Sminshall telrcv()
37927110Sminshall {
38032377Sminshall     register int c;
38132385Sminshall     register int scc;
38232385Sminshall     register char *sbp;
38332385Sminshall     int count;
38432385Sminshall     int returnValue = 0;
38527088Sminshall 
38632385Sminshall     scc = 0;
38732385Sminshall     count = 0;
38832385Sminshall     while (TTYROOM() > 2) {
38932385Sminshall 	if (scc == 0) {
39032385Sminshall 	    if (count) {
39132528Sminshall 		ring_consumed(&netiring, count);
39232385Sminshall 		returnValue = 1;
39332385Sminshall 		count = 0;
39432385Sminshall 	    }
39532528Sminshall 	    sbp = netiring.consume;
39632528Sminshall 	    scc = ring_full_consecutive(&netiring);
39732385Sminshall 	    if (scc == 0) {
39832385Sminshall 		/* No more data coming in */
39932385Sminshall 		break;
40032385Sminshall 	    }
40132385Sminshall 	}
40232385Sminshall 
40332385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
40432385Sminshall 
40532377Sminshall 	switch (telrcv_state) {
40627110Sminshall 
40732377Sminshall 	case TS_CR:
40832377Sminshall 	    telrcv_state = TS_DATA;
40932377Sminshall 	    if (c == '\0') {
41032377Sminshall 		break;	/* Ignore \0 after CR */
41132377Sminshall 	    } else if (c == '\n') {
41232657Sminshall 		if ((!hisopts[TELOPT_ECHO]) && !crmod) {
41332377Sminshall 		    TTYADD(c);
41432377Sminshall 		}
41532377Sminshall 		break;
41632377Sminshall 	    }
41732377Sminshall 	    /* Else, fall through */
41827088Sminshall 
41932377Sminshall 	case TS_DATA:
42032377Sminshall 	    if (c == IAC) {
42132377Sminshall 		telrcv_state = TS_IAC;
422*33804Sminshall 		break;
42332377Sminshall 	    }
42432377Sminshall #	    if defined(TN3270)
42532377Sminshall 	    if (In3270) {
42632377Sminshall 		*Ifrontp++ = c;
42732385Sminshall 		while (scc > 0) {
42832385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
42932377Sminshall 		    if (c == IAC) {
43032377Sminshall 			telrcv_state = TS_IAC;
431*33804Sminshall 			continue;
43232377Sminshall 		    }
43332377Sminshall 		    *Ifrontp++ = c;
43432377Sminshall 		}
43532377Sminshall 	    } else
43632377Sminshall #	    endif /* defined(TN3270) */
43732377Sminshall 		    /*
43832377Sminshall 		     * The 'crmod' hack (see following) is needed
43932377Sminshall 		     * since we can't * set CRMOD on output only.
44032377Sminshall 		     * Machines like MULTICS like to send \r without
44132377Sminshall 		     * \n; since we must turn off CRMOD to get proper
44232377Sminshall 		     * input, the mapping is done here (sigh).
44332377Sminshall 		     */
44432377Sminshall 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
44532377Sminshall 		if (scc > 0) {
44632377Sminshall 		    c = *sbp&0xff;
44732377Sminshall 		    if (c == 0) {
44832385Sminshall 			sbp++, scc--; count++;
44932377Sminshall 			/* a "true" CR */
45032377Sminshall 			TTYADD('\r');
45132377Sminshall 		    } else if (!hisopts[TELOPT_ECHO] &&
45232377Sminshall 					(c == '\n')) {
45332385Sminshall 			sbp++, scc--; count++;
45432377Sminshall 			TTYADD('\n');
45532377Sminshall 		    } else {
45632377Sminshall 			TTYADD('\r');
45732377Sminshall 			if (crmod) {
45832377Sminshall 				TTYADD('\n');
45932377Sminshall 			}
46032377Sminshall 		    }
46132377Sminshall 		} else {
46232377Sminshall 		    telrcv_state = TS_CR;
46332377Sminshall 		    TTYADD('\r');
46432377Sminshall 		    if (crmod) {
46532377Sminshall 			    TTYADD('\n');
46632377Sminshall 		    }
46732377Sminshall 		}
46832377Sminshall 	    } else {
46932377Sminshall 		TTYADD(c);
47032377Sminshall 	    }
47132377Sminshall 	    continue;
47227088Sminshall 
47332377Sminshall 	case TS_IAC:
47432377Sminshall 	    switch (c) {
47532377Sminshall 
47632377Sminshall 	    case WILL:
47732377Sminshall 		telrcv_state = TS_WILL;
47832377Sminshall 		continue;
47927261Sminshall 
48032377Sminshall 	    case WONT:
48132377Sminshall 		telrcv_state = TS_WONT;
48232377Sminshall 		continue;
48327261Sminshall 
48432377Sminshall 	    case DO:
48532377Sminshall 		telrcv_state = TS_DO;
48632377Sminshall 		continue;
48727261Sminshall 
48832377Sminshall 	    case DONT:
48932377Sminshall 		telrcv_state = TS_DONT;
49032377Sminshall 		continue;
49127261Sminshall 
49232377Sminshall 	    case DM:
49332377Sminshall 		    /*
49432377Sminshall 		     * We may have missed an urgent notification,
49532377Sminshall 		     * so make sure we flush whatever is in the
49632377Sminshall 		     * buffer currently.
49732377Sminshall 		     */
49832377Sminshall 		SYNCHing = 1;
49932377Sminshall 		ttyflush(1);
50032554Sminshall 		SYNCHing = stilloob();
50132377Sminshall 		settimer(gotDM);
50232377Sminshall 		break;
50327088Sminshall 
50432377Sminshall 	    case NOP:
50532377Sminshall 	    case GA:
50632377Sminshall 		break;
50727088Sminshall 
50832377Sminshall 	    case SB:
50932377Sminshall 		SB_CLEAR();
51032377Sminshall 		telrcv_state = TS_SB;
51132377Sminshall 		continue;
51227261Sminshall 
51332377Sminshall #	    if defined(TN3270)
51432377Sminshall 	    case EOR:
51532377Sminshall 		if (In3270) {
51632377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
51732377Sminshall 		    if (Ibackp == Ifrontp) {
51832377Sminshall 			Ibackp = Ifrontp = Ibuf;
51932377Sminshall 			ISend = 0;	/* should have been! */
52032377Sminshall 		    } else {
52132377Sminshall 			ISend = 1;
52227088Sminshall 		    }
52327088Sminshall 		}
52427088Sminshall 		break;
52532377Sminshall #	    endif /* defined(TN3270) */
52632377Sminshall 
52732377Sminshall 	    case IAC:
52832377Sminshall #	    if !defined(TN3270)
52932377Sminshall 		TTYADD(IAC);
53032377Sminshall #	    else /* !defined(TN3270) */
53132377Sminshall 		if (In3270) {
53232377Sminshall 		    *Ifrontp++ = IAC;
53332377Sminshall 		} else {
53432377Sminshall 		    TTYADD(IAC);
53532377Sminshall 		}
53632377Sminshall #	    endif /* !defined(TN3270) */
53727088Sminshall 		break;
53832377Sminshall 
53927088Sminshall 	    default:
54027088Sminshall 		break;
54127088Sminshall 	    }
54232377Sminshall 	    telrcv_state = TS_DATA;
54332377Sminshall 	    continue;
54427088Sminshall 
54532377Sminshall 	case TS_WILL:
54632377Sminshall 	    printoption(">RCVD", will, c, !hisopts[c]);
54732377Sminshall 	    if (c == TELOPT_TM) {
54832377Sminshall 		if (flushout) {
54932377Sminshall 		    flushout = 0;
55032377Sminshall 		}
55132377Sminshall 	    } else if (!hisopts[c]) {
55232377Sminshall 		willoption(c, 1);
55332377Sminshall 	    }
55432377Sminshall 	    SetIn3270();
55532377Sminshall 	    telrcv_state = TS_DATA;
55632377Sminshall 	    continue;
55727110Sminshall 
55832377Sminshall 	case TS_WONT:
55932377Sminshall 	    printoption(">RCVD", wont, c, hisopts[c]);
56032377Sminshall 	    if (c == TELOPT_TM) {
56132377Sminshall 		if (flushout) {
56232377Sminshall 		    flushout = 0;
56332377Sminshall 		}
56432377Sminshall 	    } else if (hisopts[c]) {
56532377Sminshall 		wontoption(c, 1);
56632377Sminshall 	    }
56732377Sminshall 	    SetIn3270();
56832377Sminshall 	    telrcv_state = TS_DATA;
56932377Sminshall 	    continue;
57027088Sminshall 
57132377Sminshall 	case TS_DO:
57232377Sminshall 	    printoption(">RCVD", doopt, c, !myopts[c]);
57332377Sminshall 	    if (!myopts[c])
57432377Sminshall 		dooption(c);
57532377Sminshall 	    SetIn3270();
57632377Sminshall 	    telrcv_state = TS_DATA;
57732377Sminshall 	    continue;
57827088Sminshall 
57932377Sminshall 	case TS_DONT:
58032377Sminshall 	    printoption(">RCVD", dont, c, myopts[c]);
58132377Sminshall 	    if (myopts[c]) {
58232377Sminshall 		myopts[c] = 0;
58332554Sminshall 		printring(&netoring, wont, c);
58432377Sminshall 		flushline = 1;
58532377Sminshall 		setconnmode();	/* set new tty mode (maybe) */
58632377Sminshall 		printoption(">SENT", wont, c, 0);
58732377Sminshall 	    }
58832377Sminshall 	    SetIn3270();
58932377Sminshall 	    telrcv_state = TS_DATA;
59032377Sminshall 	    continue;
59127088Sminshall 
59232377Sminshall 	case TS_SB:
59332377Sminshall 	    if (c == IAC) {
59432377Sminshall 		telrcv_state = TS_SE;
59532377Sminshall 	    } else {
59632377Sminshall 		SB_ACCUM(c);
59732377Sminshall 	    }
59832377Sminshall 	    continue;
59927088Sminshall 
60032377Sminshall 	case TS_SE:
60132377Sminshall 	    if (c != SE) {
60232377Sminshall 		if (c != IAC) {
60332377Sminshall 		    SB_ACCUM(IAC);
60432377Sminshall 		}
60532377Sminshall 		SB_ACCUM(c);
60632377Sminshall 		telrcv_state = TS_SB;
60732377Sminshall 	    } else {
60832377Sminshall 		SB_TERM();
60932377Sminshall 		suboption();	/* handle sub-option */
61032377Sminshall 		SetIn3270();
61132377Sminshall 		telrcv_state = TS_DATA;
61232377Sminshall 	    }
61327088Sminshall 	}
61427088Sminshall     }
61532667Sminshall     if (count)
61632667Sminshall 	ring_consumed(&netiring, count);
61732385Sminshall     return returnValue||count;
61827088Sminshall }
61932385Sminshall 
62032385Sminshall static int
62132554Sminshall telsnd()
62232385Sminshall {
62332385Sminshall     int tcc;
62432385Sminshall     int count;
62532385Sminshall     int returnValue = 0;
62632385Sminshall     char *tbp;
62732385Sminshall 
62832385Sminshall     tcc = 0;
62932385Sminshall     count = 0;
63032385Sminshall     while (NETROOM() > 2) {
63132385Sminshall 	register int sc;
63232385Sminshall 	register int c;
63332385Sminshall 
63432385Sminshall 	if (tcc == 0) {
63532385Sminshall 	    if (count) {
63632528Sminshall 		ring_consumed(&ttyiring, count);
63732385Sminshall 		returnValue = 1;
63832385Sminshall 		count = 0;
63932385Sminshall 	    }
64032528Sminshall 	    tbp = ttyiring.consume;
64132528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
64232385Sminshall 	    if (tcc == 0) {
64332385Sminshall 		break;
64432385Sminshall 	    }
64532385Sminshall 	}
64632385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
64732385Sminshall 	if (sc == escape) {
64832385Sminshall 	    command(0);
64932385Sminshall 	    tcc = 0;
65032385Sminshall 	    flushline = 1;
65132385Sminshall 	    break;
65232385Sminshall 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
65332385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
65432385Sminshall 		tcc--; tbp++; count++;
65532385Sminshall 	    } else {
65632385Sminshall 		dontlecho = !dontlecho;
65732385Sminshall 		settimer(echotoggle);
65832385Sminshall 		setconnmode();
65932385Sminshall 		flushline = 1;
66032385Sminshall 		break;
66132385Sminshall 	    }
66232385Sminshall 	}
66332385Sminshall 	if (localchars) {
66432385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
66532385Sminshall 		break;
66632385Sminshall 	    }
66732385Sminshall 	}
66832385Sminshall 	if (!myopts[TELOPT_BINARY]) {
66932385Sminshall 	    switch (c) {
67032385Sminshall 	    case '\n':
67132385Sminshall 		    /*
67232385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
67332385Sminshall 		     * on our local machine, then probably
67432385Sminshall 		     * a newline (unix) is CRLF (TELNET).
67532385Sminshall 		     */
67632385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
67732385Sminshall 		    NETADD('\r');
67832385Sminshall 		}
67932385Sminshall 		NETADD('\n');
68032385Sminshall 		flushline = 1;
68132385Sminshall 		break;
68232385Sminshall 	    case '\r':
68332385Sminshall 		if (!crlf) {
68432385Sminshall 		    NET2ADD('\r', '\0');
68532385Sminshall 		} else {
68632385Sminshall 		    NET2ADD('\r', '\n');
68732385Sminshall 		}
68832385Sminshall 		flushline = 1;
68932385Sminshall 		break;
69032385Sminshall 	    case IAC:
69132385Sminshall 		NET2ADD(IAC, IAC);
69232385Sminshall 		break;
69332385Sminshall 	    default:
69432385Sminshall 		NETADD(c);
69532385Sminshall 		break;
69632385Sminshall 	    }
69732385Sminshall 	} else if (c == IAC) {
69832385Sminshall 	    NET2ADD(IAC, IAC);
69932385Sminshall 	} else {
70032385Sminshall 	    NETADD(c);
70132385Sminshall 	}
70232385Sminshall     }
70332667Sminshall     if (count)
70432667Sminshall 	ring_consumed(&ttyiring, count);
70532385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
70632385Sminshall }
70732377Sminshall 
70827088Sminshall /*
70932377Sminshall  * Scheduler()
71032377Sminshall  *
71132377Sminshall  * Try to do something.
71232377Sminshall  *
71332377Sminshall  * If we do something useful, return 1; else return 0.
71432377Sminshall  *
71527110Sminshall  */
71627110Sminshall 
71727110Sminshall 
71832377Sminshall int
71932377Sminshall Scheduler(block)
72032377Sminshall int	block;			/* should we block in the select ? */
72127110Sminshall {
72232377Sminshall     register int c;
72332377Sminshall 		/* One wants to be a bit careful about setting returnValue
72432377Sminshall 		 * to one, since a one implies we did some useful work,
72532377Sminshall 		 * and therefore probably won't be called to block next
72632377Sminshall 		 * time (TN3270 mode only).
72732377Sminshall 		 */
72832531Sminshall     int returnValue;
72932531Sminshall     int netin, netout, netex, ttyin, ttyout;
73027110Sminshall 
73132531Sminshall     /* Decide which rings should be processed */
73232531Sminshall 
73332531Sminshall     netout = ring_full_count(&netoring) &&
73432531Sminshall 	    (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]);
73532531Sminshall     ttyout = ring_full_count(&ttyoring);
73632531Sminshall 
73732377Sminshall #if	defined(TN3270)
73832531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
73932377Sminshall #else	/* defined(TN3270) */
74032531Sminshall     ttyin = ring_empty_count(&ttyiring);
74132377Sminshall #endif	/* defined(TN3270) */
74232531Sminshall 
74332531Sminshall #if	defined(TN3270)
74432531Sminshall     netin = ring_empty_count(&netiring);
74532377Sminshall #   else /* !defined(TN3270) */
74632531Sminshall     netin = !ISend && ring_empty_count(&netiring);
74732377Sminshall #   endif /* !defined(TN3270) */
74832531Sminshall 
74932531Sminshall     netex = !SYNCHing;
75032531Sminshall 
75132531Sminshall     /* If we have seen a signal recently, reset things */
75232377Sminshall #   if defined(TN3270) && defined(unix)
75332377Sminshall     if (HaveInput) {
75432377Sminshall 	HaveInput = 0;
75532377Sminshall 	signal(SIGIO, inputAvailable);
75632377Sminshall     }
75732377Sminshall #endif	/* defined(TN3270) && defined(unix) */
75832377Sminshall 
75932531Sminshall     /* Call to system code to process rings */
76027178Sminshall 
76132531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
76227178Sminshall 
76332531Sminshall     /* Now, look at the input rings, looking for work to do. */
76432377Sminshall 
76532531Sminshall     if (ring_full_count(&ttyiring)) {
76632377Sminshall #   if defined(TN3270)
76732377Sminshall 	if (In3270) {
768*33804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
76932528Sminshall 					ring_full_consecutive(&ttyiring));
77032377Sminshall 	    if (c) {
77132377Sminshall 		returnValue = 1;
77232667Sminshall 	        ring_consumed(&ttyiring, c);
77332377Sminshall 	    }
77432377Sminshall 	} else {
77532377Sminshall #   endif /* defined(TN3270) */
77632554Sminshall 	    returnValue |= telsnd();
77732377Sminshall #   if defined(TN3270)
77827178Sminshall 	}
77932531Sminshall #   endif /* defined(TN3270) */
78027178Sminshall     }
78132377Sminshall 
78232528Sminshall     if (ring_full_count(&netiring)) {
78332377Sminshall #	if !defined(TN3270)
78432385Sminshall 	returnValue |= telrcv();
78532377Sminshall #	else /* !defined(TN3270) */
78632377Sminshall 	returnValue = Push3270();
78732377Sminshall #	endif /* !defined(TN3270) */
78832377Sminshall     }
78932377Sminshall     return returnValue;
79027178Sminshall }
79127178Sminshall 
79227178Sminshall /*
79332377Sminshall  * Select from tty and network...
79427088Sminshall  */
79532377Sminshall void
79632377Sminshall telnet()
79727088Sminshall {
79832531Sminshall     sys_telnet_init();
79927088Sminshall 
80032377Sminshall #   if !defined(TN3270)
80132377Sminshall     if (telnetport) {
80232377Sminshall 	if (!hisopts[TELOPT_SGA]) {
80332377Sminshall 	    willoption(TELOPT_SGA, 0);
80427110Sminshall 	}
80532377Sminshall 	if (!myopts[TELOPT_TTYPE]) {
80632377Sminshall 	    dooption(TELOPT_TTYPE, 0);
80732377Sminshall 	}
80827178Sminshall     }
80932377Sminshall #   endif /* !defined(TN3270) */
81027088Sminshall 
81132377Sminshall #   if !defined(TN3270)
81232377Sminshall     for (;;) {
81332385Sminshall 	int schedValue;
81432385Sminshall 
81532385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
81632385Sminshall 	    if (schedValue == -1) {
81732385Sminshall 		setcommandmode();
81832385Sminshall 		return;
81932385Sminshall 	    }
82032385Sminshall 	}
82132385Sminshall 
82232531Sminshall 	if (Scheduler(1) == -1) {
82332377Sminshall 	    setcommandmode();
82432377Sminshall 	    return;
82532377Sminshall 	}
82632377Sminshall     }
82732377Sminshall #   else /* !defined(TN3270) */
82832377Sminshall     for (;;) {
82932377Sminshall 	int schedValue;
83027088Sminshall 
83132377Sminshall 	while (!In3270 && !shell_active) {
83232531Sminshall 	    if (Scheduler(1) == -1) {
83332377Sminshall 		setcommandmode();
83432377Sminshall 		return;
83532377Sminshall 	    }
83627088Sminshall 	}
83732377Sminshall 
83832377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
83932377Sminshall 	    if (schedValue == -1) {
84032377Sminshall 		setcommandmode();
84132377Sminshall 		return;
84232377Sminshall 	    }
84327088Sminshall 	}
84432377Sminshall 		/* If there is data waiting to go out to terminal, don't
84532377Sminshall 		 * schedule any more data for the terminal.
84632377Sminshall 		 */
847*33804Sminshall 	if (ring_full_count(ttyoring)) {
84832377Sminshall 	    schedValue = 1;
84927088Sminshall 	} else {
85032377Sminshall 	    if (shell_active) {
85132377Sminshall 		if (shell_continue() == 0) {
85232377Sminshall 		    ConnectScreen();
85327088Sminshall 		}
85432377Sminshall 	    } else if (In3270) {
85532377Sminshall 		schedValue = DoTerminalOutput();
85632377Sminshall 	    }
85727088Sminshall 	}
85832377Sminshall 	if (schedValue && (shell_active == 0)) {
85932531Sminshall 	    if (Scheduler(1) == -1) {
86032377Sminshall 		setcommandmode();
86132377Sminshall 		return;
86232377Sminshall 	    }
86327088Sminshall 	}
86432377Sminshall     }
86532377Sminshall #   endif /* !defined(TN3270) */
86627088Sminshall }
86732377Sminshall 
86827088Sminshall /*
86932554Sminshall  * nextitem()
87032554Sminshall  *
87132554Sminshall  *	Return the address of the next "item" in the TELNET data
87232554Sminshall  * stream.  This will be the address of the next character if
87332554Sminshall  * the current address is a user data character, or it will
87432554Sminshall  * be the address of the character following the TELNET command
87532554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
87632554Sminshall  * character.
87732554Sminshall  */
87832554Sminshall 
87932554Sminshall static char *
88032554Sminshall nextitem(current)
88132554Sminshall char	*current;
88232554Sminshall {
88332554Sminshall     if ((*current&0xff) != IAC) {
88432554Sminshall 	return current+1;
88532554Sminshall     }
88632554Sminshall     switch (*(current+1)&0xff) {
88732554Sminshall     case DO:
88832554Sminshall     case DONT:
88932554Sminshall     case WILL:
89032554Sminshall     case WONT:
89132554Sminshall 	return current+3;
89232554Sminshall     case SB:		/* loop forever looking for the SE */
89332554Sminshall 	{
89432554Sminshall 	    register char *look = current+2;
89532554Sminshall 
89632554Sminshall 	    for (;;) {
89732554Sminshall 		if ((*look++&0xff) == IAC) {
89832554Sminshall 		    if ((*look++&0xff) == SE) {
89932554Sminshall 			return look;
90032554Sminshall 		    }
90132554Sminshall 		}
90232554Sminshall 	    }
90332554Sminshall 	}
90432554Sminshall     default:
90532554Sminshall 	return current+2;
90632554Sminshall     }
90732554Sminshall }
90832554Sminshall 
90932554Sminshall /*
91032554Sminshall  * netclear()
91132554Sminshall  *
91232554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
91332554Sminshall  * the path to the network.
91432554Sminshall  *
91532554Sminshall  *	Things are a bit tricky since we may have sent the first
91632554Sminshall  * byte or so of a previous TELNET command into the network.
91732554Sminshall  * So, we have to scan the network buffer from the beginning
91832554Sminshall  * until we are up to where we want to be.
91932554Sminshall  *
92032554Sminshall  *	A side effect of what we do, just to keep things
92132554Sminshall  * simple, is to clear the urgent data pointer.  The principal
92232554Sminshall  * caller should be setting the urgent data pointer AFTER calling
92332554Sminshall  * us in any case.
92432554Sminshall  */
92532554Sminshall 
92632554Sminshall static void
92732554Sminshall netclear()
92832554Sminshall {
92932554Sminshall #if	0	/* XXX */
93032554Sminshall     register char *thisitem, *next;
93132554Sminshall     char *good;
93232554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
93332554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
93432554Sminshall 
93532554Sminshall     thisitem = netobuf;
93632554Sminshall 
93732554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
93832554Sminshall 	thisitem = next;
93932554Sminshall     }
94032554Sminshall 
94132554Sminshall     /* Now, thisitem is first before/at boundary. */
94232554Sminshall 
94332554Sminshall     good = netobuf;	/* where the good bytes go */
94432554Sminshall 
94532554Sminshall     while (netoring.add > thisitem) {
94632554Sminshall 	if (wewant(thisitem)) {
94732554Sminshall 	    int length;
94832554Sminshall 
94932554Sminshall 	    next = thisitem;
95032554Sminshall 	    do {
95132554Sminshall 		next = nextitem(next);
95232554Sminshall 	    } while (wewant(next) && (nfrontp > next));
95332554Sminshall 	    length = next-thisitem;
95432554Sminshall 	    memcpy(good, thisitem, length);
95532554Sminshall 	    good += length;
95632554Sminshall 	    thisitem = next;
95732554Sminshall 	} else {
95832554Sminshall 	    thisitem = nextitem(thisitem);
95932554Sminshall 	}
96032554Sminshall     }
96132554Sminshall 
96232554Sminshall #endif	/* 0 */
96332554Sminshall }
96432554Sminshall 
96532554Sminshall /*
96632377Sminshall  * These routines add various telnet commands to the data stream.
96727088Sminshall  */
96832377Sminshall 
96932554Sminshall static void
97032554Sminshall doflush()
97132554Sminshall {
97232554Sminshall     NET2ADD(IAC, DO);
97332554Sminshall     NETADD(TELOPT_TM);
97432554Sminshall     flushline = 1;
97532554Sminshall     flushout = 1;
97632554Sminshall     ttyflush(1);			/* Flush/drop output */
97732554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
97832554Sminshall     printoption("<SENT", doopt, TELOPT_TM, 0);
97932554Sminshall }
98032554Sminshall 
98132377Sminshall void
98232377Sminshall xmitAO()
98327088Sminshall {
98432377Sminshall     NET2ADD(IAC, AO);
98532377Sminshall     if (autoflush) {
98632377Sminshall 	doflush();
98732377Sminshall     }
98832377Sminshall }
98927088Sminshall 
99032377Sminshall 
99132377Sminshall void
99232377Sminshall xmitEL()
99327088Sminshall {
99432377Sminshall     NET2ADD(IAC, EL);
99527088Sminshall }
99627088Sminshall 
99732377Sminshall void
99832377Sminshall xmitEC()
99927088Sminshall {
100032377Sminshall     NET2ADD(IAC, EC);
100127088Sminshall }
100227088Sminshall 
100332377Sminshall 
100432377Sminshall #if	defined(NOT43)
100532377Sminshall int
100632377Sminshall #else	/* defined(NOT43) */
100732377Sminshall void
100832377Sminshall #endif	/* defined(NOT43) */
100932377Sminshall dosynch()
101027088Sminshall {
101132377Sminshall     netclear();			/* clear the path to the network */
101233294Sminshall     NETADD(IAC);
101333294Sminshall     setneturg();
101433294Sminshall     NETADD(DM);
101527088Sminshall 
101632377Sminshall #if	defined(NOT43)
101732377Sminshall     return 0;
101832377Sminshall #endif	/* defined(NOT43) */
101927088Sminshall }
102027088Sminshall 
102132377Sminshall void
102232377Sminshall intp()
102327088Sminshall {
102432377Sminshall     NET2ADD(IAC, IP);
102532377Sminshall     flushline = 1;
102632377Sminshall     if (autoflush) {
102732377Sminshall 	doflush();
102832377Sminshall     }
102932377Sminshall     if (autosynch) {
103032377Sminshall 	dosynch();
103132377Sminshall     }
103227088Sminshall }
103327186Sminshall 
103432377Sminshall void
103532377Sminshall sendbrk()
103627186Sminshall {
103732377Sminshall     NET2ADD(IAC, BREAK);
103832377Sminshall     flushline = 1;
103932377Sminshall     if (autoflush) {
104032377Sminshall 	doflush();
104132377Sminshall     }
104232377Sminshall     if (autosynch) {
104332377Sminshall 	dosynch();
104432377Sminshall     }
104527186Sminshall }
1046