xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 34898)
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
6*34898Sbostic  * provided that the above copyright notice and this paragraph are
7*34898Sbostic  * duplicated in all such forms and that any documentation,
8*34898Sbostic  * advertising materials, and other materials related to such
9*34898Sbostic  * distribution and use acknowledge that the software was developed
10*34898Sbostic  * by the University of California, Berkeley.  The name of the
11*34898Sbostic  * University may not be used to endorse or promote products derived
12*34898Sbostic  * from this software without specific prior written permission.
13*34898Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34898Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34898Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1633685Sbostic  */
1711758Ssam 
1821580Sdist #ifndef lint
19*34898Sbostic static char sccsid[] = "@(#)telnet.c	5.33 (Berkeley) 06/29/88";
2033685Sbostic #endif /* not lint */
2121580Sdist 
229217Ssam #include <sys/types.h>
239217Ssam 
2432377Sminshall #if	defined(unix)
2533804Sminshall #include <signal.h>
2632377Sminshall /* By the way, we need to include curses.h before telnet.h since,
2732377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
2832377Sminshall  * declared in curses.h.
2932377Sminshall  */
3032377Sminshall #include <curses.h>
3132377Sminshall #endif	/* defined(unix) */
3232377Sminshall 
3312212Ssam #include <arpa/telnet.h>
3432377Sminshall 
3532377Sminshall #if	defined(unix)
3627186Sminshall #include <strings.h>
3732377Sminshall #else	/* defined(unix) */
3832377Sminshall #include <string.h>
3932377Sminshall #endif	/* defined(unix) */
409217Ssam 
4132381Sminshall #include "ring.h"
4232381Sminshall 
4332377Sminshall #include "defines.h"
4432377Sminshall #include "externs.h"
4532377Sminshall #include "types.h"
4632377Sminshall #include "general.h"
4727178Sminshall 
4827178Sminshall 
4927228Sminshall #define	strip(x)	((x)&0x7f)
506000Sroot 
5127088Sminshall 
5232377Sminshall static char	subbuffer[SUBBUFSIZE],
5332377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
5427676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
5527676Sminshall #define	SB_TERM()	subend = subpointer;
5627676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5727676Sminshall 				*subpointer++ = (c); \
5827676Sminshall 			}
5927676Sminshall 
606000Sroot char	hisopts[256];
616000Sroot char	myopts[256];
626000Sroot 
636000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
646000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
656000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
666000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
676000Sroot 
6832377Sminshall int
6932377Sminshall 	connected,
7032377Sminshall 	showoptions,
7132377Sminshall 	In3270,		/* Are we in 3270 mode? */
7232377Sminshall 	ISend,		/* trying to send network data in */
7332377Sminshall 	debug = 0,
7432377Sminshall 	crmod,
7532377Sminshall 	netdata,	/* Print out network data flow */
7632377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
7734848Sminshall #if	defined(TN3270)
7832377Sminshall 	noasynch = 0,	/* User specified "-noasynch" on command line */
7932377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
8034848Sminshall #endif	/* defined(TN3270) */
8133286Sminshall 	telnetport,
8232531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
8332531Sminshall 	flushout,	/* flush output */
8432531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
8532531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
8632531Sminshall 	localchars,	/* we recognize interrupt/quit */
8732531Sminshall 	donelclchars,	/* the user has set "localchars" */
8832531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
8932531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
9032531Sminshall 	globalmode;
9127088Sminshall 
9232377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
936000Sroot 
9432377Sminshall char
9532377Sminshall 	*prompt = 0,
9632377Sminshall 	escape,
9732377Sminshall 	echoc;
9827186Sminshall 
9927186Sminshall /*
1006000Sroot  * Telnet receiver states for fsm
1016000Sroot  */
1026000Sroot #define	TS_DATA		0
1036000Sroot #define	TS_IAC		1
1046000Sroot #define	TS_WILL		2
1056000Sroot #define	TS_WONT		3
1066000Sroot #define	TS_DO		4
1076000Sroot #define	TS_DONT		5
10827021Sminshall #define	TS_CR		6
10927676Sminshall #define	TS_SB		7		/* sub-option collection */
11027676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1116000Sroot 
11232377Sminshall static int	telrcv_state;
1136000Sroot 
11432377Sminshall jmp_buf	toplevel = { 0 };
11532377Sminshall jmp_buf	peerdied;
1166000Sroot 
11732377Sminshall int	flushline;
11827021Sminshall 
11932377Sminshall /*
12032377Sminshall  * The following are some clocks used to decide how to interpret
12132377Sminshall  * the relationship between various variables.
12232377Sminshall  */
1236000Sroot 
12432377Sminshall Clocks clocks;
12532377Sminshall 
12632377Sminshall Modelist modelist[] = {
12732377Sminshall 	{ "telnet command mode", COMMAND_LINE },
12832377Sminshall 	{ "character-at-a-time mode", 0 },
12932377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
13032377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
13132377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
13232377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
13332377Sminshall 	{ "3270 mode", 0 },
13432377Sminshall };
1356000Sroot 
13632377Sminshall 
13732377Sminshall /*
13832377Sminshall  * Initialize telnet environment.
13932377Sminshall  */
1406000Sroot 
14132377Sminshall init_telnet()
14232377Sminshall {
14332377Sminshall     SB_CLEAR();
14432377Sminshall     ClearArray(hisopts);
14532377Sminshall     ClearArray(myopts);
1466000Sroot 
14732554Sminshall     connected = In3270 = ISend = donebinarytoggle = 0;
1486000Sroot 
14932377Sminshall #if	defined(unix) && defined(TN3270)
15032377Sminshall     HaveInput = 0;
15132377Sminshall #endif	/* defined(unix) && defined(TN3270) */
1526000Sroot 
15332377Sminshall     SYNCHing = 0;
1546000Sroot 
15532377Sminshall     /* Don't change NetTrace */
1566000Sroot 
15732377Sminshall     escape = CONTROL(']');
15832377Sminshall     echoc = CONTROL('E');
1596000Sroot 
16032377Sminshall     flushline = 1;
16132377Sminshall     telrcv_state = TS_DATA;
16232377Sminshall }
16332554Sminshall 
1646000Sroot 
16532554Sminshall #include <varargs.h>
1666000Sroot 
16734848Sminshall /*VARARGS*/
16832554Sminshall static void
16932554Sminshall printring(va_alist)
17032554Sminshall va_dcl
17132554Sminshall {
17232554Sminshall     va_list ap;
17332554Sminshall     char buffer[100];		/* where things go */
17432554Sminshall     char *ptr;
17532554Sminshall     char *format;
17632554Sminshall     char *string;
17732554Sminshall     Ring *ring;
17832554Sminshall     int i;
17932554Sminshall 
18032554Sminshall     va_start(ap);
18132554Sminshall 
18232554Sminshall     ring = va_arg(ap, Ring *);
18332554Sminshall     format = va_arg(ap, char *);
18432554Sminshall     ptr = buffer;
18532554Sminshall 
18632554Sminshall     while ((i = *format++) != 0) {
18732554Sminshall 	if (i == '%') {
18832554Sminshall 	    i = *format++;
18932554Sminshall 	    switch (i) {
19032554Sminshall 	    case 'c':
19132554Sminshall 		*ptr++ = va_arg(ap, int);
19232554Sminshall 		break;
19332554Sminshall 	    case 's':
19432554Sminshall 		string = va_arg(ap, char *);
19532554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
19632554Sminshall 		ring_supply_data(ring, string, strlen(string));
19732554Sminshall 		ptr = buffer;
19832554Sminshall 		break;
19932554Sminshall 	    case 0:
20032554Sminshall 		ExitString("printring: trailing %%.\n", 1);
20132554Sminshall 		/*NOTREACHED*/
20232554Sminshall 	    default:
20332554Sminshall 		ExitString("printring: unknown format character.\n", 1);
20432554Sminshall 		/*NOTREACHED*/
20532554Sminshall 	    }
20632554Sminshall 	} else {
20732554Sminshall 	    *ptr++ = i;
20832554Sminshall 	}
20932554Sminshall     }
21032554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
21132554Sminshall }
21232554Sminshall 
21332554Sminshall 
21432377Sminshall void
21527676Sminshall willoption(option, reply)
21627676Sminshall 	int option, reply;
2176000Sroot {
2186000Sroot 	char *fmt;
2196000Sroot 
2206000Sroot 	switch (option) {
2216000Sroot 
2226000Sroot 	case TELOPT_ECHO:
22332377Sminshall #	if defined(TN3270)
22432377Sminshall 	    /*
22532377Sminshall 	     * The following is a pain in the rear-end.
22632377Sminshall 	     * Various IBM servers (some versions of Wiscnet,
22732377Sminshall 	     * possibly Fibronics/Spartacus, and who knows who
22832377Sminshall 	     * else) will NOT allow us to send "DO SGA" too early
22932377Sminshall 	     * in the setup proceedings.  On the other hand,
23032377Sminshall 	     * 4.2 servers (telnetd) won't set SGA correctly.
23132377Sminshall 	     * So, we are stuck.  Empirically (but, based on
23232377Sminshall 	     * a VERY small sample), the IBM servers don't send
23332377Sminshall 	     * out anything about ECHO, so we postpone our sending
23432377Sminshall 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
23532377Sminshall 	     * DO send).
23632377Sminshall 	     */
23732377Sminshall 	    {
23832377Sminshall 		if (askedSGA == 0) {
23932377Sminshall 		    askedSGA = 1;
24032377Sminshall 		    if (!hisopts[TELOPT_SGA]) {
24132377Sminshall 			willoption(TELOPT_SGA, 0);
24232377Sminshall 		    }
24332377Sminshall 		}
24432377Sminshall 	    }
24532377Sminshall 		/* Fall through */
24632377Sminshall 	case TELOPT_EOR:
24732377Sminshall 	case TELOPT_BINARY:
24832377Sminshall #endif	/* defined(TN3270) */
2496000Sroot 	case TELOPT_SGA:
25027110Sminshall 		settimer(modenegotiated);
2516000Sroot 		hisopts[option] = 1;
2526000Sroot 		fmt = doopt;
25327110Sminshall 		setconnmode();		/* possibly set new tty mode */
2546000Sroot 		break;
2556000Sroot 
2566000Sroot 	case TELOPT_TM:
25727110Sminshall 		return;			/* Never reply to TM will's/wont's */
2586000Sroot 
2596000Sroot 	default:
2606000Sroot 		fmt = dont;
2616000Sroot 		break;
2626000Sroot 	}
26332554Sminshall 	printring(&netoring, fmt, option);
26427676Sminshall 	if (reply)
26532377Sminshall 		printoption(">SENT", fmt, option, reply);
26627676Sminshall 	else
26732377Sminshall 		printoption("<SENT", fmt, option, reply);
2686000Sroot }
2696000Sroot 
27032377Sminshall void
27127676Sminshall wontoption(option, reply)
27227676Sminshall 	int option, reply;
2736000Sroot {
2746000Sroot 	char *fmt;
2756000Sroot 
2766000Sroot 	switch (option) {
2776000Sroot 
2786000Sroot 	case TELOPT_ECHO:
2796000Sroot 	case TELOPT_SGA:
28027110Sminshall 		settimer(modenegotiated);
2816000Sroot 		hisopts[option] = 0;
2826000Sroot 		fmt = dont;
28327110Sminshall 		setconnmode();			/* Set new tty mode */
2846000Sroot 		break;
2856000Sroot 
28627110Sminshall 	case TELOPT_TM:
28727110Sminshall 		return;		/* Never reply to TM will's/wont's */
28827110Sminshall 
2896000Sroot 	default:
2906000Sroot 		fmt = dont;
2916000Sroot 	}
29232554Sminshall 	printring(&netoring, fmt, option);
29327676Sminshall 	if (reply)
29432377Sminshall 		printoption(">SENT", fmt, option, reply);
29527676Sminshall 	else
29632377Sminshall 		printoption("<SENT", fmt, option, reply);
2976000Sroot }
2986000Sroot 
29932377Sminshall static void
3006000Sroot dooption(option)
3016000Sroot 	int option;
3026000Sroot {
3036000Sroot 	char *fmt;
3046000Sroot 
3056000Sroot 	switch (option) {
3066000Sroot 
3076000Sroot 	case TELOPT_TM:
30813231Ssam 		fmt = will;
30913231Ssam 		break;
31013231Ssam 
31132377Sminshall #	if defined(TN3270)
31232377Sminshall 	case TELOPT_EOR:
31332377Sminshall 	case TELOPT_BINARY:
31432377Sminshall #	endif	/* defined(TN3270) */
31527676Sminshall 	case TELOPT_TTYPE:		/* terminal type option */
31627110Sminshall 	case TELOPT_SGA:		/* no big deal */
3176000Sroot 		fmt = will;
31827110Sminshall 		myopts[option] = 1;
3196000Sroot 		break;
3206000Sroot 
32127110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
3226000Sroot 	default:
3236000Sroot 		fmt = wont;
3246000Sroot 		break;
3256000Sroot 	}
32632554Sminshall 	printring(&netoring, fmt, option);
32732377Sminshall 	printoption(">SENT", fmt, option, 0);
3286000Sroot }
32927676Sminshall 
33027676Sminshall /*
33127676Sminshall  * suboption()
33227676Sminshall  *
33327676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
33427676Sminshall  * side.
33527676Sminshall  *
33627676Sminshall  *	Currently we recognize:
33727676Sminshall  *
33827676Sminshall  *		Terminal type, send request.
33927676Sminshall  */
34027676Sminshall 
34132377Sminshall static void
34227676Sminshall suboption()
34327676Sminshall {
34432377Sminshall     printsub("<", subbuffer, subend-subbuffer+1);
34527676Sminshall     switch (subbuffer[0]&0xff) {
34627676Sminshall     case TELOPT_TTYPE:
34727676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
34827676Sminshall 	    ;
34927676Sminshall 	} else {
35027676Sminshall 	    char *name;
35127676Sminshall 	    char namebuf[41];
35232377Sminshall 	    extern char *getenv();
35327676Sminshall 	    int len;
35427676Sminshall 
35532377Sminshall #if	defined(TN3270)
35632531Sminshall 	    if (tn3270_ttype()) {
35732377Sminshall 		return;
35832377Sminshall 	    }
35932377Sminshall #endif	/* defined(TN3270) */
36027676Sminshall 	    name = getenv("TERM");
36127676Sminshall 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
36227676Sminshall 		name = "UNKNOWN";
36333492Sminshall 		len = strlen(name);
36427676Sminshall 	    }
36527676Sminshall 	    if ((len + 4+2) < NETROOM()) {
36627676Sminshall 		strcpy(namebuf, name);
36727676Sminshall 		upcase(namebuf);
36832554Sminshall 		printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
36927676Sminshall 				    TELQUAL_IS, namebuf, IAC, SE);
37032381Sminshall 		/* XXX */
37132381Sminshall 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
37232377Sminshall 	    } else {
37332381Sminshall 		ExitString("No room in buffer for terminal type.\n",
37432377Sminshall 							1);
37532377Sminshall 		/*NOTREACHED*/
37627676Sminshall 	    }
37727676Sminshall 	}
37827676Sminshall 
37927676Sminshall     default:
38027676Sminshall 	break;
38127676Sminshall     }
38227676Sminshall }
38332377Sminshall 
38427088Sminshall 
38533804Sminshall int
38632377Sminshall telrcv()
38727110Sminshall {
38832377Sminshall     register int c;
38932385Sminshall     register int scc;
39032385Sminshall     register char *sbp;
39132385Sminshall     int count;
39232385Sminshall     int returnValue = 0;
39327088Sminshall 
39432385Sminshall     scc = 0;
39532385Sminshall     count = 0;
39632385Sminshall     while (TTYROOM() > 2) {
39732385Sminshall 	if (scc == 0) {
39832385Sminshall 	    if (count) {
39932528Sminshall 		ring_consumed(&netiring, count);
40032385Sminshall 		returnValue = 1;
40132385Sminshall 		count = 0;
40232385Sminshall 	    }
40332528Sminshall 	    sbp = netiring.consume;
40432528Sminshall 	    scc = ring_full_consecutive(&netiring);
40532385Sminshall 	    if (scc == 0) {
40632385Sminshall 		/* No more data coming in */
40732385Sminshall 		break;
40832385Sminshall 	    }
40932385Sminshall 	}
41032385Sminshall 
41132385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
41232385Sminshall 
41332377Sminshall 	switch (telrcv_state) {
41427110Sminshall 
41532377Sminshall 	case TS_CR:
41632377Sminshall 	    telrcv_state = TS_DATA;
41732377Sminshall 	    if (c == '\0') {
41832377Sminshall 		break;	/* Ignore \0 after CR */
41932377Sminshall 	    } else if (c == '\n') {
42032657Sminshall 		if ((!hisopts[TELOPT_ECHO]) && !crmod) {
42132377Sminshall 		    TTYADD(c);
42232377Sminshall 		}
42332377Sminshall 		break;
42432377Sminshall 	    }
42532377Sminshall 	    /* Else, fall through */
42627088Sminshall 
42732377Sminshall 	case TS_DATA:
42832377Sminshall 	    if (c == IAC) {
42932377Sminshall 		telrcv_state = TS_IAC;
43033804Sminshall 		break;
43132377Sminshall 	    }
43232377Sminshall #	    if defined(TN3270)
43332377Sminshall 	    if (In3270) {
43432377Sminshall 		*Ifrontp++ = c;
43532385Sminshall 		while (scc > 0) {
43632385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
43732377Sminshall 		    if (c == IAC) {
43832377Sminshall 			telrcv_state = TS_IAC;
43934304Sminshall 			break;
44032377Sminshall 		    }
44132377Sminshall 		    *Ifrontp++ = c;
44232377Sminshall 		}
44332377Sminshall 	    } else
44432377Sminshall #	    endif /* defined(TN3270) */
44532377Sminshall 		    /*
44632377Sminshall 		     * The 'crmod' hack (see following) is needed
44732377Sminshall 		     * since we can't * set CRMOD on output only.
44832377Sminshall 		     * Machines like MULTICS like to send \r without
44932377Sminshall 		     * \n; since we must turn off CRMOD to get proper
45032377Sminshall 		     * input, the mapping is done here (sigh).
45132377Sminshall 		     */
45232377Sminshall 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
45332377Sminshall 		if (scc > 0) {
45432377Sminshall 		    c = *sbp&0xff;
45532377Sminshall 		    if (c == 0) {
45632385Sminshall 			sbp++, scc--; count++;
45732377Sminshall 			/* a "true" CR */
45832377Sminshall 			TTYADD('\r');
45932377Sminshall 		    } else if (!hisopts[TELOPT_ECHO] &&
46032377Sminshall 					(c == '\n')) {
46132385Sminshall 			sbp++, scc--; count++;
46232377Sminshall 			TTYADD('\n');
46332377Sminshall 		    } else {
46432377Sminshall 			TTYADD('\r');
46532377Sminshall 			if (crmod) {
46632377Sminshall 				TTYADD('\n');
46732377Sminshall 			}
46832377Sminshall 		    }
46932377Sminshall 		} else {
47032377Sminshall 		    telrcv_state = TS_CR;
47132377Sminshall 		    TTYADD('\r');
47232377Sminshall 		    if (crmod) {
47332377Sminshall 			    TTYADD('\n');
47432377Sminshall 		    }
47532377Sminshall 		}
47632377Sminshall 	    } else {
47732377Sminshall 		TTYADD(c);
47832377Sminshall 	    }
47932377Sminshall 	    continue;
48027088Sminshall 
48132377Sminshall 	case TS_IAC:
48232377Sminshall 	    switch (c) {
48332377Sminshall 
48432377Sminshall 	    case WILL:
48532377Sminshall 		telrcv_state = TS_WILL;
48632377Sminshall 		continue;
48727261Sminshall 
48832377Sminshall 	    case WONT:
48932377Sminshall 		telrcv_state = TS_WONT;
49032377Sminshall 		continue;
49127261Sminshall 
49232377Sminshall 	    case DO:
49332377Sminshall 		telrcv_state = TS_DO;
49432377Sminshall 		continue;
49527261Sminshall 
49632377Sminshall 	    case DONT:
49732377Sminshall 		telrcv_state = TS_DONT;
49832377Sminshall 		continue;
49927261Sminshall 
50032377Sminshall 	    case DM:
50132377Sminshall 		    /*
50232377Sminshall 		     * We may have missed an urgent notification,
50332377Sminshall 		     * so make sure we flush whatever is in the
50432377Sminshall 		     * buffer currently.
50532377Sminshall 		     */
50632377Sminshall 		SYNCHing = 1;
50732377Sminshall 		ttyflush(1);
50832554Sminshall 		SYNCHing = stilloob();
50932377Sminshall 		settimer(gotDM);
51032377Sminshall 		break;
51127088Sminshall 
51232377Sminshall 	    case NOP:
51332377Sminshall 	    case GA:
51432377Sminshall 		break;
51527088Sminshall 
51632377Sminshall 	    case SB:
51732377Sminshall 		SB_CLEAR();
51832377Sminshall 		telrcv_state = TS_SB;
51932377Sminshall 		continue;
52027261Sminshall 
52132377Sminshall #	    if defined(TN3270)
52232377Sminshall 	    case EOR:
52332377Sminshall 		if (In3270) {
52432377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
52532377Sminshall 		    if (Ibackp == Ifrontp) {
52632377Sminshall 			Ibackp = Ifrontp = Ibuf;
52732377Sminshall 			ISend = 0;	/* should have been! */
52832377Sminshall 		    } else {
52932377Sminshall 			ISend = 1;
53027088Sminshall 		    }
53127088Sminshall 		}
53227088Sminshall 		break;
53332377Sminshall #	    endif /* defined(TN3270) */
53432377Sminshall 
53532377Sminshall 	    case IAC:
53632377Sminshall #	    if !defined(TN3270)
53732377Sminshall 		TTYADD(IAC);
53832377Sminshall #	    else /* !defined(TN3270) */
53932377Sminshall 		if (In3270) {
54032377Sminshall 		    *Ifrontp++ = IAC;
54132377Sminshall 		} else {
54232377Sminshall 		    TTYADD(IAC);
54332377Sminshall 		}
54432377Sminshall #	    endif /* !defined(TN3270) */
54527088Sminshall 		break;
54632377Sminshall 
54727088Sminshall 	    default:
54827088Sminshall 		break;
54927088Sminshall 	    }
55032377Sminshall 	    telrcv_state = TS_DATA;
55132377Sminshall 	    continue;
55227088Sminshall 
55332377Sminshall 	case TS_WILL:
55432377Sminshall 	    printoption(">RCVD", will, c, !hisopts[c]);
55532377Sminshall 	    if (c == TELOPT_TM) {
55632377Sminshall 		if (flushout) {
55732377Sminshall 		    flushout = 0;
55832377Sminshall 		}
55932377Sminshall 	    } else if (!hisopts[c]) {
56032377Sminshall 		willoption(c, 1);
56132377Sminshall 	    }
56232377Sminshall 	    SetIn3270();
56332377Sminshall 	    telrcv_state = TS_DATA;
56432377Sminshall 	    continue;
56527110Sminshall 
56632377Sminshall 	case TS_WONT:
56732377Sminshall 	    printoption(">RCVD", wont, c, hisopts[c]);
56832377Sminshall 	    if (c == TELOPT_TM) {
56932377Sminshall 		if (flushout) {
57032377Sminshall 		    flushout = 0;
57132377Sminshall 		}
57232377Sminshall 	    } else if (hisopts[c]) {
57332377Sminshall 		wontoption(c, 1);
57432377Sminshall 	    }
57532377Sminshall 	    SetIn3270();
57632377Sminshall 	    telrcv_state = TS_DATA;
57732377Sminshall 	    continue;
57827088Sminshall 
57932377Sminshall 	case TS_DO:
58032377Sminshall 	    printoption(">RCVD", doopt, c, !myopts[c]);
58132377Sminshall 	    if (!myopts[c])
58232377Sminshall 		dooption(c);
58332377Sminshall 	    SetIn3270();
58432377Sminshall 	    telrcv_state = TS_DATA;
58532377Sminshall 	    continue;
58627088Sminshall 
58732377Sminshall 	case TS_DONT:
58832377Sminshall 	    printoption(">RCVD", dont, c, myopts[c]);
58932377Sminshall 	    if (myopts[c]) {
59032377Sminshall 		myopts[c] = 0;
59132554Sminshall 		printring(&netoring, wont, c);
59232377Sminshall 		flushline = 1;
59332377Sminshall 		setconnmode();	/* set new tty mode (maybe) */
59432377Sminshall 		printoption(">SENT", wont, c, 0);
59532377Sminshall 	    }
59632377Sminshall 	    SetIn3270();
59732377Sminshall 	    telrcv_state = TS_DATA;
59832377Sminshall 	    continue;
59927088Sminshall 
60032377Sminshall 	case TS_SB:
60132377Sminshall 	    if (c == IAC) {
60232377Sminshall 		telrcv_state = TS_SE;
60332377Sminshall 	    } else {
60432377Sminshall 		SB_ACCUM(c);
60532377Sminshall 	    }
60632377Sminshall 	    continue;
60727088Sminshall 
60832377Sminshall 	case TS_SE:
60932377Sminshall 	    if (c != SE) {
61032377Sminshall 		if (c != IAC) {
61132377Sminshall 		    SB_ACCUM(IAC);
61232377Sminshall 		}
61332377Sminshall 		SB_ACCUM(c);
61432377Sminshall 		telrcv_state = TS_SB;
61532377Sminshall 	    } else {
61632377Sminshall 		SB_TERM();
61732377Sminshall 		suboption();	/* handle sub-option */
61832377Sminshall 		SetIn3270();
61932377Sminshall 		telrcv_state = TS_DATA;
62032377Sminshall 	    }
62127088Sminshall 	}
62227088Sminshall     }
62332667Sminshall     if (count)
62432667Sminshall 	ring_consumed(&netiring, count);
62532385Sminshall     return returnValue||count;
62627088Sminshall }
62732385Sminshall 
62832385Sminshall static int
62932554Sminshall telsnd()
63032385Sminshall {
63132385Sminshall     int tcc;
63232385Sminshall     int count;
63332385Sminshall     int returnValue = 0;
63432385Sminshall     char *tbp;
63532385Sminshall 
63632385Sminshall     tcc = 0;
63732385Sminshall     count = 0;
63832385Sminshall     while (NETROOM() > 2) {
63932385Sminshall 	register int sc;
64032385Sminshall 	register int c;
64132385Sminshall 
64232385Sminshall 	if (tcc == 0) {
64332385Sminshall 	    if (count) {
64432528Sminshall 		ring_consumed(&ttyiring, count);
64532385Sminshall 		returnValue = 1;
64632385Sminshall 		count = 0;
64732385Sminshall 	    }
64832528Sminshall 	    tbp = ttyiring.consume;
64932528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
65032385Sminshall 	    if (tcc == 0) {
65132385Sminshall 		break;
65232385Sminshall 	    }
65332385Sminshall 	}
65432385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
65532385Sminshall 	if (sc == escape) {
65632385Sminshall 	    command(0);
65732385Sminshall 	    tcc = 0;
65832385Sminshall 	    flushline = 1;
65932385Sminshall 	    break;
66032385Sminshall 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
66132385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
66232385Sminshall 		tcc--; tbp++; count++;
66332385Sminshall 	    } else {
66432385Sminshall 		dontlecho = !dontlecho;
66532385Sminshall 		settimer(echotoggle);
66632385Sminshall 		setconnmode();
66732385Sminshall 		flushline = 1;
66832385Sminshall 		break;
66932385Sminshall 	    }
67032385Sminshall 	}
67132385Sminshall 	if (localchars) {
67232385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
67332385Sminshall 		break;
67432385Sminshall 	    }
67532385Sminshall 	}
67632385Sminshall 	if (!myopts[TELOPT_BINARY]) {
67732385Sminshall 	    switch (c) {
67832385Sminshall 	    case '\n':
67932385Sminshall 		    /*
68032385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
68132385Sminshall 		     * on our local machine, then probably
68232385Sminshall 		     * a newline (unix) is CRLF (TELNET).
68332385Sminshall 		     */
68432385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
68532385Sminshall 		    NETADD('\r');
68632385Sminshall 		}
68732385Sminshall 		NETADD('\n');
68832385Sminshall 		flushline = 1;
68932385Sminshall 		break;
69032385Sminshall 	    case '\r':
69132385Sminshall 		if (!crlf) {
69232385Sminshall 		    NET2ADD('\r', '\0');
69332385Sminshall 		} else {
69432385Sminshall 		    NET2ADD('\r', '\n');
69532385Sminshall 		}
69632385Sminshall 		flushline = 1;
69732385Sminshall 		break;
69832385Sminshall 	    case IAC:
69932385Sminshall 		NET2ADD(IAC, IAC);
70032385Sminshall 		break;
70132385Sminshall 	    default:
70232385Sminshall 		NETADD(c);
70332385Sminshall 		break;
70432385Sminshall 	    }
70532385Sminshall 	} else if (c == IAC) {
70632385Sminshall 	    NET2ADD(IAC, IAC);
70732385Sminshall 	} else {
70832385Sminshall 	    NETADD(c);
70932385Sminshall 	}
71032385Sminshall     }
71132667Sminshall     if (count)
71232667Sminshall 	ring_consumed(&ttyiring, count);
71332385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
71432385Sminshall }
71532377Sminshall 
71627088Sminshall /*
71732377Sminshall  * Scheduler()
71832377Sminshall  *
71932377Sminshall  * Try to do something.
72032377Sminshall  *
72132377Sminshall  * If we do something useful, return 1; else return 0.
72232377Sminshall  *
72327110Sminshall  */
72427110Sminshall 
72527110Sminshall 
72632377Sminshall int
72732377Sminshall Scheduler(block)
72832377Sminshall int	block;			/* should we block in the select ? */
72927110Sminshall {
73032377Sminshall 		/* One wants to be a bit careful about setting returnValue
73132377Sminshall 		 * to one, since a one implies we did some useful work,
73232377Sminshall 		 * and therefore probably won't be called to block next
73332377Sminshall 		 * time (TN3270 mode only).
73432377Sminshall 		 */
73532531Sminshall     int returnValue;
73632531Sminshall     int netin, netout, netex, ttyin, ttyout;
73727110Sminshall 
73832531Sminshall     /* Decide which rings should be processed */
73932531Sminshall 
74032531Sminshall     netout = ring_full_count(&netoring) &&
74132531Sminshall 	    (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]);
74232531Sminshall     ttyout = ring_full_count(&ttyoring);
74332531Sminshall 
74432377Sminshall #if	defined(TN3270)
74532531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
74632377Sminshall #else	/* defined(TN3270) */
74732531Sminshall     ttyin = ring_empty_count(&ttyiring);
74832377Sminshall #endif	/* defined(TN3270) */
74932531Sminshall 
75032531Sminshall #if	defined(TN3270)
75132531Sminshall     netin = ring_empty_count(&netiring);
75232377Sminshall #   else /* !defined(TN3270) */
75332531Sminshall     netin = !ISend && ring_empty_count(&netiring);
75432377Sminshall #   endif /* !defined(TN3270) */
75532531Sminshall 
75632531Sminshall     netex = !SYNCHing;
75732531Sminshall 
75832531Sminshall     /* If we have seen a signal recently, reset things */
75932377Sminshall #   if defined(TN3270) && defined(unix)
76032377Sminshall     if (HaveInput) {
76132377Sminshall 	HaveInput = 0;
76232377Sminshall 	signal(SIGIO, inputAvailable);
76332377Sminshall     }
76432377Sminshall #endif	/* defined(TN3270) && defined(unix) */
76532377Sminshall 
76632531Sminshall     /* Call to system code to process rings */
76727178Sminshall 
76832531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
76927178Sminshall 
77032531Sminshall     /* Now, look at the input rings, looking for work to do. */
77132377Sminshall 
77232531Sminshall     if (ring_full_count(&ttyiring)) {
77332377Sminshall #   if defined(TN3270)
77432377Sminshall 	if (In3270) {
77534848Sminshall 	    int c;
77634848Sminshall 
77733804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
77832528Sminshall 					ring_full_consecutive(&ttyiring));
77932377Sminshall 	    if (c) {
78032377Sminshall 		returnValue = 1;
78132667Sminshall 	        ring_consumed(&ttyiring, c);
78232377Sminshall 	    }
78332377Sminshall 	} else {
78432377Sminshall #   endif /* defined(TN3270) */
78532554Sminshall 	    returnValue |= telsnd();
78632377Sminshall #   if defined(TN3270)
78727178Sminshall 	}
78832531Sminshall #   endif /* defined(TN3270) */
78927178Sminshall     }
79032377Sminshall 
79132528Sminshall     if (ring_full_count(&netiring)) {
79232377Sminshall #	if !defined(TN3270)
79332385Sminshall 	returnValue |= telrcv();
79432377Sminshall #	else /* !defined(TN3270) */
79532377Sminshall 	returnValue = Push3270();
79632377Sminshall #	endif /* !defined(TN3270) */
79732377Sminshall     }
79832377Sminshall     return returnValue;
79927178Sminshall }
80027178Sminshall 
80127178Sminshall /*
80232377Sminshall  * Select from tty and network...
80327088Sminshall  */
80432377Sminshall void
80532377Sminshall telnet()
80627088Sminshall {
80732531Sminshall     sys_telnet_init();
80827088Sminshall 
80932377Sminshall #   if !defined(TN3270)
81032377Sminshall     if (telnetport) {
81132377Sminshall 	if (!hisopts[TELOPT_SGA]) {
81232377Sminshall 	    willoption(TELOPT_SGA, 0);
81327110Sminshall 	}
81432377Sminshall 	if (!myopts[TELOPT_TTYPE]) {
81534848Sminshall 	    dooption(TELOPT_TTYPE);
81632377Sminshall 	}
81727178Sminshall     }
81832377Sminshall #   endif /* !defined(TN3270) */
81927088Sminshall 
82032377Sminshall #   if !defined(TN3270)
82132377Sminshall     for (;;) {
82232385Sminshall 	int schedValue;
82332385Sminshall 
82432385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
82532385Sminshall 	    if (schedValue == -1) {
82632385Sminshall 		setcommandmode();
82732385Sminshall 		return;
82832385Sminshall 	    }
82932385Sminshall 	}
83032385Sminshall 
83132531Sminshall 	if (Scheduler(1) == -1) {
83232377Sminshall 	    setcommandmode();
83332377Sminshall 	    return;
83432377Sminshall 	}
83532377Sminshall     }
83632377Sminshall #   else /* !defined(TN3270) */
83732377Sminshall     for (;;) {
83832377Sminshall 	int schedValue;
83927088Sminshall 
84032377Sminshall 	while (!In3270 && !shell_active) {
84132531Sminshall 	    if (Scheduler(1) == -1) {
84232377Sminshall 		setcommandmode();
84332377Sminshall 		return;
84432377Sminshall 	    }
84527088Sminshall 	}
84632377Sminshall 
84732377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
84832377Sminshall 	    if (schedValue == -1) {
84932377Sminshall 		setcommandmode();
85032377Sminshall 		return;
85132377Sminshall 	    }
85227088Sminshall 	}
85332377Sminshall 		/* If there is data waiting to go out to terminal, don't
85432377Sminshall 		 * schedule any more data for the terminal.
85532377Sminshall 		 */
85634304Sminshall 	if (ring_full_count(&ttyoring)) {
85732377Sminshall 	    schedValue = 1;
85827088Sminshall 	} else {
85932377Sminshall 	    if (shell_active) {
86032377Sminshall 		if (shell_continue() == 0) {
86132377Sminshall 		    ConnectScreen();
86227088Sminshall 		}
86332377Sminshall 	    } else if (In3270) {
86432377Sminshall 		schedValue = DoTerminalOutput();
86532377Sminshall 	    }
86627088Sminshall 	}
86732377Sminshall 	if (schedValue && (shell_active == 0)) {
86832531Sminshall 	    if (Scheduler(1) == -1) {
86932377Sminshall 		setcommandmode();
87032377Sminshall 		return;
87132377Sminshall 	    }
87227088Sminshall 	}
87332377Sminshall     }
87432377Sminshall #   endif /* !defined(TN3270) */
87527088Sminshall }
87632377Sminshall 
87734848Sminshall #if	0	/* XXX - this not being in is a bug */
87827088Sminshall /*
87932554Sminshall  * nextitem()
88032554Sminshall  *
88132554Sminshall  *	Return the address of the next "item" in the TELNET data
88232554Sminshall  * stream.  This will be the address of the next character if
88332554Sminshall  * the current address is a user data character, or it will
88432554Sminshall  * be the address of the character following the TELNET command
88532554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
88632554Sminshall  * character.
88732554Sminshall  */
88832554Sminshall 
88932554Sminshall static char *
89032554Sminshall nextitem(current)
89132554Sminshall char	*current;
89232554Sminshall {
89332554Sminshall     if ((*current&0xff) != IAC) {
89432554Sminshall 	return current+1;
89532554Sminshall     }
89632554Sminshall     switch (*(current+1)&0xff) {
89732554Sminshall     case DO:
89832554Sminshall     case DONT:
89932554Sminshall     case WILL:
90032554Sminshall     case WONT:
90132554Sminshall 	return current+3;
90232554Sminshall     case SB:		/* loop forever looking for the SE */
90332554Sminshall 	{
90432554Sminshall 	    register char *look = current+2;
90532554Sminshall 
90632554Sminshall 	    for (;;) {
90732554Sminshall 		if ((*look++&0xff) == IAC) {
90832554Sminshall 		    if ((*look++&0xff) == SE) {
90932554Sminshall 			return look;
91032554Sminshall 		    }
91132554Sminshall 		}
91232554Sminshall 	    }
91332554Sminshall 	}
91432554Sminshall     default:
91532554Sminshall 	return current+2;
91632554Sminshall     }
91732554Sminshall }
91834848Sminshall #endif	/* 0 */
91932554Sminshall 
92032554Sminshall /*
92132554Sminshall  * netclear()
92232554Sminshall  *
92332554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
92432554Sminshall  * the path to the network.
92532554Sminshall  *
92632554Sminshall  *	Things are a bit tricky since we may have sent the first
92732554Sminshall  * byte or so of a previous TELNET command into the network.
92832554Sminshall  * So, we have to scan the network buffer from the beginning
92932554Sminshall  * until we are up to where we want to be.
93032554Sminshall  *
93132554Sminshall  *	A side effect of what we do, just to keep things
93232554Sminshall  * simple, is to clear the urgent data pointer.  The principal
93332554Sminshall  * caller should be setting the urgent data pointer AFTER calling
93432554Sminshall  * us in any case.
93532554Sminshall  */
93632554Sminshall 
93732554Sminshall static void
93832554Sminshall netclear()
93932554Sminshall {
94032554Sminshall #if	0	/* XXX */
94132554Sminshall     register char *thisitem, *next;
94232554Sminshall     char *good;
94332554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
94432554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
94532554Sminshall 
94632554Sminshall     thisitem = netobuf;
94732554Sminshall 
94832554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
94932554Sminshall 	thisitem = next;
95032554Sminshall     }
95132554Sminshall 
95232554Sminshall     /* Now, thisitem is first before/at boundary. */
95332554Sminshall 
95432554Sminshall     good = netobuf;	/* where the good bytes go */
95532554Sminshall 
95632554Sminshall     while (netoring.add > thisitem) {
95732554Sminshall 	if (wewant(thisitem)) {
95832554Sminshall 	    int length;
95932554Sminshall 
96032554Sminshall 	    next = thisitem;
96132554Sminshall 	    do {
96232554Sminshall 		next = nextitem(next);
96332554Sminshall 	    } while (wewant(next) && (nfrontp > next));
96432554Sminshall 	    length = next-thisitem;
96532554Sminshall 	    memcpy(good, thisitem, length);
96632554Sminshall 	    good += length;
96732554Sminshall 	    thisitem = next;
96832554Sminshall 	} else {
96932554Sminshall 	    thisitem = nextitem(thisitem);
97032554Sminshall 	}
97132554Sminshall     }
97232554Sminshall 
97332554Sminshall #endif	/* 0 */
97432554Sminshall }
97532554Sminshall 
97632554Sminshall /*
97732377Sminshall  * These routines add various telnet commands to the data stream.
97827088Sminshall  */
97932377Sminshall 
98032554Sminshall static void
98132554Sminshall doflush()
98232554Sminshall {
98332554Sminshall     NET2ADD(IAC, DO);
98432554Sminshall     NETADD(TELOPT_TM);
98532554Sminshall     flushline = 1;
98632554Sminshall     flushout = 1;
98732554Sminshall     ttyflush(1);			/* Flush/drop output */
98832554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
98932554Sminshall     printoption("<SENT", doopt, TELOPT_TM, 0);
99032554Sminshall }
99132554Sminshall 
99232377Sminshall void
99332377Sminshall xmitAO()
99427088Sminshall {
99532377Sminshall     NET2ADD(IAC, AO);
99632377Sminshall     if (autoflush) {
99732377Sminshall 	doflush();
99832377Sminshall     }
99932377Sminshall }
100027088Sminshall 
100132377Sminshall 
100232377Sminshall void
100332377Sminshall xmitEL()
100427088Sminshall {
100532377Sminshall     NET2ADD(IAC, EL);
100627088Sminshall }
100727088Sminshall 
100832377Sminshall void
100932377Sminshall xmitEC()
101027088Sminshall {
101132377Sminshall     NET2ADD(IAC, EC);
101227088Sminshall }
101327088Sminshall 
101432377Sminshall 
101532377Sminshall #if	defined(NOT43)
101632377Sminshall int
101732377Sminshall #else	/* defined(NOT43) */
101832377Sminshall void
101932377Sminshall #endif	/* defined(NOT43) */
102032377Sminshall dosynch()
102127088Sminshall {
102232377Sminshall     netclear();			/* clear the path to the network */
102333294Sminshall     NETADD(IAC);
102433294Sminshall     setneturg();
102533294Sminshall     NETADD(DM);
102627088Sminshall 
102732377Sminshall #if	defined(NOT43)
102832377Sminshall     return 0;
102932377Sminshall #endif	/* defined(NOT43) */
103027088Sminshall }
103127088Sminshall 
103232377Sminshall void
103332377Sminshall intp()
103427088Sminshall {
103532377Sminshall     NET2ADD(IAC, IP);
103632377Sminshall     flushline = 1;
103732377Sminshall     if (autoflush) {
103832377Sminshall 	doflush();
103932377Sminshall     }
104032377Sminshall     if (autosynch) {
104132377Sminshall 	dosynch();
104232377Sminshall     }
104327088Sminshall }
104427186Sminshall 
104532377Sminshall void
104632377Sminshall sendbrk()
104727186Sminshall {
104832377Sminshall     NET2ADD(IAC, BREAK);
104932377Sminshall     flushline = 1;
105032377Sminshall     if (autoflush) {
105132377Sminshall 	doflush();
105232377Sminshall     }
105332377Sminshall     if (autosynch) {
105432377Sminshall 	dosynch();
105532377Sminshall     }
105627186Sminshall }
1057