1*33685Sbostic /* 2*33685Sbostic * Copyright (c) 1988 Regents of the University of California. 3*33685Sbostic * All rights reserved. 4*33685Sbostic * 5*33685Sbostic * Redistribution and use in source and binary forms are permitted 6*33685Sbostic * provided that this notice is preserved and that due credit is given 7*33685Sbostic * to the University of California at Berkeley. The name of the University 8*33685Sbostic * may not be used to endorse or promote products derived from this 9*33685Sbostic * software without specific prior written permission. This software 10*33685Sbostic * is provided ``as is'' without express or implied warranty. 11*33685Sbostic */ 1211758Ssam 1321580Sdist #ifndef lint 14*33685Sbostic static char sccsid[] = "@(#)telnet.c 5.29 (Berkeley) 03/08/88"; 15*33685Sbostic #endif /* not lint */ 1621580Sdist 179217Ssam #include <sys/types.h> 189217Ssam 1932377Sminshall #if defined(unix) 2032377Sminshall /* By the way, we need to include curses.h before telnet.h since, 2132377Sminshall * among other things, telnet.h #defines 'DO', which is a variable 2232377Sminshall * declared in curses.h. 2332377Sminshall */ 2432377Sminshall #include <curses.h> 2532377Sminshall #endif /* defined(unix) */ 2632377Sminshall 2712212Ssam #include <arpa/telnet.h> 2832377Sminshall 2932377Sminshall #if defined(unix) 3027186Sminshall #include <strings.h> 3132377Sminshall #else /* defined(unix) */ 3232377Sminshall #include <string.h> 3332377Sminshall #endif /* defined(unix) */ 349217Ssam 3532381Sminshall #include "ring.h" 3632381Sminshall 3732377Sminshall #include "defines.h" 3832377Sminshall #include "externs.h" 3932377Sminshall #include "types.h" 4032377Sminshall #include "general.h" 4127178Sminshall 4227178Sminshall 4327228Sminshall #define strip(x) ((x)&0x7f) 446000Sroot 4527088Sminshall 4632377Sminshall static char subbuffer[SUBBUFSIZE], 4732377Sminshall *subpointer, *subend; /* buffer for sub-options */ 4827676Sminshall #define SB_CLEAR() subpointer = subbuffer; 4927676Sminshall #define SB_TERM() subend = subpointer; 5027676Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 5127676Sminshall *subpointer++ = (c); \ 5227676Sminshall } 5327676Sminshall 546000Sroot char hisopts[256]; 556000Sroot char myopts[256]; 566000Sroot 576000Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 586000Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 596000Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 606000Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 616000Sroot 6232377Sminshall int 6332377Sminshall connected, 6432377Sminshall showoptions, 6532377Sminshall In3270, /* Are we in 3270 mode? */ 6632377Sminshall ISend, /* trying to send network data in */ 6732377Sminshall debug = 0, 6832377Sminshall crmod, 6932377Sminshall netdata, /* Print out network data flow */ 7032377Sminshall crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ 7132377Sminshall noasynch = 0, /* User specified "-noasynch" on command line */ 7232377Sminshall askedSGA = 0, /* We have talked about suppress go ahead */ 7333286Sminshall telnetport, 7432531Sminshall SYNCHing, /* we are in TELNET SYNCH mode */ 7532531Sminshall flushout, /* flush output */ 7632531Sminshall autoflush = 0, /* flush output when interrupting? */ 7732531Sminshall autosynch, /* send interrupt characters with SYNCH? */ 7832531Sminshall localchars, /* we recognize interrupt/quit */ 7932531Sminshall donelclchars, /* the user has set "localchars" */ 8032531Sminshall donebinarytoggle, /* the user has put us in binary */ 8132531Sminshall dontlecho, /* do we suppress local echoing right now? */ 8232531Sminshall globalmode; 8327088Sminshall 8432377Sminshall #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 856000Sroot 8632377Sminshall char 8732377Sminshall *prompt = 0, 8832377Sminshall escape, 8932377Sminshall echoc; 9027186Sminshall 9127186Sminshall /* 926000Sroot * Telnet receiver states for fsm 936000Sroot */ 946000Sroot #define TS_DATA 0 956000Sroot #define TS_IAC 1 966000Sroot #define TS_WILL 2 976000Sroot #define TS_WONT 3 986000Sroot #define TS_DO 4 996000Sroot #define TS_DONT 5 10027021Sminshall #define TS_CR 6 10127676Sminshall #define TS_SB 7 /* sub-option collection */ 10227676Sminshall #define TS_SE 8 /* looking for sub-option end */ 1036000Sroot 10432377Sminshall static int telrcv_state; 1056000Sroot 10632377Sminshall jmp_buf toplevel = { 0 }; 10732377Sminshall jmp_buf peerdied; 1086000Sroot 10932377Sminshall int flushline; 11027021Sminshall 11132377Sminshall /* 11232377Sminshall * The following are some clocks used to decide how to interpret 11332377Sminshall * the relationship between various variables. 11432377Sminshall */ 1156000Sroot 11632377Sminshall Clocks clocks; 11732377Sminshall 11832377Sminshall Modelist modelist[] = { 11932377Sminshall { "telnet command mode", COMMAND_LINE }, 12032377Sminshall { "character-at-a-time mode", 0 }, 12132377Sminshall { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 12232377Sminshall { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 12332377Sminshall { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 12432377Sminshall { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 12532377Sminshall { "3270 mode", 0 }, 12632377Sminshall }; 1276000Sroot 12832377Sminshall 12932377Sminshall /* 13032377Sminshall * Initialize telnet environment. 13132377Sminshall */ 1326000Sroot 13332377Sminshall init_telnet() 13432377Sminshall { 13532377Sminshall SB_CLEAR(); 13632377Sminshall ClearArray(hisopts); 13732377Sminshall ClearArray(myopts); 1386000Sroot 13932554Sminshall connected = In3270 = ISend = donebinarytoggle = 0; 1406000Sroot 14132377Sminshall #if defined(unix) && defined(TN3270) 14232377Sminshall HaveInput = 0; 14332377Sminshall #endif /* defined(unix) && defined(TN3270) */ 1446000Sroot 14532377Sminshall SYNCHing = 0; 1466000Sroot 14732377Sminshall /* Don't change NetTrace */ 1486000Sroot 14932377Sminshall escape = CONTROL(']'); 15032377Sminshall echoc = CONTROL('E'); 1516000Sroot 15232377Sminshall flushline = 1; 15332377Sminshall telrcv_state = TS_DATA; 15432377Sminshall } 15532554Sminshall 1566000Sroot 15732554Sminshall #include <varargs.h> 1586000Sroot 15932554Sminshall static void 16032554Sminshall printring(va_alist) 16132554Sminshall va_dcl 16232554Sminshall { 16332554Sminshall va_list ap; 16432554Sminshall char buffer[100]; /* where things go */ 16532554Sminshall char *ptr; 16632554Sminshall char *format; 16732554Sminshall char *string; 16832554Sminshall Ring *ring; 16932554Sminshall int i; 17032554Sminshall 17132554Sminshall va_start(ap); 17232554Sminshall 17332554Sminshall ring = va_arg(ap, Ring *); 17432554Sminshall format = va_arg(ap, char *); 17532554Sminshall ptr = buffer; 17632554Sminshall 17732554Sminshall while ((i = *format++) != 0) { 17832554Sminshall if (i == '%') { 17932554Sminshall i = *format++; 18032554Sminshall switch (i) { 18132554Sminshall case 'c': 18232554Sminshall *ptr++ = va_arg(ap, int); 18332554Sminshall break; 18432554Sminshall case 's': 18532554Sminshall string = va_arg(ap, char *); 18632554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 18732554Sminshall ring_supply_data(ring, string, strlen(string)); 18832554Sminshall ptr = buffer; 18932554Sminshall break; 19032554Sminshall case 0: 19132554Sminshall ExitString("printring: trailing %%.\n", 1); 19232554Sminshall /*NOTREACHED*/ 19332554Sminshall default: 19432554Sminshall ExitString("printring: unknown format character.\n", 1); 19532554Sminshall /*NOTREACHED*/ 19632554Sminshall } 19732554Sminshall } else { 19832554Sminshall *ptr++ = i; 19932554Sminshall } 20032554Sminshall } 20132554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 20232554Sminshall } 20332554Sminshall 20432554Sminshall 20532377Sminshall void 20627676Sminshall willoption(option, reply) 20727676Sminshall int option, reply; 2086000Sroot { 2096000Sroot char *fmt; 2106000Sroot 2116000Sroot switch (option) { 2126000Sroot 2136000Sroot case TELOPT_ECHO: 21432377Sminshall # if defined(TN3270) 21532377Sminshall /* 21632377Sminshall * The following is a pain in the rear-end. 21732377Sminshall * Various IBM servers (some versions of Wiscnet, 21832377Sminshall * possibly Fibronics/Spartacus, and who knows who 21932377Sminshall * else) will NOT allow us to send "DO SGA" too early 22032377Sminshall * in the setup proceedings. On the other hand, 22132377Sminshall * 4.2 servers (telnetd) won't set SGA correctly. 22232377Sminshall * So, we are stuck. Empirically (but, based on 22332377Sminshall * a VERY small sample), the IBM servers don't send 22432377Sminshall * out anything about ECHO, so we postpone our sending 22532377Sminshall * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 22632377Sminshall * DO send). 22732377Sminshall */ 22832377Sminshall { 22932377Sminshall if (askedSGA == 0) { 23032377Sminshall askedSGA = 1; 23132377Sminshall if (!hisopts[TELOPT_SGA]) { 23232377Sminshall willoption(TELOPT_SGA, 0); 23332377Sminshall } 23432377Sminshall } 23532377Sminshall } 23632377Sminshall /* Fall through */ 23732377Sminshall case TELOPT_EOR: 23832377Sminshall case TELOPT_BINARY: 23932377Sminshall #endif /* defined(TN3270) */ 2406000Sroot case TELOPT_SGA: 24127110Sminshall settimer(modenegotiated); 2426000Sroot hisopts[option] = 1; 2436000Sroot fmt = doopt; 24427110Sminshall setconnmode(); /* possibly set new tty mode */ 2456000Sroot break; 2466000Sroot 2476000Sroot case TELOPT_TM: 24827110Sminshall return; /* Never reply to TM will's/wont's */ 2496000Sroot 2506000Sroot default: 2516000Sroot fmt = dont; 2526000Sroot break; 2536000Sroot } 25432554Sminshall printring(&netoring, fmt, option); 25527676Sminshall if (reply) 25632377Sminshall printoption(">SENT", fmt, option, reply); 25727676Sminshall else 25832377Sminshall printoption("<SENT", fmt, option, reply); 2596000Sroot } 2606000Sroot 26132377Sminshall void 26227676Sminshall wontoption(option, reply) 26327676Sminshall int option, reply; 2646000Sroot { 2656000Sroot char *fmt; 2666000Sroot 2676000Sroot switch (option) { 2686000Sroot 2696000Sroot case TELOPT_ECHO: 2706000Sroot case TELOPT_SGA: 27127110Sminshall settimer(modenegotiated); 2726000Sroot hisopts[option] = 0; 2736000Sroot fmt = dont; 27427110Sminshall setconnmode(); /* Set new tty mode */ 2756000Sroot break; 2766000Sroot 27727110Sminshall case TELOPT_TM: 27827110Sminshall return; /* Never reply to TM will's/wont's */ 27927110Sminshall 2806000Sroot default: 2816000Sroot fmt = dont; 2826000Sroot } 28332554Sminshall printring(&netoring, fmt, option); 28427676Sminshall if (reply) 28532377Sminshall printoption(">SENT", fmt, option, reply); 28627676Sminshall else 28732377Sminshall printoption("<SENT", fmt, option, reply); 2886000Sroot } 2896000Sroot 29032377Sminshall static void 2916000Sroot dooption(option) 2926000Sroot int option; 2936000Sroot { 2946000Sroot char *fmt; 2956000Sroot 2966000Sroot switch (option) { 2976000Sroot 2986000Sroot case TELOPT_TM: 29913231Ssam fmt = will; 30013231Ssam break; 30113231Ssam 30232377Sminshall # if defined(TN3270) 30332377Sminshall case TELOPT_EOR: 30432377Sminshall case TELOPT_BINARY: 30532377Sminshall # endif /* defined(TN3270) */ 30627676Sminshall case TELOPT_TTYPE: /* terminal type option */ 30727110Sminshall case TELOPT_SGA: /* no big deal */ 3086000Sroot fmt = will; 30927110Sminshall myopts[option] = 1; 3106000Sroot break; 3116000Sroot 31227110Sminshall case TELOPT_ECHO: /* We're never going to echo... */ 3136000Sroot default: 3146000Sroot fmt = wont; 3156000Sroot break; 3166000Sroot } 31732554Sminshall printring(&netoring, fmt, option); 31832377Sminshall printoption(">SENT", fmt, option, 0); 3196000Sroot } 32027676Sminshall 32127676Sminshall /* 32227676Sminshall * suboption() 32327676Sminshall * 32427676Sminshall * Look at the sub-option buffer, and try to be helpful to the other 32527676Sminshall * side. 32627676Sminshall * 32727676Sminshall * Currently we recognize: 32827676Sminshall * 32927676Sminshall * Terminal type, send request. 33027676Sminshall */ 33127676Sminshall 33232377Sminshall static void 33327676Sminshall suboption() 33427676Sminshall { 33532377Sminshall printsub("<", subbuffer, subend-subbuffer+1); 33627676Sminshall switch (subbuffer[0]&0xff) { 33727676Sminshall case TELOPT_TTYPE: 33827676Sminshall if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 33927676Sminshall ; 34027676Sminshall } else { 34127676Sminshall char *name; 34227676Sminshall char namebuf[41]; 34332377Sminshall extern char *getenv(); 34427676Sminshall int len; 34527676Sminshall 34632377Sminshall #if defined(TN3270) 34732531Sminshall if (tn3270_ttype()) { 34832377Sminshall return; 34932377Sminshall } 35032377Sminshall #endif /* defined(TN3270) */ 35127676Sminshall name = getenv("TERM"); 35227676Sminshall if ((name == 0) || ((len = strlen(name)) > 40)) { 35327676Sminshall name = "UNKNOWN"; 35433492Sminshall len = strlen(name); 35527676Sminshall } 35627676Sminshall if ((len + 4+2) < NETROOM()) { 35727676Sminshall strcpy(namebuf, name); 35827676Sminshall upcase(namebuf); 35932554Sminshall printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 36027676Sminshall TELQUAL_IS, namebuf, IAC, SE); 36132381Sminshall /* XXX */ 36232381Sminshall /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ 36332377Sminshall } else { 36432381Sminshall ExitString("No room in buffer for terminal type.\n", 36532377Sminshall 1); 36632377Sminshall /*NOTREACHED*/ 36727676Sminshall } 36827676Sminshall } 36927676Sminshall 37027676Sminshall default: 37127676Sminshall break; 37227676Sminshall } 37327676Sminshall } 37432377Sminshall 37527088Sminshall 37632385Sminshall static int 37732377Sminshall telrcv() 37827110Sminshall { 37932377Sminshall register int c; 38032385Sminshall register int scc; 38132385Sminshall register char *sbp; 38232385Sminshall int count; 38332385Sminshall int returnValue = 0; 38427088Sminshall 38532385Sminshall scc = 0; 38632385Sminshall count = 0; 38732385Sminshall while (TTYROOM() > 2) { 38832385Sminshall if (scc == 0) { 38932385Sminshall if (count) { 39032528Sminshall ring_consumed(&netiring, count); 39132385Sminshall returnValue = 1; 39232385Sminshall count = 0; 39332385Sminshall } 39432528Sminshall sbp = netiring.consume; 39532528Sminshall scc = ring_full_consecutive(&netiring); 39632385Sminshall if (scc == 0) { 39732385Sminshall /* No more data coming in */ 39832385Sminshall break; 39932385Sminshall } 40032385Sminshall } 40132385Sminshall 40232385Sminshall c = *sbp++ & 0xff, scc--; count++; 40332385Sminshall 40432377Sminshall switch (telrcv_state) { 40527110Sminshall 40632377Sminshall case TS_CR: 40732377Sminshall telrcv_state = TS_DATA; 40832377Sminshall if (c == '\0') { 40932377Sminshall break; /* Ignore \0 after CR */ 41032377Sminshall } else if (c == '\n') { 41132657Sminshall if ((!hisopts[TELOPT_ECHO]) && !crmod) { 41232377Sminshall TTYADD(c); 41332377Sminshall } 41432377Sminshall break; 41532377Sminshall } 41632377Sminshall /* Else, fall through */ 41727088Sminshall 41832377Sminshall case TS_DATA: 41932377Sminshall if (c == IAC) { 42032377Sminshall telrcv_state = TS_IAC; 42132377Sminshall continue; 42232377Sminshall } 42332377Sminshall # if defined(TN3270) 42432377Sminshall if (In3270) { 42532377Sminshall *Ifrontp++ = c; 42632385Sminshall while (scc > 0) { 42732385Sminshall c = *sbp++ & 0377, scc--; count++; 42832377Sminshall if (c == IAC) { 42932377Sminshall telrcv_state = TS_IAC; 43032377Sminshall break; 43132377Sminshall } 43232377Sminshall *Ifrontp++ = c; 43332377Sminshall } 43432377Sminshall } else 43532377Sminshall # endif /* defined(TN3270) */ 43632377Sminshall /* 43732377Sminshall * The 'crmod' hack (see following) is needed 43832377Sminshall * since we can't * set CRMOD on output only. 43932377Sminshall * Machines like MULTICS like to send \r without 44032377Sminshall * \n; since we must turn off CRMOD to get proper 44132377Sminshall * input, the mapping is done here (sigh). 44232377Sminshall */ 44332377Sminshall if ((c == '\r') && !hisopts[TELOPT_BINARY]) { 44432377Sminshall if (scc > 0) { 44532377Sminshall c = *sbp&0xff; 44632377Sminshall if (c == 0) { 44732385Sminshall sbp++, scc--; count++; 44832377Sminshall /* a "true" CR */ 44932377Sminshall TTYADD('\r'); 45032377Sminshall } else if (!hisopts[TELOPT_ECHO] && 45132377Sminshall (c == '\n')) { 45232385Sminshall sbp++, scc--; count++; 45332377Sminshall TTYADD('\n'); 45432377Sminshall } else { 45532377Sminshall TTYADD('\r'); 45632377Sminshall if (crmod) { 45732377Sminshall TTYADD('\n'); 45832377Sminshall } 45932377Sminshall } 46032377Sminshall } else { 46132377Sminshall telrcv_state = TS_CR; 46232377Sminshall TTYADD('\r'); 46332377Sminshall if (crmod) { 46432377Sminshall TTYADD('\n'); 46532377Sminshall } 46632377Sminshall } 46732377Sminshall } else { 46832377Sminshall TTYADD(c); 46932377Sminshall } 47032377Sminshall continue; 47127088Sminshall 47232377Sminshall case TS_IAC: 47332377Sminshall switch (c) { 47432377Sminshall 47532377Sminshall case WILL: 47632377Sminshall telrcv_state = TS_WILL; 47732377Sminshall continue; 47827261Sminshall 47932377Sminshall case WONT: 48032377Sminshall telrcv_state = TS_WONT; 48132377Sminshall continue; 48227261Sminshall 48332377Sminshall case DO: 48432377Sminshall telrcv_state = TS_DO; 48532377Sminshall continue; 48627261Sminshall 48732377Sminshall case DONT: 48832377Sminshall telrcv_state = TS_DONT; 48932377Sminshall continue; 49027261Sminshall 49132377Sminshall case DM: 49232377Sminshall /* 49332377Sminshall * We may have missed an urgent notification, 49432377Sminshall * so make sure we flush whatever is in the 49532377Sminshall * buffer currently. 49632377Sminshall */ 49732377Sminshall SYNCHing = 1; 49832377Sminshall ttyflush(1); 49932554Sminshall SYNCHing = stilloob(); 50032377Sminshall settimer(gotDM); 50132377Sminshall break; 50227088Sminshall 50332377Sminshall case NOP: 50432377Sminshall case GA: 50532377Sminshall break; 50627088Sminshall 50732377Sminshall case SB: 50832377Sminshall SB_CLEAR(); 50932377Sminshall telrcv_state = TS_SB; 51032377Sminshall continue; 51127261Sminshall 51232377Sminshall # if defined(TN3270) 51332377Sminshall case EOR: 51432377Sminshall if (In3270) { 51532377Sminshall Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 51632377Sminshall if (Ibackp == Ifrontp) { 51732377Sminshall Ibackp = Ifrontp = Ibuf; 51832377Sminshall ISend = 0; /* should have been! */ 51932377Sminshall } else { 52032377Sminshall ISend = 1; 52127088Sminshall } 52227088Sminshall } 52327088Sminshall break; 52432377Sminshall # endif /* defined(TN3270) */ 52532377Sminshall 52632377Sminshall case IAC: 52732377Sminshall # if !defined(TN3270) 52832377Sminshall TTYADD(IAC); 52932377Sminshall # else /* !defined(TN3270) */ 53032377Sminshall if (In3270) { 53132377Sminshall *Ifrontp++ = IAC; 53232377Sminshall } else { 53332377Sminshall TTYADD(IAC); 53432377Sminshall } 53532377Sminshall # endif /* !defined(TN3270) */ 53627088Sminshall break; 53732377Sminshall 53827088Sminshall default: 53927088Sminshall break; 54027088Sminshall } 54132377Sminshall telrcv_state = TS_DATA; 54232377Sminshall continue; 54327088Sminshall 54432377Sminshall case TS_WILL: 54532377Sminshall printoption(">RCVD", will, c, !hisopts[c]); 54632377Sminshall if (c == TELOPT_TM) { 54732377Sminshall if (flushout) { 54832377Sminshall flushout = 0; 54932377Sminshall } 55032377Sminshall } else if (!hisopts[c]) { 55132377Sminshall willoption(c, 1); 55232377Sminshall } 55332377Sminshall SetIn3270(); 55432377Sminshall telrcv_state = TS_DATA; 55532377Sminshall continue; 55627110Sminshall 55732377Sminshall case TS_WONT: 55832377Sminshall printoption(">RCVD", wont, c, hisopts[c]); 55932377Sminshall if (c == TELOPT_TM) { 56032377Sminshall if (flushout) { 56132377Sminshall flushout = 0; 56232377Sminshall } 56332377Sminshall } else if (hisopts[c]) { 56432377Sminshall wontoption(c, 1); 56532377Sminshall } 56632377Sminshall SetIn3270(); 56732377Sminshall telrcv_state = TS_DATA; 56832377Sminshall continue; 56927088Sminshall 57032377Sminshall case TS_DO: 57132377Sminshall printoption(">RCVD", doopt, c, !myopts[c]); 57232377Sminshall if (!myopts[c]) 57332377Sminshall dooption(c); 57432377Sminshall SetIn3270(); 57532377Sminshall telrcv_state = TS_DATA; 57632377Sminshall continue; 57727088Sminshall 57832377Sminshall case TS_DONT: 57932377Sminshall printoption(">RCVD", dont, c, myopts[c]); 58032377Sminshall if (myopts[c]) { 58132377Sminshall myopts[c] = 0; 58232554Sminshall printring(&netoring, wont, c); 58332377Sminshall flushline = 1; 58432377Sminshall setconnmode(); /* set new tty mode (maybe) */ 58532377Sminshall printoption(">SENT", wont, c, 0); 58632377Sminshall } 58732377Sminshall SetIn3270(); 58832377Sminshall telrcv_state = TS_DATA; 58932377Sminshall continue; 59027088Sminshall 59132377Sminshall case TS_SB: 59232377Sminshall if (c == IAC) { 59332377Sminshall telrcv_state = TS_SE; 59432377Sminshall } else { 59532377Sminshall SB_ACCUM(c); 59632377Sminshall } 59732377Sminshall continue; 59827088Sminshall 59932377Sminshall case TS_SE: 60032377Sminshall if (c != SE) { 60132377Sminshall if (c != IAC) { 60232377Sminshall SB_ACCUM(IAC); 60332377Sminshall } 60432377Sminshall SB_ACCUM(c); 60532377Sminshall telrcv_state = TS_SB; 60632377Sminshall } else { 60732377Sminshall SB_TERM(); 60832377Sminshall suboption(); /* handle sub-option */ 60932377Sminshall SetIn3270(); 61032377Sminshall telrcv_state = TS_DATA; 61132377Sminshall } 61227088Sminshall } 61327088Sminshall } 61432667Sminshall if (count) 61532667Sminshall ring_consumed(&netiring, count); 61632385Sminshall return returnValue||count; 61727088Sminshall } 61832385Sminshall 61932385Sminshall static int 62032554Sminshall telsnd() 62132385Sminshall { 62232385Sminshall int tcc; 62332385Sminshall int count; 62432385Sminshall int returnValue = 0; 62532385Sminshall char *tbp; 62632385Sminshall 62732385Sminshall tcc = 0; 62832385Sminshall count = 0; 62932385Sminshall while (NETROOM() > 2) { 63032385Sminshall register int sc; 63132385Sminshall register int c; 63232385Sminshall 63332385Sminshall if (tcc == 0) { 63432385Sminshall if (count) { 63532528Sminshall ring_consumed(&ttyiring, count); 63632385Sminshall returnValue = 1; 63732385Sminshall count = 0; 63832385Sminshall } 63932528Sminshall tbp = ttyiring.consume; 64032528Sminshall tcc = ring_full_consecutive(&ttyiring); 64132385Sminshall if (tcc == 0) { 64232385Sminshall break; 64332385Sminshall } 64432385Sminshall } 64532385Sminshall c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; 64632385Sminshall if (sc == escape) { 64732385Sminshall command(0); 64832385Sminshall tcc = 0; 64932385Sminshall flushline = 1; 65032385Sminshall break; 65132385Sminshall } else if (MODE_LINE(globalmode) && (sc == echoc)) { 65232385Sminshall if (tcc > 0 && strip(*tbp) == echoc) { 65332385Sminshall tcc--; tbp++; count++; 65432385Sminshall } else { 65532385Sminshall dontlecho = !dontlecho; 65632385Sminshall settimer(echotoggle); 65732385Sminshall setconnmode(); 65832385Sminshall flushline = 1; 65932385Sminshall break; 66032385Sminshall } 66132385Sminshall } 66232385Sminshall if (localchars) { 66332385Sminshall if (TerminalSpecialChars(sc) == 0) { 66432385Sminshall break; 66532385Sminshall } 66632385Sminshall } 66732385Sminshall if (!myopts[TELOPT_BINARY]) { 66832385Sminshall switch (c) { 66932385Sminshall case '\n': 67032385Sminshall /* 67132385Sminshall * If we are in CRMOD mode (\r ==> \n) 67232385Sminshall * on our local machine, then probably 67332385Sminshall * a newline (unix) is CRLF (TELNET). 67432385Sminshall */ 67532385Sminshall if (MODE_LOCAL_CHARS(globalmode)) { 67632385Sminshall NETADD('\r'); 67732385Sminshall } 67832385Sminshall NETADD('\n'); 67932385Sminshall flushline = 1; 68032385Sminshall break; 68132385Sminshall case '\r': 68232385Sminshall if (!crlf) { 68332385Sminshall NET2ADD('\r', '\0'); 68432385Sminshall } else { 68532385Sminshall NET2ADD('\r', '\n'); 68632385Sminshall } 68732385Sminshall flushline = 1; 68832385Sminshall break; 68932385Sminshall case IAC: 69032385Sminshall NET2ADD(IAC, IAC); 69132385Sminshall break; 69232385Sminshall default: 69332385Sminshall NETADD(c); 69432385Sminshall break; 69532385Sminshall } 69632385Sminshall } else if (c == IAC) { 69732385Sminshall NET2ADD(IAC, IAC); 69832385Sminshall } else { 69932385Sminshall NETADD(c); 70032385Sminshall } 70132385Sminshall } 70232667Sminshall if (count) 70332667Sminshall ring_consumed(&ttyiring, count); 70432385Sminshall return returnValue||count; /* Non-zero if we did anything */ 70532385Sminshall } 70632377Sminshall 70727088Sminshall /* 70832377Sminshall * Scheduler() 70932377Sminshall * 71032377Sminshall * Try to do something. 71132377Sminshall * 71232377Sminshall * If we do something useful, return 1; else return 0. 71332377Sminshall * 71427110Sminshall */ 71527110Sminshall 71627110Sminshall 71732377Sminshall int 71832377Sminshall Scheduler(block) 71932377Sminshall int block; /* should we block in the select ? */ 72027110Sminshall { 72132377Sminshall register int c; 72232377Sminshall /* One wants to be a bit careful about setting returnValue 72332377Sminshall * to one, since a one implies we did some useful work, 72432377Sminshall * and therefore probably won't be called to block next 72532377Sminshall * time (TN3270 mode only). 72632377Sminshall */ 72732531Sminshall int returnValue; 72832531Sminshall int netin, netout, netex, ttyin, ttyout; 72927110Sminshall 73032531Sminshall /* Decide which rings should be processed */ 73132531Sminshall 73232531Sminshall netout = ring_full_count(&netoring) && 73332531Sminshall (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]); 73432531Sminshall ttyout = ring_full_count(&ttyoring); 73532531Sminshall 73632377Sminshall #if defined(TN3270) 73732531Sminshall ttyin = ring_empty_count(&ttyiring) && (shell_active == 0); 73832377Sminshall #else /* defined(TN3270) */ 73932531Sminshall ttyin = ring_empty_count(&ttyiring); 74032377Sminshall #endif /* defined(TN3270) */ 74132531Sminshall 74232531Sminshall #if defined(TN3270) 74332531Sminshall netin = ring_empty_count(&netiring); 74432377Sminshall # else /* !defined(TN3270) */ 74532531Sminshall netin = !ISend && ring_empty_count(&netiring); 74632377Sminshall # endif /* !defined(TN3270) */ 74732531Sminshall 74832531Sminshall netex = !SYNCHing; 74932531Sminshall 75032531Sminshall /* If we have seen a signal recently, reset things */ 75132377Sminshall # if defined(TN3270) && defined(unix) 75232377Sminshall if (HaveInput) { 75332377Sminshall HaveInput = 0; 75432377Sminshall signal(SIGIO, inputAvailable); 75532377Sminshall } 75632377Sminshall #endif /* defined(TN3270) && defined(unix) */ 75732377Sminshall 75832531Sminshall /* Call to system code to process rings */ 75927178Sminshall 76032531Sminshall returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); 76127178Sminshall 76232531Sminshall /* Now, look at the input rings, looking for work to do. */ 76332377Sminshall 76432531Sminshall if (ring_full_count(&ttyiring)) { 76532377Sminshall # if defined(TN3270) 76632377Sminshall if (In3270) { 76732385Sminshall c = DataFromTerminal(ttyiring.send, 76832528Sminshall ring_full_consecutive(&ttyiring)); 76932377Sminshall if (c) { 77032377Sminshall returnValue = 1; 77132667Sminshall ring_consumed(&ttyiring, c); 77232377Sminshall } 77332377Sminshall } else { 77432377Sminshall # endif /* defined(TN3270) */ 77532554Sminshall returnValue |= telsnd(); 77632377Sminshall # if defined(TN3270) 77727178Sminshall } 77832531Sminshall # endif /* defined(TN3270) */ 77927178Sminshall } 78032377Sminshall 78132528Sminshall if (ring_full_count(&netiring)) { 78232377Sminshall # if !defined(TN3270) 78332385Sminshall returnValue |= telrcv(); 78432377Sminshall # else /* !defined(TN3270) */ 78532377Sminshall returnValue = Push3270(); 78632377Sminshall # endif /* !defined(TN3270) */ 78732377Sminshall } 78832377Sminshall return returnValue; 78927178Sminshall } 79027178Sminshall 79127178Sminshall /* 79232377Sminshall * Select from tty and network... 79327088Sminshall */ 79432377Sminshall void 79532377Sminshall telnet() 79627088Sminshall { 79732531Sminshall sys_telnet_init(); 79827088Sminshall 79932377Sminshall # if !defined(TN3270) 80032377Sminshall if (telnetport) { 80132377Sminshall if (!hisopts[TELOPT_SGA]) { 80232377Sminshall willoption(TELOPT_SGA, 0); 80327110Sminshall } 80432377Sminshall if (!myopts[TELOPT_TTYPE]) { 80532377Sminshall dooption(TELOPT_TTYPE, 0); 80632377Sminshall } 80727178Sminshall } 80832377Sminshall # endif /* !defined(TN3270) */ 80927088Sminshall 81032377Sminshall # if !defined(TN3270) 81132377Sminshall for (;;) { 81232385Sminshall int schedValue; 81332385Sminshall 81432385Sminshall while ((schedValue = Scheduler(0)) != 0) { 81532385Sminshall if (schedValue == -1) { 81632385Sminshall setcommandmode(); 81732385Sminshall return; 81832385Sminshall } 81932385Sminshall } 82032385Sminshall 82132531Sminshall if (Scheduler(1) == -1) { 82232377Sminshall setcommandmode(); 82332377Sminshall return; 82432377Sminshall } 82532377Sminshall } 82632377Sminshall # else /* !defined(TN3270) */ 82732377Sminshall for (;;) { 82832377Sminshall int schedValue; 82927088Sminshall 83032377Sminshall while (!In3270 && !shell_active) { 83132531Sminshall if (Scheduler(1) == -1) { 83232377Sminshall setcommandmode(); 83332377Sminshall return; 83432377Sminshall } 83527088Sminshall } 83632377Sminshall 83732377Sminshall while ((schedValue = Scheduler(0)) != 0) { 83832377Sminshall if (schedValue == -1) { 83932377Sminshall setcommandmode(); 84032377Sminshall return; 84132377Sminshall } 84227088Sminshall } 84332377Sminshall /* If there is data waiting to go out to terminal, don't 84432377Sminshall * schedule any more data for the terminal. 84532377Sminshall */ 84632377Sminshall if (tfrontp-tbackp) { 84732377Sminshall schedValue = 1; 84827088Sminshall } else { 84932377Sminshall if (shell_active) { 85032377Sminshall if (shell_continue() == 0) { 85132377Sminshall ConnectScreen(); 85227088Sminshall } 85332377Sminshall } else if (In3270) { 85432377Sminshall schedValue = DoTerminalOutput(); 85532377Sminshall } 85627088Sminshall } 85732377Sminshall if (schedValue && (shell_active == 0)) { 85832531Sminshall if (Scheduler(1) == -1) { 85932377Sminshall setcommandmode(); 86032377Sminshall return; 86132377Sminshall } 86227088Sminshall } 86332377Sminshall } 86432377Sminshall # endif /* !defined(TN3270) */ 86527088Sminshall } 86632377Sminshall 86727088Sminshall /* 86832554Sminshall * nextitem() 86932554Sminshall * 87032554Sminshall * Return the address of the next "item" in the TELNET data 87132554Sminshall * stream. This will be the address of the next character if 87232554Sminshall * the current address is a user data character, or it will 87332554Sminshall * be the address of the character following the TELNET command 87432554Sminshall * if the current address is a TELNET IAC ("I Am a Command") 87532554Sminshall * character. 87632554Sminshall */ 87732554Sminshall 87832554Sminshall static char * 87932554Sminshall nextitem(current) 88032554Sminshall char *current; 88132554Sminshall { 88232554Sminshall if ((*current&0xff) != IAC) { 88332554Sminshall return current+1; 88432554Sminshall } 88532554Sminshall switch (*(current+1)&0xff) { 88632554Sminshall case DO: 88732554Sminshall case DONT: 88832554Sminshall case WILL: 88932554Sminshall case WONT: 89032554Sminshall return current+3; 89132554Sminshall case SB: /* loop forever looking for the SE */ 89232554Sminshall { 89332554Sminshall register char *look = current+2; 89432554Sminshall 89532554Sminshall for (;;) { 89632554Sminshall if ((*look++&0xff) == IAC) { 89732554Sminshall if ((*look++&0xff) == SE) { 89832554Sminshall return look; 89932554Sminshall } 90032554Sminshall } 90132554Sminshall } 90232554Sminshall } 90332554Sminshall default: 90432554Sminshall return current+2; 90532554Sminshall } 90632554Sminshall } 90732554Sminshall 90832554Sminshall /* 90932554Sminshall * netclear() 91032554Sminshall * 91132554Sminshall * We are about to do a TELNET SYNCH operation. Clear 91232554Sminshall * the path to the network. 91332554Sminshall * 91432554Sminshall * Things are a bit tricky since we may have sent the first 91532554Sminshall * byte or so of a previous TELNET command into the network. 91632554Sminshall * So, we have to scan the network buffer from the beginning 91732554Sminshall * until we are up to where we want to be. 91832554Sminshall * 91932554Sminshall * A side effect of what we do, just to keep things 92032554Sminshall * simple, is to clear the urgent data pointer. The principal 92132554Sminshall * caller should be setting the urgent data pointer AFTER calling 92232554Sminshall * us in any case. 92332554Sminshall */ 92432554Sminshall 92532554Sminshall static void 92632554Sminshall netclear() 92732554Sminshall { 92832554Sminshall #if 0 /* XXX */ 92932554Sminshall register char *thisitem, *next; 93032554Sminshall char *good; 93132554Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 93232554Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 93332554Sminshall 93432554Sminshall thisitem = netobuf; 93532554Sminshall 93632554Sminshall while ((next = nextitem(thisitem)) <= netobuf.send) { 93732554Sminshall thisitem = next; 93832554Sminshall } 93932554Sminshall 94032554Sminshall /* Now, thisitem is first before/at boundary. */ 94132554Sminshall 94232554Sminshall good = netobuf; /* where the good bytes go */ 94332554Sminshall 94432554Sminshall while (netoring.add > thisitem) { 94532554Sminshall if (wewant(thisitem)) { 94632554Sminshall int length; 94732554Sminshall 94832554Sminshall next = thisitem; 94932554Sminshall do { 95032554Sminshall next = nextitem(next); 95132554Sminshall } while (wewant(next) && (nfrontp > next)); 95232554Sminshall length = next-thisitem; 95332554Sminshall memcpy(good, thisitem, length); 95432554Sminshall good += length; 95532554Sminshall thisitem = next; 95632554Sminshall } else { 95732554Sminshall thisitem = nextitem(thisitem); 95832554Sminshall } 95932554Sminshall } 96032554Sminshall 96132554Sminshall #endif /* 0 */ 96232554Sminshall } 96332554Sminshall 96432554Sminshall /* 96532377Sminshall * These routines add various telnet commands to the data stream. 96627088Sminshall */ 96732377Sminshall 96832554Sminshall static void 96932554Sminshall doflush() 97032554Sminshall { 97132554Sminshall NET2ADD(IAC, DO); 97232554Sminshall NETADD(TELOPT_TM); 97332554Sminshall flushline = 1; 97432554Sminshall flushout = 1; 97532554Sminshall ttyflush(1); /* Flush/drop output */ 97632554Sminshall /* do printoption AFTER flush, otherwise the output gets tossed... */ 97732554Sminshall printoption("<SENT", doopt, TELOPT_TM, 0); 97832554Sminshall } 97932554Sminshall 98032377Sminshall void 98132377Sminshall xmitAO() 98227088Sminshall { 98332377Sminshall NET2ADD(IAC, AO); 98432377Sminshall if (autoflush) { 98532377Sminshall doflush(); 98632377Sminshall } 98732377Sminshall } 98827088Sminshall 98932377Sminshall 99032377Sminshall void 99132377Sminshall xmitEL() 99227088Sminshall { 99332377Sminshall NET2ADD(IAC, EL); 99427088Sminshall } 99527088Sminshall 99632377Sminshall void 99732377Sminshall xmitEC() 99827088Sminshall { 99932377Sminshall NET2ADD(IAC, EC); 100027088Sminshall } 100127088Sminshall 100232377Sminshall 100332377Sminshall #if defined(NOT43) 100432377Sminshall int 100532377Sminshall #else /* defined(NOT43) */ 100632377Sminshall void 100732377Sminshall #endif /* defined(NOT43) */ 100832377Sminshall dosynch() 100927088Sminshall { 101032377Sminshall netclear(); /* clear the path to the network */ 101133294Sminshall NETADD(IAC); 101233294Sminshall setneturg(); 101333294Sminshall NETADD(DM); 101427088Sminshall 101532377Sminshall #if defined(NOT43) 101632377Sminshall return 0; 101732377Sminshall #endif /* defined(NOT43) */ 101827088Sminshall } 101927088Sminshall 102032377Sminshall void 102132377Sminshall intp() 102227088Sminshall { 102332377Sminshall NET2ADD(IAC, IP); 102432377Sminshall flushline = 1; 102532377Sminshall if (autoflush) { 102632377Sminshall doflush(); 102732377Sminshall } 102832377Sminshall if (autosynch) { 102932377Sminshall dosynch(); 103032377Sminshall } 103127088Sminshall } 103227186Sminshall 103332377Sminshall void 103432377Sminshall sendbrk() 103527186Sminshall { 103632377Sminshall NET2ADD(IAC, BREAK); 103732377Sminshall flushline = 1; 103832377Sminshall if (autoflush) { 103932377Sminshall doflush(); 104032377Sminshall } 104132377Sminshall if (autosynch) { 104232377Sminshall dosynch(); 104332377Sminshall } 104427186Sminshall } 1045