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