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 634898Sbostic * provided that the above copyright notice and this paragraph are 734898Sbostic * duplicated in all such forms and that any documentation, 834898Sbostic * advertising materials, and other materials related to such 934898Sbostic * distribution and use acknowledge that the software was developed 1034898Sbostic * by the University of California, Berkeley. The name of the 1134898Sbostic * University may not be used to endorse or promote products derived 1234898Sbostic * from this software without specific prior written permission. 1334898Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434898Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534898Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1633685Sbostic */ 1711758Ssam 1821580Sdist #ifndef lint 1935518Sminshall static char sccsid[] = "@(#)telnet.c 5.33 (Berkeley) 6/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) 7836241Sminshall noasynchtty = 0,/* User specified "-noasynch" on command line */ 7936241Sminshall noasynchnet = 0,/* User specified "-noasynch" on command line */ 8032377Sminshall askedSGA = 0, /* We have talked about suppress go ahead */ 8134848Sminshall #endif /* defined(TN3270) */ 8233286Sminshall telnetport, 8332531Sminshall SYNCHing, /* we are in TELNET SYNCH mode */ 8432531Sminshall flushout, /* flush output */ 8532531Sminshall autoflush = 0, /* flush output when interrupting? */ 8632531Sminshall autosynch, /* send interrupt characters with SYNCH? */ 8732531Sminshall localchars, /* we recognize interrupt/quit */ 8832531Sminshall donelclchars, /* the user has set "localchars" */ 8932531Sminshall donebinarytoggle, /* the user has put us in binary */ 9032531Sminshall dontlecho, /* do we suppress local echoing right now? */ 9132531Sminshall globalmode; 9227088Sminshall 9332377Sminshall #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 946000Sroot 9532377Sminshall char 9632377Sminshall *prompt = 0, 9732377Sminshall escape, 9832377Sminshall echoc; 9927186Sminshall 10027186Sminshall /* 1016000Sroot * Telnet receiver states for fsm 1026000Sroot */ 1036000Sroot #define TS_DATA 0 1046000Sroot #define TS_IAC 1 1056000Sroot #define TS_WILL 2 1066000Sroot #define TS_WONT 3 1076000Sroot #define TS_DO 4 1086000Sroot #define TS_DONT 5 10927021Sminshall #define TS_CR 6 11027676Sminshall #define TS_SB 7 /* sub-option collection */ 11127676Sminshall #define TS_SE 8 /* looking for sub-option end */ 1126000Sroot 11332377Sminshall static int telrcv_state; 1146000Sroot 11532377Sminshall jmp_buf toplevel = { 0 }; 11632377Sminshall jmp_buf peerdied; 1176000Sroot 11832377Sminshall int flushline; 11927021Sminshall 12032377Sminshall /* 12132377Sminshall * The following are some clocks used to decide how to interpret 12232377Sminshall * the relationship between various variables. 12332377Sminshall */ 1246000Sroot 12532377Sminshall Clocks clocks; 12632377Sminshall 12732377Sminshall Modelist modelist[] = { 12832377Sminshall { "telnet command mode", COMMAND_LINE }, 12932377Sminshall { "character-at-a-time mode", 0 }, 13032377Sminshall { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 13132377Sminshall { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 13232377Sminshall { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 13332377Sminshall { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 13432377Sminshall { "3270 mode", 0 }, 13532377Sminshall }; 1366000Sroot 13732377Sminshall 13832377Sminshall /* 13932377Sminshall * Initialize telnet environment. 14032377Sminshall */ 1416000Sroot 14232377Sminshall init_telnet() 14332377Sminshall { 14432377Sminshall SB_CLEAR(); 14532377Sminshall ClearArray(hisopts); 14632377Sminshall ClearArray(myopts); 1476000Sroot 14832554Sminshall connected = In3270 = ISend = donebinarytoggle = 0; 1496000Sroot 15032377Sminshall SYNCHing = 0; 1516000Sroot 15232377Sminshall /* Don't change NetTrace */ 1536000Sroot 15432377Sminshall escape = CONTROL(']'); 15532377Sminshall echoc = CONTROL('E'); 1566000Sroot 15732377Sminshall flushline = 1; 15832377Sminshall telrcv_state = TS_DATA; 15932377Sminshall } 16032554Sminshall 1616000Sroot 16232554Sminshall #include <varargs.h> 1636000Sroot 16434848Sminshall /*VARARGS*/ 16532554Sminshall static void 16632554Sminshall printring(va_alist) 16732554Sminshall va_dcl 16832554Sminshall { 16932554Sminshall va_list ap; 17032554Sminshall char buffer[100]; /* where things go */ 17132554Sminshall char *ptr; 17232554Sminshall char *format; 17332554Sminshall char *string; 17432554Sminshall Ring *ring; 17532554Sminshall int i; 17632554Sminshall 17732554Sminshall va_start(ap); 17832554Sminshall 17932554Sminshall ring = va_arg(ap, Ring *); 18032554Sminshall format = va_arg(ap, char *); 18132554Sminshall ptr = buffer; 18232554Sminshall 18332554Sminshall while ((i = *format++) != 0) { 18432554Sminshall if (i == '%') { 18532554Sminshall i = *format++; 18632554Sminshall switch (i) { 18732554Sminshall case 'c': 18832554Sminshall *ptr++ = va_arg(ap, int); 18932554Sminshall break; 19032554Sminshall case 's': 19132554Sminshall string = va_arg(ap, char *); 19232554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 19332554Sminshall ring_supply_data(ring, string, strlen(string)); 19432554Sminshall ptr = buffer; 19532554Sminshall break; 19632554Sminshall case 0: 19732554Sminshall ExitString("printring: trailing %%.\n", 1); 19832554Sminshall /*NOTREACHED*/ 19932554Sminshall default: 20032554Sminshall ExitString("printring: unknown format character.\n", 1); 20132554Sminshall /*NOTREACHED*/ 20232554Sminshall } 20332554Sminshall } else { 20432554Sminshall *ptr++ = i; 20532554Sminshall } 20632554Sminshall } 20732554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 20832554Sminshall } 20932554Sminshall 21032554Sminshall 21132377Sminshall void 21227676Sminshall willoption(option, reply) 21327676Sminshall int option, reply; 2146000Sroot { 2156000Sroot char *fmt; 2166000Sroot 2176000Sroot switch (option) { 2186000Sroot 2196000Sroot case TELOPT_ECHO: 22032377Sminshall # if defined(TN3270) 22132377Sminshall /* 22232377Sminshall * The following is a pain in the rear-end. 22332377Sminshall * Various IBM servers (some versions of Wiscnet, 22432377Sminshall * possibly Fibronics/Spartacus, and who knows who 22532377Sminshall * else) will NOT allow us to send "DO SGA" too early 22632377Sminshall * in the setup proceedings. On the other hand, 22732377Sminshall * 4.2 servers (telnetd) won't set SGA correctly. 22832377Sminshall * So, we are stuck. Empirically (but, based on 22932377Sminshall * a VERY small sample), the IBM servers don't send 23032377Sminshall * out anything about ECHO, so we postpone our sending 23132377Sminshall * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 23232377Sminshall * DO send). 23332377Sminshall */ 23432377Sminshall { 23532377Sminshall if (askedSGA == 0) { 23632377Sminshall askedSGA = 1; 23732377Sminshall if (!hisopts[TELOPT_SGA]) { 23832377Sminshall willoption(TELOPT_SGA, 0); 23932377Sminshall } 24032377Sminshall } 24132377Sminshall } 24232377Sminshall /* Fall through */ 24332377Sminshall case TELOPT_EOR: 24432377Sminshall case TELOPT_BINARY: 24532377Sminshall #endif /* defined(TN3270) */ 2466000Sroot case TELOPT_SGA: 24727110Sminshall settimer(modenegotiated); 2486000Sroot hisopts[option] = 1; 2496000Sroot fmt = doopt; 25027110Sminshall setconnmode(); /* possibly set new tty mode */ 2516000Sroot break; 2526000Sroot 2536000Sroot case TELOPT_TM: 25427110Sminshall return; /* Never reply to TM will's/wont's */ 2556000Sroot 2566000Sroot default: 2576000Sroot fmt = dont; 2586000Sroot break; 2596000Sroot } 26032554Sminshall printring(&netoring, fmt, option); 26127676Sminshall if (reply) 26232377Sminshall printoption(">SENT", fmt, option, reply); 26327676Sminshall else 26432377Sminshall printoption("<SENT", fmt, option, reply); 2656000Sroot } 2666000Sroot 26732377Sminshall void 26827676Sminshall wontoption(option, reply) 26927676Sminshall int option, reply; 2706000Sroot { 2716000Sroot char *fmt; 2726000Sroot 2736000Sroot switch (option) { 2746000Sroot 2756000Sroot case TELOPT_ECHO: 2766000Sroot case TELOPT_SGA: 27727110Sminshall settimer(modenegotiated); 2786000Sroot hisopts[option] = 0; 2796000Sroot fmt = dont; 28027110Sminshall setconnmode(); /* Set new tty mode */ 2816000Sroot break; 2826000Sroot 28327110Sminshall case TELOPT_TM: 28427110Sminshall return; /* Never reply to TM will's/wont's */ 28527110Sminshall 2866000Sroot default: 287*36466Sminshall hisopts[option] = 0; 2886000Sroot fmt = dont; 2896000Sroot } 29032554Sminshall printring(&netoring, fmt, option); 29127676Sminshall if (reply) 29232377Sminshall printoption(">SENT", fmt, option, reply); 29327676Sminshall else 29432377Sminshall printoption("<SENT", fmt, option, reply); 2956000Sroot } 2966000Sroot 29732377Sminshall static void 2986000Sroot dooption(option) 2996000Sroot int option; 3006000Sroot { 3016000Sroot char *fmt; 3026000Sroot 3036000Sroot switch (option) { 3046000Sroot 3056000Sroot case TELOPT_TM: 30613231Ssam fmt = will; 30713231Ssam break; 30813231Ssam 30932377Sminshall # if defined(TN3270) 31032377Sminshall case TELOPT_EOR: 31132377Sminshall case TELOPT_BINARY: 31232377Sminshall # endif /* defined(TN3270) */ 31327676Sminshall case TELOPT_TTYPE: /* terminal type option */ 31427110Sminshall case TELOPT_SGA: /* no big deal */ 3156000Sroot fmt = will; 31627110Sminshall myopts[option] = 1; 3176000Sroot break; 3186000Sroot 31927110Sminshall case TELOPT_ECHO: /* We're never going to echo... */ 3206000Sroot default: 3216000Sroot fmt = wont; 3226000Sroot break; 3236000Sroot } 32432554Sminshall printring(&netoring, fmt, option); 32532377Sminshall printoption(">SENT", fmt, option, 0); 3266000Sroot } 32727676Sminshall 32827676Sminshall /* 32927676Sminshall * suboption() 33027676Sminshall * 33127676Sminshall * Look at the sub-option buffer, and try to be helpful to the other 33227676Sminshall * side. 33327676Sminshall * 33427676Sminshall * Currently we recognize: 33527676Sminshall * 33627676Sminshall * Terminal type, send request. 33727676Sminshall */ 33827676Sminshall 33932377Sminshall static void 34027676Sminshall suboption() 34127676Sminshall { 34232377Sminshall printsub("<", subbuffer, subend-subbuffer+1); 34327676Sminshall switch (subbuffer[0]&0xff) { 34427676Sminshall case TELOPT_TTYPE: 34527676Sminshall if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 34627676Sminshall ; 34727676Sminshall } else { 34827676Sminshall char *name; 34927676Sminshall char namebuf[41]; 35032377Sminshall extern char *getenv(); 35127676Sminshall int len; 35227676Sminshall 35332377Sminshall #if defined(TN3270) 35432531Sminshall if (tn3270_ttype()) { 35532377Sminshall return; 35632377Sminshall } 35732377Sminshall #endif /* defined(TN3270) */ 35827676Sminshall name = getenv("TERM"); 35927676Sminshall if ((name == 0) || ((len = strlen(name)) > 40)) { 36027676Sminshall name = "UNKNOWN"; 36133492Sminshall len = strlen(name); 36227676Sminshall } 36327676Sminshall if ((len + 4+2) < NETROOM()) { 36427676Sminshall strcpy(namebuf, name); 36527676Sminshall upcase(namebuf); 36632554Sminshall printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 36727676Sminshall TELQUAL_IS, namebuf, IAC, SE); 36832381Sminshall /* XXX */ 36932381Sminshall /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ 37032377Sminshall } else { 37132381Sminshall ExitString("No room in buffer for terminal type.\n", 37232377Sminshall 1); 37332377Sminshall /*NOTREACHED*/ 37427676Sminshall } 37527676Sminshall } 37627676Sminshall 37727676Sminshall default: 37827676Sminshall break; 37927676Sminshall } 38027676Sminshall } 38132377Sminshall 38227088Sminshall 38333804Sminshall int 38432377Sminshall telrcv() 38527110Sminshall { 38632377Sminshall register int c; 38732385Sminshall register int scc; 38832385Sminshall register char *sbp; 38932385Sminshall int count; 39032385Sminshall int returnValue = 0; 39127088Sminshall 39232385Sminshall scc = 0; 39332385Sminshall count = 0; 39432385Sminshall while (TTYROOM() > 2) { 39532385Sminshall if (scc == 0) { 39632385Sminshall if (count) { 39732528Sminshall ring_consumed(&netiring, count); 39832385Sminshall returnValue = 1; 39932385Sminshall count = 0; 40032385Sminshall } 40132528Sminshall sbp = netiring.consume; 40232528Sminshall scc = ring_full_consecutive(&netiring); 40332385Sminshall if (scc == 0) { 40432385Sminshall /* No more data coming in */ 40532385Sminshall break; 40632385Sminshall } 40732385Sminshall } 40832385Sminshall 40932385Sminshall c = *sbp++ & 0xff, scc--; count++; 41032385Sminshall 41132377Sminshall switch (telrcv_state) { 41227110Sminshall 41332377Sminshall case TS_CR: 41432377Sminshall telrcv_state = TS_DATA; 41535518Sminshall if (c == '\0') { 41635518Sminshall break; /* Ignore \0 after CR */ 41735518Sminshall } else if ((c == '\n') && (!hisopts[TELOPT_ECHO]) && !crmod) { 41835518Sminshall TTYADD(c); 41935518Sminshall break; 42032377Sminshall } 42135518Sminshall /* Else, fall through */ 42227088Sminshall 42332377Sminshall case TS_DATA: 42432377Sminshall if (c == IAC) { 42532377Sminshall telrcv_state = TS_IAC; 42633804Sminshall break; 42732377Sminshall } 42832377Sminshall # if defined(TN3270) 42932377Sminshall if (In3270) { 43032377Sminshall *Ifrontp++ = c; 43132385Sminshall while (scc > 0) { 43232385Sminshall c = *sbp++ & 0377, scc--; count++; 43332377Sminshall if (c == IAC) { 43432377Sminshall telrcv_state = TS_IAC; 43534304Sminshall break; 43632377Sminshall } 43732377Sminshall *Ifrontp++ = c; 43832377Sminshall } 43932377Sminshall } else 44032377Sminshall # endif /* defined(TN3270) */ 44135518Sminshall /* 44235518Sminshall * The 'crmod' hack (see following) is needed 44335518Sminshall * since we can't * set CRMOD on output only. 44435518Sminshall * Machines like MULTICS like to send \r without 44535518Sminshall * \n; since we must turn off CRMOD to get proper 44635518Sminshall * input, the mapping is done here (sigh). 44735518Sminshall */ 44832377Sminshall if ((c == '\r') && !hisopts[TELOPT_BINARY]) { 44935518Sminshall if (scc > 0) { 45035518Sminshall c = *sbp&0xff; 45135518Sminshall if (c == 0) { 45235518Sminshall sbp++, scc--; count++; 45335518Sminshall /* a "true" CR */ 45432377Sminshall TTYADD('\r'); 45535518Sminshall } else if (!hisopts[TELOPT_ECHO] && 45635518Sminshall (c == '\n')) { 45735518Sminshall sbp++, scc--; count++; 45832377Sminshall TTYADD('\n'); 45935518Sminshall } else { 46035518Sminshall TTYADD('\r'); 46135518Sminshall if (crmod) { 46235518Sminshall TTYADD('\n'); 46332377Sminshall } 46432377Sminshall } 46535518Sminshall } else { 46635518Sminshall telrcv_state = TS_CR; 46735518Sminshall TTYADD('\r'); 46835518Sminshall if (crmod) { 46935518Sminshall TTYADD('\n'); 47035518Sminshall } 47132377Sminshall } 47232377Sminshall } else { 47332377Sminshall TTYADD(c); 47432377Sminshall } 47532377Sminshall continue; 47627088Sminshall 47732377Sminshall case TS_IAC: 47832377Sminshall switch (c) { 47932377Sminshall 48032377Sminshall case WILL: 48132377Sminshall telrcv_state = TS_WILL; 48232377Sminshall continue; 48327261Sminshall 48432377Sminshall case WONT: 48532377Sminshall telrcv_state = TS_WONT; 48632377Sminshall continue; 48727261Sminshall 48832377Sminshall case DO: 48932377Sminshall telrcv_state = TS_DO; 49032377Sminshall continue; 49127261Sminshall 49232377Sminshall case DONT: 49332377Sminshall telrcv_state = TS_DONT; 49432377Sminshall continue; 49527261Sminshall 49632377Sminshall case DM: 49732377Sminshall /* 49832377Sminshall * We may have missed an urgent notification, 49932377Sminshall * so make sure we flush whatever is in the 50032377Sminshall * buffer currently. 50132377Sminshall */ 50232377Sminshall SYNCHing = 1; 50332377Sminshall ttyflush(1); 50432554Sminshall SYNCHing = stilloob(); 50532377Sminshall settimer(gotDM); 50632377Sminshall break; 50727088Sminshall 50832377Sminshall case NOP: 50932377Sminshall case GA: 51032377Sminshall break; 51127088Sminshall 51232377Sminshall case SB: 51332377Sminshall SB_CLEAR(); 51432377Sminshall telrcv_state = TS_SB; 51532377Sminshall continue; 51627261Sminshall 51732377Sminshall # if defined(TN3270) 51832377Sminshall case EOR: 51932377Sminshall if (In3270) { 52032377Sminshall Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 52132377Sminshall if (Ibackp == Ifrontp) { 52232377Sminshall Ibackp = Ifrontp = Ibuf; 52332377Sminshall ISend = 0; /* should have been! */ 52432377Sminshall } else { 52532377Sminshall ISend = 1; 52627088Sminshall } 52727088Sminshall } 52827088Sminshall break; 52932377Sminshall # endif /* defined(TN3270) */ 53032377Sminshall 53132377Sminshall case IAC: 53232377Sminshall # if !defined(TN3270) 53332377Sminshall TTYADD(IAC); 53432377Sminshall # else /* !defined(TN3270) */ 53532377Sminshall if (In3270) { 53632377Sminshall *Ifrontp++ = IAC; 53732377Sminshall } else { 53832377Sminshall TTYADD(IAC); 53932377Sminshall } 54032377Sminshall # endif /* !defined(TN3270) */ 54127088Sminshall break; 54232377Sminshall 54327088Sminshall default: 54427088Sminshall break; 54527088Sminshall } 54632377Sminshall telrcv_state = TS_DATA; 54732377Sminshall continue; 54827088Sminshall 54932377Sminshall case TS_WILL: 55032377Sminshall printoption(">RCVD", will, c, !hisopts[c]); 55132377Sminshall if (c == TELOPT_TM) { 55232377Sminshall if (flushout) { 55332377Sminshall flushout = 0; 55432377Sminshall } 55532377Sminshall } else if (!hisopts[c]) { 55632377Sminshall willoption(c, 1); 55732377Sminshall } 55832377Sminshall SetIn3270(); 55932377Sminshall telrcv_state = TS_DATA; 56032377Sminshall continue; 56127110Sminshall 56232377Sminshall case TS_WONT: 56332377Sminshall printoption(">RCVD", wont, c, hisopts[c]); 56432377Sminshall if (c == TELOPT_TM) { 56532377Sminshall if (flushout) { 56632377Sminshall flushout = 0; 56732377Sminshall } 56832377Sminshall } else if (hisopts[c]) { 56932377Sminshall wontoption(c, 1); 57032377Sminshall } 57132377Sminshall SetIn3270(); 57232377Sminshall telrcv_state = TS_DATA; 57332377Sminshall continue; 57427088Sminshall 57532377Sminshall case TS_DO: 57632377Sminshall printoption(">RCVD", doopt, c, !myopts[c]); 57732377Sminshall if (!myopts[c]) 57832377Sminshall dooption(c); 57932377Sminshall SetIn3270(); 58032377Sminshall telrcv_state = TS_DATA; 58132377Sminshall continue; 58227088Sminshall 58332377Sminshall case TS_DONT: 58432377Sminshall printoption(">RCVD", dont, c, myopts[c]); 58532377Sminshall if (myopts[c]) { 58632377Sminshall myopts[c] = 0; 58732554Sminshall printring(&netoring, wont, c); 58832377Sminshall flushline = 1; 58932377Sminshall setconnmode(); /* set new tty mode (maybe) */ 59032377Sminshall printoption(">SENT", wont, c, 0); 59132377Sminshall } 59232377Sminshall SetIn3270(); 59332377Sminshall telrcv_state = TS_DATA; 59432377Sminshall continue; 59527088Sminshall 59632377Sminshall case TS_SB: 59732377Sminshall if (c == IAC) { 59832377Sminshall telrcv_state = TS_SE; 59932377Sminshall } else { 60032377Sminshall SB_ACCUM(c); 60132377Sminshall } 60232377Sminshall continue; 60327088Sminshall 60432377Sminshall case TS_SE: 60532377Sminshall if (c != SE) { 60632377Sminshall if (c != IAC) { 60732377Sminshall SB_ACCUM(IAC); 60832377Sminshall } 60932377Sminshall SB_ACCUM(c); 61032377Sminshall telrcv_state = TS_SB; 61132377Sminshall } else { 61232377Sminshall SB_TERM(); 61332377Sminshall suboption(); /* handle sub-option */ 61432377Sminshall SetIn3270(); 61532377Sminshall telrcv_state = TS_DATA; 61632377Sminshall } 61727088Sminshall } 61827088Sminshall } 61932667Sminshall if (count) 62032667Sminshall ring_consumed(&netiring, count); 62132385Sminshall return returnValue||count; 62227088Sminshall } 62332385Sminshall 62432385Sminshall static int 62532554Sminshall telsnd() 62632385Sminshall { 62732385Sminshall int tcc; 62832385Sminshall int count; 62932385Sminshall int returnValue = 0; 63032385Sminshall char *tbp; 63132385Sminshall 63232385Sminshall tcc = 0; 63332385Sminshall count = 0; 63432385Sminshall while (NETROOM() > 2) { 63532385Sminshall register int sc; 63632385Sminshall register int c; 63732385Sminshall 63832385Sminshall if (tcc == 0) { 63932385Sminshall if (count) { 64032528Sminshall ring_consumed(&ttyiring, count); 64132385Sminshall returnValue = 1; 64232385Sminshall count = 0; 64332385Sminshall } 64432528Sminshall tbp = ttyiring.consume; 64532528Sminshall tcc = ring_full_consecutive(&ttyiring); 64632385Sminshall if (tcc == 0) { 64732385Sminshall break; 64832385Sminshall } 64932385Sminshall } 65032385Sminshall c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; 65132385Sminshall if (sc == escape) { 65232385Sminshall command(0); 65332385Sminshall tcc = 0; 65432385Sminshall flushline = 1; 65532385Sminshall break; 65632385Sminshall } else if (MODE_LINE(globalmode) && (sc == echoc)) { 65732385Sminshall if (tcc > 0 && strip(*tbp) == echoc) { 65832385Sminshall tcc--; tbp++; count++; 65932385Sminshall } else { 66032385Sminshall dontlecho = !dontlecho; 66132385Sminshall settimer(echotoggle); 66232385Sminshall setconnmode(); 66332385Sminshall flushline = 1; 66432385Sminshall break; 66532385Sminshall } 66632385Sminshall } 66732385Sminshall if (localchars) { 66832385Sminshall if (TerminalSpecialChars(sc) == 0) { 66932385Sminshall break; 67032385Sminshall } 67132385Sminshall } 67232385Sminshall if (!myopts[TELOPT_BINARY]) { 67332385Sminshall switch (c) { 67432385Sminshall case '\n': 67532385Sminshall /* 67632385Sminshall * If we are in CRMOD mode (\r ==> \n) 67732385Sminshall * on our local machine, then probably 67832385Sminshall * a newline (unix) is CRLF (TELNET). 67932385Sminshall */ 68032385Sminshall if (MODE_LOCAL_CHARS(globalmode)) { 68132385Sminshall NETADD('\r'); 68232385Sminshall } 68332385Sminshall NETADD('\n'); 68432385Sminshall flushline = 1; 68532385Sminshall break; 68632385Sminshall case '\r': 68732385Sminshall if (!crlf) { 68832385Sminshall NET2ADD('\r', '\0'); 68932385Sminshall } else { 69032385Sminshall NET2ADD('\r', '\n'); 69132385Sminshall } 69232385Sminshall flushline = 1; 69332385Sminshall break; 69432385Sminshall case IAC: 69532385Sminshall NET2ADD(IAC, IAC); 69632385Sminshall break; 69732385Sminshall default: 69832385Sminshall NETADD(c); 69932385Sminshall break; 70032385Sminshall } 70132385Sminshall } else if (c == IAC) { 70232385Sminshall NET2ADD(IAC, IAC); 70332385Sminshall } else { 70432385Sminshall NETADD(c); 70532385Sminshall } 70632385Sminshall } 70732667Sminshall if (count) 70832667Sminshall ring_consumed(&ttyiring, count); 70932385Sminshall return returnValue||count; /* Non-zero if we did anything */ 71032385Sminshall } 71132377Sminshall 71227088Sminshall /* 71332377Sminshall * Scheduler() 71432377Sminshall * 71532377Sminshall * Try to do something. 71632377Sminshall * 71732377Sminshall * If we do something useful, return 1; else return 0. 71832377Sminshall * 71927110Sminshall */ 72027110Sminshall 72127110Sminshall 72232377Sminshall int 72332377Sminshall Scheduler(block) 72432377Sminshall int block; /* should we block in the select ? */ 72527110Sminshall { 72632377Sminshall /* One wants to be a bit careful about setting returnValue 72732377Sminshall * to one, since a one implies we did some useful work, 72832377Sminshall * and therefore probably won't be called to block next 72932377Sminshall * time (TN3270 mode only). 73032377Sminshall */ 73132531Sminshall int returnValue; 73232531Sminshall int netin, netout, netex, ttyin, ttyout; 73327110Sminshall 73432531Sminshall /* Decide which rings should be processed */ 73532531Sminshall 73632531Sminshall netout = ring_full_count(&netoring) && 73732531Sminshall (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]); 73832531Sminshall ttyout = ring_full_count(&ttyoring); 73932531Sminshall 74032377Sminshall #if defined(TN3270) 74132531Sminshall ttyin = ring_empty_count(&ttyiring) && (shell_active == 0); 74232377Sminshall #else /* defined(TN3270) */ 74332531Sminshall ttyin = ring_empty_count(&ttyiring); 74432377Sminshall #endif /* defined(TN3270) */ 74532531Sminshall 74632531Sminshall #if defined(TN3270) 74732531Sminshall netin = ring_empty_count(&netiring); 74832377Sminshall # else /* !defined(TN3270) */ 74932531Sminshall netin = !ISend && ring_empty_count(&netiring); 75032377Sminshall # endif /* !defined(TN3270) */ 75132531Sminshall 75232531Sminshall netex = !SYNCHing; 75332531Sminshall 75432531Sminshall /* If we have seen a signal recently, reset things */ 75532377Sminshall # if defined(TN3270) && defined(unix) 75632377Sminshall if (HaveInput) { 75732377Sminshall HaveInput = 0; 75832377Sminshall signal(SIGIO, inputAvailable); 75932377Sminshall } 76032377Sminshall #endif /* defined(TN3270) && defined(unix) */ 76132377Sminshall 76232531Sminshall /* Call to system code to process rings */ 76327178Sminshall 76432531Sminshall returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); 76527178Sminshall 76632531Sminshall /* Now, look at the input rings, looking for work to do. */ 76732377Sminshall 76832531Sminshall if (ring_full_count(&ttyiring)) { 76932377Sminshall # if defined(TN3270) 77032377Sminshall if (In3270) { 77134848Sminshall int c; 77234848Sminshall 77333804Sminshall c = DataFromTerminal(ttyiring.consume, 77432528Sminshall ring_full_consecutive(&ttyiring)); 77532377Sminshall if (c) { 77632377Sminshall returnValue = 1; 77732667Sminshall ring_consumed(&ttyiring, c); 77832377Sminshall } 77932377Sminshall } else { 78032377Sminshall # endif /* defined(TN3270) */ 78132554Sminshall returnValue |= telsnd(); 78232377Sminshall # if defined(TN3270) 78327178Sminshall } 78432531Sminshall # endif /* defined(TN3270) */ 78527178Sminshall } 78632377Sminshall 78732528Sminshall if (ring_full_count(&netiring)) { 78832377Sminshall # if !defined(TN3270) 78932385Sminshall returnValue |= telrcv(); 79032377Sminshall # else /* !defined(TN3270) */ 79132377Sminshall returnValue = Push3270(); 79232377Sminshall # endif /* !defined(TN3270) */ 79332377Sminshall } 79432377Sminshall return returnValue; 79527178Sminshall } 79627178Sminshall 79727178Sminshall /* 79832377Sminshall * Select from tty and network... 79927088Sminshall */ 80032377Sminshall void 80132377Sminshall telnet() 80227088Sminshall { 80332531Sminshall sys_telnet_init(); 80427088Sminshall 80532377Sminshall # if !defined(TN3270) 80632377Sminshall if (telnetport) { 80732377Sminshall if (!hisopts[TELOPT_SGA]) { 80832377Sminshall willoption(TELOPT_SGA, 0); 80927110Sminshall } 81032377Sminshall if (!myopts[TELOPT_TTYPE]) { 81134848Sminshall dooption(TELOPT_TTYPE); 81232377Sminshall } 81327178Sminshall } 81432377Sminshall # endif /* !defined(TN3270) */ 81527088Sminshall 81632377Sminshall # if !defined(TN3270) 81732377Sminshall for (;;) { 81832385Sminshall int schedValue; 81932385Sminshall 82032385Sminshall while ((schedValue = Scheduler(0)) != 0) { 82132385Sminshall if (schedValue == -1) { 82232385Sminshall setcommandmode(); 82332385Sminshall return; 82432385Sminshall } 82532385Sminshall } 82632385Sminshall 82732531Sminshall if (Scheduler(1) == -1) { 82832377Sminshall setcommandmode(); 82932377Sminshall return; 83032377Sminshall } 83132377Sminshall } 83232377Sminshall # else /* !defined(TN3270) */ 83332377Sminshall for (;;) { 83432377Sminshall int schedValue; 83527088Sminshall 83632377Sminshall while (!In3270 && !shell_active) { 83732531Sminshall if (Scheduler(1) == -1) { 83832377Sminshall setcommandmode(); 83932377Sminshall return; 84032377Sminshall } 84127088Sminshall } 84232377Sminshall 84332377Sminshall while ((schedValue = Scheduler(0)) != 0) { 84432377Sminshall if (schedValue == -1) { 84532377Sminshall setcommandmode(); 84632377Sminshall return; 84732377Sminshall } 84827088Sminshall } 84932377Sminshall /* If there is data waiting to go out to terminal, don't 85032377Sminshall * schedule any more data for the terminal. 85132377Sminshall */ 85234304Sminshall if (ring_full_count(&ttyoring)) { 85332377Sminshall schedValue = 1; 85427088Sminshall } else { 85532377Sminshall if (shell_active) { 85632377Sminshall if (shell_continue() == 0) { 85732377Sminshall ConnectScreen(); 85827088Sminshall } 85932377Sminshall } else if (In3270) { 86032377Sminshall schedValue = DoTerminalOutput(); 86132377Sminshall } 86227088Sminshall } 86332377Sminshall if (schedValue && (shell_active == 0)) { 86432531Sminshall if (Scheduler(1) == -1) { 86532377Sminshall setcommandmode(); 86632377Sminshall return; 86732377Sminshall } 86827088Sminshall } 86932377Sminshall } 87032377Sminshall # endif /* !defined(TN3270) */ 87127088Sminshall } 87232377Sminshall 87334848Sminshall #if 0 /* XXX - this not being in is a bug */ 87427088Sminshall /* 87532554Sminshall * nextitem() 87632554Sminshall * 87732554Sminshall * Return the address of the next "item" in the TELNET data 87832554Sminshall * stream. This will be the address of the next character if 87932554Sminshall * the current address is a user data character, or it will 88032554Sminshall * be the address of the character following the TELNET command 88132554Sminshall * if the current address is a TELNET IAC ("I Am a Command") 88232554Sminshall * character. 88332554Sminshall */ 88432554Sminshall 88532554Sminshall static char * 88632554Sminshall nextitem(current) 88732554Sminshall char *current; 88832554Sminshall { 88932554Sminshall if ((*current&0xff) != IAC) { 89032554Sminshall return current+1; 89132554Sminshall } 89232554Sminshall switch (*(current+1)&0xff) { 89332554Sminshall case DO: 89432554Sminshall case DONT: 89532554Sminshall case WILL: 89632554Sminshall case WONT: 89732554Sminshall return current+3; 89832554Sminshall case SB: /* loop forever looking for the SE */ 89932554Sminshall { 90032554Sminshall register char *look = current+2; 90132554Sminshall 90232554Sminshall for (;;) { 90332554Sminshall if ((*look++&0xff) == IAC) { 90432554Sminshall if ((*look++&0xff) == SE) { 90532554Sminshall return look; 90632554Sminshall } 90732554Sminshall } 90832554Sminshall } 90932554Sminshall } 91032554Sminshall default: 91132554Sminshall return current+2; 91232554Sminshall } 91332554Sminshall } 91434848Sminshall #endif /* 0 */ 91532554Sminshall 91632554Sminshall /* 91732554Sminshall * netclear() 91832554Sminshall * 91932554Sminshall * We are about to do a TELNET SYNCH operation. Clear 92032554Sminshall * the path to the network. 92132554Sminshall * 92232554Sminshall * Things are a bit tricky since we may have sent the first 92332554Sminshall * byte or so of a previous TELNET command into the network. 92432554Sminshall * So, we have to scan the network buffer from the beginning 92532554Sminshall * until we are up to where we want to be. 92632554Sminshall * 92732554Sminshall * A side effect of what we do, just to keep things 92832554Sminshall * simple, is to clear the urgent data pointer. The principal 92932554Sminshall * caller should be setting the urgent data pointer AFTER calling 93032554Sminshall * us in any case. 93132554Sminshall */ 93232554Sminshall 93332554Sminshall static void 93432554Sminshall netclear() 93532554Sminshall { 93632554Sminshall #if 0 /* XXX */ 93732554Sminshall register char *thisitem, *next; 93832554Sminshall char *good; 93932554Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 94032554Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 94132554Sminshall 94232554Sminshall thisitem = netobuf; 94332554Sminshall 94432554Sminshall while ((next = nextitem(thisitem)) <= netobuf.send) { 94532554Sminshall thisitem = next; 94632554Sminshall } 94732554Sminshall 94832554Sminshall /* Now, thisitem is first before/at boundary. */ 94932554Sminshall 95032554Sminshall good = netobuf; /* where the good bytes go */ 95132554Sminshall 95232554Sminshall while (netoring.add > thisitem) { 95332554Sminshall if (wewant(thisitem)) { 95432554Sminshall int length; 95532554Sminshall 95632554Sminshall next = thisitem; 95732554Sminshall do { 95832554Sminshall next = nextitem(next); 95932554Sminshall } while (wewant(next) && (nfrontp > next)); 96032554Sminshall length = next-thisitem; 96132554Sminshall memcpy(good, thisitem, length); 96232554Sminshall good += length; 96332554Sminshall thisitem = next; 96432554Sminshall } else { 96532554Sminshall thisitem = nextitem(thisitem); 96632554Sminshall } 96732554Sminshall } 96832554Sminshall 96932554Sminshall #endif /* 0 */ 97032554Sminshall } 97132554Sminshall 97232554Sminshall /* 97332377Sminshall * These routines add various telnet commands to the data stream. 97427088Sminshall */ 97532377Sminshall 97632554Sminshall static void 97732554Sminshall doflush() 97832554Sminshall { 97932554Sminshall NET2ADD(IAC, DO); 98032554Sminshall NETADD(TELOPT_TM); 98132554Sminshall flushline = 1; 98232554Sminshall flushout = 1; 98332554Sminshall ttyflush(1); /* Flush/drop output */ 98432554Sminshall /* do printoption AFTER flush, otherwise the output gets tossed... */ 98532554Sminshall printoption("<SENT", doopt, TELOPT_TM, 0); 98632554Sminshall } 98732554Sminshall 98832377Sminshall void 98932377Sminshall xmitAO() 99027088Sminshall { 99132377Sminshall NET2ADD(IAC, AO); 99232377Sminshall if (autoflush) { 99332377Sminshall doflush(); 99432377Sminshall } 99532377Sminshall } 99627088Sminshall 99732377Sminshall 99832377Sminshall void 99932377Sminshall xmitEL() 100027088Sminshall { 100132377Sminshall NET2ADD(IAC, EL); 100227088Sminshall } 100327088Sminshall 100432377Sminshall void 100532377Sminshall xmitEC() 100627088Sminshall { 100732377Sminshall NET2ADD(IAC, EC); 100827088Sminshall } 100927088Sminshall 101032377Sminshall 101132377Sminshall #if defined(NOT43) 101232377Sminshall int 101332377Sminshall #else /* defined(NOT43) */ 101432377Sminshall void 101532377Sminshall #endif /* defined(NOT43) */ 101632377Sminshall dosynch() 101727088Sminshall { 101832377Sminshall netclear(); /* clear the path to the network */ 101933294Sminshall NETADD(IAC); 102033294Sminshall setneturg(); 102133294Sminshall NETADD(DM); 102227088Sminshall 102332377Sminshall #if defined(NOT43) 102432377Sminshall return 0; 102532377Sminshall #endif /* defined(NOT43) */ 102627088Sminshall } 102727088Sminshall 102832377Sminshall void 102932377Sminshall intp() 103027088Sminshall { 103132377Sminshall NET2ADD(IAC, IP); 103232377Sminshall flushline = 1; 103332377Sminshall if (autoflush) { 103432377Sminshall doflush(); 103532377Sminshall } 103632377Sminshall if (autosynch) { 103732377Sminshall dosynch(); 103832377Sminshall } 103927088Sminshall } 104027186Sminshall 104132377Sminshall void 104232377Sminshall sendbrk() 104327186Sminshall { 104432377Sminshall NET2ADD(IAC, BREAK); 104532377Sminshall flushline = 1; 104632377Sminshall if (autoflush) { 104732377Sminshall doflush(); 104832377Sminshall } 104932377Sminshall if (autosynch) { 105032377Sminshall dosynch(); 105132377Sminshall } 105227186Sminshall } 1053