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