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