133685Sbostic /* 233685Sbostic * Copyright (c) 1988 Regents of the University of California. 333685Sbostic * All rights reserved. 433685Sbostic * 533685Sbostic * Redistribution and use in source and binary forms are permitted 633685Sbostic * provided that this notice is preserved and that due credit is given 733685Sbostic * to the University of California at Berkeley. The name of the University 833685Sbostic * may not be used to endorse or promote products derived from this 933685Sbostic * software without specific prior written permission. This software 1033685Sbostic * is provided ``as is'' without express or implied warranty. 1133685Sbostic */ 1211758Ssam 1321580Sdist #ifndef lint 14*34848Sminshall static char sccsid[] = "@(#)telnet.c 5.32 (Berkeley) 06/27/88"; 1533685Sbostic #endif /* not lint */ 1621580Sdist 179217Ssam #include <sys/types.h> 189217Ssam 1932377Sminshall #if defined(unix) 2033804Sminshall #include <signal.h> 2132377Sminshall /* By the way, we need to include curses.h before telnet.h since, 2232377Sminshall * among other things, telnet.h #defines 'DO', which is a variable 2332377Sminshall * declared in curses.h. 2432377Sminshall */ 2532377Sminshall #include <curses.h> 2632377Sminshall #endif /* defined(unix) */ 2732377Sminshall 2812212Ssam #include <arpa/telnet.h> 2932377Sminshall 3032377Sminshall #if defined(unix) 3127186Sminshall #include <strings.h> 3232377Sminshall #else /* defined(unix) */ 3332377Sminshall #include <string.h> 3432377Sminshall #endif /* defined(unix) */ 359217Ssam 3632381Sminshall #include "ring.h" 3732381Sminshall 3832377Sminshall #include "defines.h" 3932377Sminshall #include "externs.h" 4032377Sminshall #include "types.h" 4132377Sminshall #include "general.h" 4227178Sminshall 4327178Sminshall 4427228Sminshall #define strip(x) ((x)&0x7f) 456000Sroot 4627088Sminshall 4732377Sminshall static char subbuffer[SUBBUFSIZE], 4832377Sminshall *subpointer, *subend; /* buffer for sub-options */ 4927676Sminshall #define SB_CLEAR() subpointer = subbuffer; 5027676Sminshall #define SB_TERM() subend = subpointer; 5127676Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 5227676Sminshall *subpointer++ = (c); \ 5327676Sminshall } 5427676Sminshall 556000Sroot char hisopts[256]; 566000Sroot char myopts[256]; 576000Sroot 586000Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 596000Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 606000Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 616000Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 626000Sroot 6332377Sminshall int 6432377Sminshall connected, 6532377Sminshall showoptions, 6632377Sminshall In3270, /* Are we in 3270 mode? */ 6732377Sminshall ISend, /* trying to send network data in */ 6832377Sminshall debug = 0, 6932377Sminshall crmod, 7032377Sminshall netdata, /* Print out network data flow */ 7132377Sminshall crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ 72*34848Sminshall #if defined(TN3270) 7332377Sminshall noasynch = 0, /* User specified "-noasynch" on command line */ 7432377Sminshall askedSGA = 0, /* We have talked about suppress go ahead */ 75*34848Sminshall #endif /* defined(TN3270) */ 7633286Sminshall telnetport, 7732531Sminshall SYNCHing, /* we are in TELNET SYNCH mode */ 7832531Sminshall flushout, /* flush output */ 7932531Sminshall autoflush = 0, /* flush output when interrupting? */ 8032531Sminshall autosynch, /* send interrupt characters with SYNCH? */ 8132531Sminshall localchars, /* we recognize interrupt/quit */ 8232531Sminshall donelclchars, /* the user has set "localchars" */ 8332531Sminshall donebinarytoggle, /* the user has put us in binary */ 8432531Sminshall dontlecho, /* do we suppress local echoing right now? */ 8532531Sminshall globalmode; 8627088Sminshall 8732377Sminshall #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 886000Sroot 8932377Sminshall char 9032377Sminshall *prompt = 0, 9132377Sminshall escape, 9232377Sminshall echoc; 9327186Sminshall 9427186Sminshall /* 956000Sroot * Telnet receiver states for fsm 966000Sroot */ 976000Sroot #define TS_DATA 0 986000Sroot #define TS_IAC 1 996000Sroot #define TS_WILL 2 1006000Sroot #define TS_WONT 3 1016000Sroot #define TS_DO 4 1026000Sroot #define TS_DONT 5 10327021Sminshall #define TS_CR 6 10427676Sminshall #define TS_SB 7 /* sub-option collection */ 10527676Sminshall #define TS_SE 8 /* looking for sub-option end */ 1066000Sroot 10732377Sminshall static int telrcv_state; 1086000Sroot 10932377Sminshall jmp_buf toplevel = { 0 }; 11032377Sminshall jmp_buf peerdied; 1116000Sroot 11232377Sminshall int flushline; 11327021Sminshall 11432377Sminshall /* 11532377Sminshall * The following are some clocks used to decide how to interpret 11632377Sminshall * the relationship between various variables. 11732377Sminshall */ 1186000Sroot 11932377Sminshall Clocks clocks; 12032377Sminshall 12132377Sminshall Modelist modelist[] = { 12232377Sminshall { "telnet command mode", COMMAND_LINE }, 12332377Sminshall { "character-at-a-time mode", 0 }, 12432377Sminshall { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 12532377Sminshall { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 12632377Sminshall { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 12732377Sminshall { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 12832377Sminshall { "3270 mode", 0 }, 12932377Sminshall }; 1306000Sroot 13132377Sminshall 13232377Sminshall /* 13332377Sminshall * Initialize telnet environment. 13432377Sminshall */ 1356000Sroot 13632377Sminshall init_telnet() 13732377Sminshall { 13832377Sminshall SB_CLEAR(); 13932377Sminshall ClearArray(hisopts); 14032377Sminshall ClearArray(myopts); 1416000Sroot 14232554Sminshall connected = In3270 = ISend = donebinarytoggle = 0; 1436000Sroot 14432377Sminshall #if defined(unix) && defined(TN3270) 14532377Sminshall HaveInput = 0; 14632377Sminshall #endif /* defined(unix) && defined(TN3270) */ 1476000Sroot 14832377Sminshall SYNCHing = 0; 1496000Sroot 15032377Sminshall /* Don't change NetTrace */ 1516000Sroot 15232377Sminshall escape = CONTROL(']'); 15332377Sminshall echoc = CONTROL('E'); 1546000Sroot 15532377Sminshall flushline = 1; 15632377Sminshall telrcv_state = TS_DATA; 15732377Sminshall } 15832554Sminshall 1596000Sroot 16032554Sminshall #include <varargs.h> 1616000Sroot 162*34848Sminshall /*VARARGS*/ 16332554Sminshall static void 16432554Sminshall printring(va_alist) 16532554Sminshall va_dcl 16632554Sminshall { 16732554Sminshall va_list ap; 16832554Sminshall char buffer[100]; /* where things go */ 16932554Sminshall char *ptr; 17032554Sminshall char *format; 17132554Sminshall char *string; 17232554Sminshall Ring *ring; 17332554Sminshall int i; 17432554Sminshall 17532554Sminshall va_start(ap); 17632554Sminshall 17732554Sminshall ring = va_arg(ap, Ring *); 17832554Sminshall format = va_arg(ap, char *); 17932554Sminshall ptr = buffer; 18032554Sminshall 18132554Sminshall while ((i = *format++) != 0) { 18232554Sminshall if (i == '%') { 18332554Sminshall i = *format++; 18432554Sminshall switch (i) { 18532554Sminshall case 'c': 18632554Sminshall *ptr++ = va_arg(ap, int); 18732554Sminshall break; 18832554Sminshall case 's': 18932554Sminshall string = va_arg(ap, char *); 19032554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 19132554Sminshall ring_supply_data(ring, string, strlen(string)); 19232554Sminshall ptr = buffer; 19332554Sminshall break; 19432554Sminshall case 0: 19532554Sminshall ExitString("printring: trailing %%.\n", 1); 19632554Sminshall /*NOTREACHED*/ 19732554Sminshall default: 19832554Sminshall ExitString("printring: unknown format character.\n", 1); 19932554Sminshall /*NOTREACHED*/ 20032554Sminshall } 20132554Sminshall } else { 20232554Sminshall *ptr++ = i; 20332554Sminshall } 20432554Sminshall } 20532554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 20632554Sminshall } 20732554Sminshall 20832554Sminshall 20932377Sminshall void 21027676Sminshall willoption(option, reply) 21127676Sminshall int option, reply; 2126000Sroot { 2136000Sroot char *fmt; 2146000Sroot 2156000Sroot switch (option) { 2166000Sroot 2176000Sroot case TELOPT_ECHO: 21832377Sminshall # if defined(TN3270) 21932377Sminshall /* 22032377Sminshall * The following is a pain in the rear-end. 22132377Sminshall * Various IBM servers (some versions of Wiscnet, 22232377Sminshall * possibly Fibronics/Spartacus, and who knows who 22332377Sminshall * else) will NOT allow us to send "DO SGA" too early 22432377Sminshall * in the setup proceedings. On the other hand, 22532377Sminshall * 4.2 servers (telnetd) won't set SGA correctly. 22632377Sminshall * So, we are stuck. Empirically (but, based on 22732377Sminshall * a VERY small sample), the IBM servers don't send 22832377Sminshall * out anything about ECHO, so we postpone our sending 22932377Sminshall * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 23032377Sminshall * DO send). 23132377Sminshall */ 23232377Sminshall { 23332377Sminshall if (askedSGA == 0) { 23432377Sminshall askedSGA = 1; 23532377Sminshall if (!hisopts[TELOPT_SGA]) { 23632377Sminshall willoption(TELOPT_SGA, 0); 23732377Sminshall } 23832377Sminshall } 23932377Sminshall } 24032377Sminshall /* Fall through */ 24132377Sminshall case TELOPT_EOR: 24232377Sminshall case TELOPT_BINARY: 24332377Sminshall #endif /* defined(TN3270) */ 2446000Sroot case TELOPT_SGA: 24527110Sminshall settimer(modenegotiated); 2466000Sroot hisopts[option] = 1; 2476000Sroot fmt = doopt; 24827110Sminshall setconnmode(); /* possibly set new tty mode */ 2496000Sroot break; 2506000Sroot 2516000Sroot case TELOPT_TM: 25227110Sminshall return; /* Never reply to TM will's/wont's */ 2536000Sroot 2546000Sroot default: 2556000Sroot fmt = dont; 2566000Sroot break; 2576000Sroot } 25832554Sminshall printring(&netoring, fmt, option); 25927676Sminshall if (reply) 26032377Sminshall printoption(">SENT", fmt, option, reply); 26127676Sminshall else 26232377Sminshall printoption("<SENT", fmt, option, reply); 2636000Sroot } 2646000Sroot 26532377Sminshall void 26627676Sminshall wontoption(option, reply) 26727676Sminshall int option, reply; 2686000Sroot { 2696000Sroot char *fmt; 2706000Sroot 2716000Sroot switch (option) { 2726000Sroot 2736000Sroot case TELOPT_ECHO: 2746000Sroot case TELOPT_SGA: 27527110Sminshall settimer(modenegotiated); 2766000Sroot hisopts[option] = 0; 2776000Sroot fmt = dont; 27827110Sminshall setconnmode(); /* Set new tty mode */ 2796000Sroot break; 2806000Sroot 28127110Sminshall case TELOPT_TM: 28227110Sminshall return; /* Never reply to TM will's/wont's */ 28327110Sminshall 2846000Sroot default: 2856000Sroot fmt = dont; 2866000Sroot } 28732554Sminshall printring(&netoring, fmt, option); 28827676Sminshall if (reply) 28932377Sminshall printoption(">SENT", fmt, option, reply); 29027676Sminshall else 29132377Sminshall printoption("<SENT", fmt, option, reply); 2926000Sroot } 2936000Sroot 29432377Sminshall static void 2956000Sroot dooption(option) 2966000Sroot int option; 2976000Sroot { 2986000Sroot char *fmt; 2996000Sroot 3006000Sroot switch (option) { 3016000Sroot 3026000Sroot case TELOPT_TM: 30313231Ssam fmt = will; 30413231Ssam break; 30513231Ssam 30632377Sminshall # if defined(TN3270) 30732377Sminshall case TELOPT_EOR: 30832377Sminshall case TELOPT_BINARY: 30932377Sminshall # endif /* defined(TN3270) */ 31027676Sminshall case TELOPT_TTYPE: /* terminal type option */ 31127110Sminshall case TELOPT_SGA: /* no big deal */ 3126000Sroot fmt = will; 31327110Sminshall myopts[option] = 1; 3146000Sroot break; 3156000Sroot 31627110Sminshall case TELOPT_ECHO: /* We're never going to echo... */ 3176000Sroot default: 3186000Sroot fmt = wont; 3196000Sroot break; 3206000Sroot } 32132554Sminshall printring(&netoring, fmt, option); 32232377Sminshall printoption(">SENT", fmt, option, 0); 3236000Sroot } 32427676Sminshall 32527676Sminshall /* 32627676Sminshall * suboption() 32727676Sminshall * 32827676Sminshall * Look at the sub-option buffer, and try to be helpful to the other 32927676Sminshall * side. 33027676Sminshall * 33127676Sminshall * Currently we recognize: 33227676Sminshall * 33327676Sminshall * Terminal type, send request. 33427676Sminshall */ 33527676Sminshall 33632377Sminshall static void 33727676Sminshall suboption() 33827676Sminshall { 33932377Sminshall printsub("<", subbuffer, subend-subbuffer+1); 34027676Sminshall switch (subbuffer[0]&0xff) { 34127676Sminshall case TELOPT_TTYPE: 34227676Sminshall if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 34327676Sminshall ; 34427676Sminshall } else { 34527676Sminshall char *name; 34627676Sminshall char namebuf[41]; 34732377Sminshall extern char *getenv(); 34827676Sminshall int len; 34927676Sminshall 35032377Sminshall #if defined(TN3270) 35132531Sminshall if (tn3270_ttype()) { 35232377Sminshall return; 35332377Sminshall } 35432377Sminshall #endif /* defined(TN3270) */ 35527676Sminshall name = getenv("TERM"); 35627676Sminshall if ((name == 0) || ((len = strlen(name)) > 40)) { 35727676Sminshall name = "UNKNOWN"; 35833492Sminshall len = strlen(name); 35927676Sminshall } 36027676Sminshall if ((len + 4+2) < NETROOM()) { 36127676Sminshall strcpy(namebuf, name); 36227676Sminshall upcase(namebuf); 36332554Sminshall printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 36427676Sminshall TELQUAL_IS, namebuf, IAC, SE); 36532381Sminshall /* XXX */ 36632381Sminshall /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ 36732377Sminshall } else { 36832381Sminshall ExitString("No room in buffer for terminal type.\n", 36932377Sminshall 1); 37032377Sminshall /*NOTREACHED*/ 37127676Sminshall } 37227676Sminshall } 37327676Sminshall 37427676Sminshall default: 37527676Sminshall break; 37627676Sminshall } 37727676Sminshall } 37832377Sminshall 37927088Sminshall 38033804Sminshall int 38132377Sminshall telrcv() 38227110Sminshall { 38332377Sminshall register int c; 38432385Sminshall register int scc; 38532385Sminshall register char *sbp; 38632385Sminshall int count; 38732385Sminshall int returnValue = 0; 38827088Sminshall 38932385Sminshall scc = 0; 39032385Sminshall count = 0; 39132385Sminshall while (TTYROOM() > 2) { 39232385Sminshall if (scc == 0) { 39332385Sminshall if (count) { 39432528Sminshall ring_consumed(&netiring, count); 39532385Sminshall returnValue = 1; 39632385Sminshall count = 0; 39732385Sminshall } 39832528Sminshall sbp = netiring.consume; 39932528Sminshall scc = ring_full_consecutive(&netiring); 40032385Sminshall if (scc == 0) { 40132385Sminshall /* No more data coming in */ 40232385Sminshall break; 40332385Sminshall } 40432385Sminshall } 40532385Sminshall 40632385Sminshall c = *sbp++ & 0xff, scc--; count++; 40732385Sminshall 40832377Sminshall switch (telrcv_state) { 40927110Sminshall 41032377Sminshall case TS_CR: 41132377Sminshall telrcv_state = TS_DATA; 41232377Sminshall if (c == '\0') { 41332377Sminshall break; /* Ignore \0 after CR */ 41432377Sminshall } else if (c == '\n') { 41532657Sminshall if ((!hisopts[TELOPT_ECHO]) && !crmod) { 41632377Sminshall TTYADD(c); 41732377Sminshall } 41832377Sminshall break; 41932377Sminshall } 42032377Sminshall /* Else, fall through */ 42127088Sminshall 42232377Sminshall case TS_DATA: 42332377Sminshall if (c == IAC) { 42432377Sminshall telrcv_state = TS_IAC; 42533804Sminshall break; 42632377Sminshall } 42732377Sminshall # if defined(TN3270) 42832377Sminshall if (In3270) { 42932377Sminshall *Ifrontp++ = c; 43032385Sminshall while (scc > 0) { 43132385Sminshall c = *sbp++ & 0377, scc--; count++; 43232377Sminshall if (c == IAC) { 43332377Sminshall telrcv_state = TS_IAC; 43434304Sminshall break; 43532377Sminshall } 43632377Sminshall *Ifrontp++ = c; 43732377Sminshall } 43832377Sminshall } else 43932377Sminshall # endif /* defined(TN3270) */ 44032377Sminshall /* 44132377Sminshall * The 'crmod' hack (see following) is needed 44232377Sminshall * since we can't * set CRMOD on output only. 44332377Sminshall * Machines like MULTICS like to send \r without 44432377Sminshall * \n; since we must turn off CRMOD to get proper 44532377Sminshall * input, the mapping is done here (sigh). 44632377Sminshall */ 44732377Sminshall if ((c == '\r') && !hisopts[TELOPT_BINARY]) { 44832377Sminshall if (scc > 0) { 44932377Sminshall c = *sbp&0xff; 45032377Sminshall if (c == 0) { 45132385Sminshall sbp++, scc--; count++; 45232377Sminshall /* a "true" CR */ 45332377Sminshall TTYADD('\r'); 45432377Sminshall } else if (!hisopts[TELOPT_ECHO] && 45532377Sminshall (c == '\n')) { 45632385Sminshall sbp++, scc--; count++; 45732377Sminshall TTYADD('\n'); 45832377Sminshall } else { 45932377Sminshall TTYADD('\r'); 46032377Sminshall if (crmod) { 46132377Sminshall TTYADD('\n'); 46232377Sminshall } 46332377Sminshall } 46432377Sminshall } else { 46532377Sminshall telrcv_state = TS_CR; 46632377Sminshall TTYADD('\r'); 46732377Sminshall if (crmod) { 46832377Sminshall TTYADD('\n'); 46932377Sminshall } 47032377Sminshall } 47132377Sminshall } else { 47232377Sminshall TTYADD(c); 47332377Sminshall } 47432377Sminshall continue; 47527088Sminshall 47632377Sminshall case TS_IAC: 47732377Sminshall switch (c) { 47832377Sminshall 47932377Sminshall case WILL: 48032377Sminshall telrcv_state = TS_WILL; 48132377Sminshall continue; 48227261Sminshall 48332377Sminshall case WONT: 48432377Sminshall telrcv_state = TS_WONT; 48532377Sminshall continue; 48627261Sminshall 48732377Sminshall case DO: 48832377Sminshall telrcv_state = TS_DO; 48932377Sminshall continue; 49027261Sminshall 49132377Sminshall case DONT: 49232377Sminshall telrcv_state = TS_DONT; 49332377Sminshall continue; 49427261Sminshall 49532377Sminshall case DM: 49632377Sminshall /* 49732377Sminshall * We may have missed an urgent notification, 49832377Sminshall * so make sure we flush whatever is in the 49932377Sminshall * buffer currently. 50032377Sminshall */ 50132377Sminshall SYNCHing = 1; 50232377Sminshall ttyflush(1); 50332554Sminshall SYNCHing = stilloob(); 50432377Sminshall settimer(gotDM); 50532377Sminshall break; 50627088Sminshall 50732377Sminshall case NOP: 50832377Sminshall case GA: 50932377Sminshall break; 51027088Sminshall 51132377Sminshall case SB: 51232377Sminshall SB_CLEAR(); 51332377Sminshall telrcv_state = TS_SB; 51432377Sminshall continue; 51527261Sminshall 51632377Sminshall # if defined(TN3270) 51732377Sminshall case EOR: 51832377Sminshall if (In3270) { 51932377Sminshall Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 52032377Sminshall if (Ibackp == Ifrontp) { 52132377Sminshall Ibackp = Ifrontp = Ibuf; 52232377Sminshall ISend = 0; /* should have been! */ 52332377Sminshall } else { 52432377Sminshall ISend = 1; 52527088Sminshall } 52627088Sminshall } 52727088Sminshall break; 52832377Sminshall # endif /* defined(TN3270) */ 52932377Sminshall 53032377Sminshall case IAC: 53132377Sminshall # if !defined(TN3270) 53232377Sminshall TTYADD(IAC); 53332377Sminshall # else /* !defined(TN3270) */ 53432377Sminshall if (In3270) { 53532377Sminshall *Ifrontp++ = IAC; 53632377Sminshall } else { 53732377Sminshall TTYADD(IAC); 53832377Sminshall } 53932377Sminshall # endif /* !defined(TN3270) */ 54027088Sminshall break; 54132377Sminshall 54227088Sminshall default: 54327088Sminshall break; 54427088Sminshall } 54532377Sminshall telrcv_state = TS_DATA; 54632377Sminshall continue; 54727088Sminshall 54832377Sminshall case TS_WILL: 54932377Sminshall printoption(">RCVD", will, c, !hisopts[c]); 55032377Sminshall if (c == TELOPT_TM) { 55132377Sminshall if (flushout) { 55232377Sminshall flushout = 0; 55332377Sminshall } 55432377Sminshall } else if (!hisopts[c]) { 55532377Sminshall willoption(c, 1); 55632377Sminshall } 55732377Sminshall SetIn3270(); 55832377Sminshall telrcv_state = TS_DATA; 55932377Sminshall continue; 56027110Sminshall 56132377Sminshall case TS_WONT: 56232377Sminshall printoption(">RCVD", wont, c, hisopts[c]); 56332377Sminshall if (c == TELOPT_TM) { 56432377Sminshall if (flushout) { 56532377Sminshall flushout = 0; 56632377Sminshall } 56732377Sminshall } else if (hisopts[c]) { 56832377Sminshall wontoption(c, 1); 56932377Sminshall } 57032377Sminshall SetIn3270(); 57132377Sminshall telrcv_state = TS_DATA; 57232377Sminshall continue; 57327088Sminshall 57432377Sminshall case TS_DO: 57532377Sminshall printoption(">RCVD", doopt, c, !myopts[c]); 57632377Sminshall if (!myopts[c]) 57732377Sminshall dooption(c); 57832377Sminshall SetIn3270(); 57932377Sminshall telrcv_state = TS_DATA; 58032377Sminshall continue; 58127088Sminshall 58232377Sminshall case TS_DONT: 58332377Sminshall printoption(">RCVD", dont, c, myopts[c]); 58432377Sminshall if (myopts[c]) { 58532377Sminshall myopts[c] = 0; 58632554Sminshall printring(&netoring, wont, c); 58732377Sminshall flushline = 1; 58832377Sminshall setconnmode(); /* set new tty mode (maybe) */ 58932377Sminshall printoption(">SENT", wont, c, 0); 59032377Sminshall } 59132377Sminshall SetIn3270(); 59232377Sminshall telrcv_state = TS_DATA; 59332377Sminshall continue; 59427088Sminshall 59532377Sminshall case TS_SB: 59632377Sminshall if (c == IAC) { 59732377Sminshall telrcv_state = TS_SE; 59832377Sminshall } else { 59932377Sminshall SB_ACCUM(c); 60032377Sminshall } 60132377Sminshall continue; 60227088Sminshall 60332377Sminshall case TS_SE: 60432377Sminshall if (c != SE) { 60532377Sminshall if (c != IAC) { 60632377Sminshall SB_ACCUM(IAC); 60732377Sminshall } 60832377Sminshall SB_ACCUM(c); 60932377Sminshall telrcv_state = TS_SB; 61032377Sminshall } else { 61132377Sminshall SB_TERM(); 61232377Sminshall suboption(); /* handle sub-option */ 61332377Sminshall SetIn3270(); 61432377Sminshall telrcv_state = TS_DATA; 61532377Sminshall } 61627088Sminshall } 61727088Sminshall } 61832667Sminshall if (count) 61932667Sminshall ring_consumed(&netiring, count); 62032385Sminshall return returnValue||count; 62127088Sminshall } 62232385Sminshall 62332385Sminshall static int 62432554Sminshall telsnd() 62532385Sminshall { 62632385Sminshall int tcc; 62732385Sminshall int count; 62832385Sminshall int returnValue = 0; 62932385Sminshall char *tbp; 63032385Sminshall 63132385Sminshall tcc = 0; 63232385Sminshall count = 0; 63332385Sminshall while (NETROOM() > 2) { 63432385Sminshall register int sc; 63532385Sminshall register int c; 63632385Sminshall 63732385Sminshall if (tcc == 0) { 63832385Sminshall if (count) { 63932528Sminshall ring_consumed(&ttyiring, count); 64032385Sminshall returnValue = 1; 64132385Sminshall count = 0; 64232385Sminshall } 64332528Sminshall tbp = ttyiring.consume; 64432528Sminshall tcc = ring_full_consecutive(&ttyiring); 64532385Sminshall if (tcc == 0) { 64632385Sminshall break; 64732385Sminshall } 64832385Sminshall } 64932385Sminshall c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; 65032385Sminshall if (sc == escape) { 65132385Sminshall command(0); 65232385Sminshall tcc = 0; 65332385Sminshall flushline = 1; 65432385Sminshall break; 65532385Sminshall } else if (MODE_LINE(globalmode) && (sc == echoc)) { 65632385Sminshall if (tcc > 0 && strip(*tbp) == echoc) { 65732385Sminshall tcc--; tbp++; count++; 65832385Sminshall } else { 65932385Sminshall dontlecho = !dontlecho; 66032385Sminshall settimer(echotoggle); 66132385Sminshall setconnmode(); 66232385Sminshall flushline = 1; 66332385Sminshall break; 66432385Sminshall } 66532385Sminshall } 66632385Sminshall if (localchars) { 66732385Sminshall if (TerminalSpecialChars(sc) == 0) { 66832385Sminshall break; 66932385Sminshall } 67032385Sminshall } 67132385Sminshall if (!myopts[TELOPT_BINARY]) { 67232385Sminshall switch (c) { 67332385Sminshall case '\n': 67432385Sminshall /* 67532385Sminshall * If we are in CRMOD mode (\r ==> \n) 67632385Sminshall * on our local machine, then probably 67732385Sminshall * a newline (unix) is CRLF (TELNET). 67832385Sminshall */ 67932385Sminshall if (MODE_LOCAL_CHARS(globalmode)) { 68032385Sminshall NETADD('\r'); 68132385Sminshall } 68232385Sminshall NETADD('\n'); 68332385Sminshall flushline = 1; 68432385Sminshall break; 68532385Sminshall case '\r': 68632385Sminshall if (!crlf) { 68732385Sminshall NET2ADD('\r', '\0'); 68832385Sminshall } else { 68932385Sminshall NET2ADD('\r', '\n'); 69032385Sminshall } 69132385Sminshall flushline = 1; 69232385Sminshall break; 69332385Sminshall case IAC: 69432385Sminshall NET2ADD(IAC, IAC); 69532385Sminshall break; 69632385Sminshall default: 69732385Sminshall NETADD(c); 69832385Sminshall break; 69932385Sminshall } 70032385Sminshall } else if (c == IAC) { 70132385Sminshall NET2ADD(IAC, IAC); 70232385Sminshall } else { 70332385Sminshall NETADD(c); 70432385Sminshall } 70532385Sminshall } 70632667Sminshall if (count) 70732667Sminshall ring_consumed(&ttyiring, count); 70832385Sminshall return returnValue||count; /* Non-zero if we did anything */ 70932385Sminshall } 71032377Sminshall 71127088Sminshall /* 71232377Sminshall * Scheduler() 71332377Sminshall * 71432377Sminshall * Try to do something. 71532377Sminshall * 71632377Sminshall * If we do something useful, return 1; else return 0. 71732377Sminshall * 71827110Sminshall */ 71927110Sminshall 72027110Sminshall 72132377Sminshall int 72232377Sminshall Scheduler(block) 72332377Sminshall int block; /* should we block in the select ? */ 72427110Sminshall { 72532377Sminshall /* One wants to be a bit careful about setting returnValue 72632377Sminshall * to one, since a one implies we did some useful work, 72732377Sminshall * and therefore probably won't be called to block next 72832377Sminshall * time (TN3270 mode only). 72932377Sminshall */ 73032531Sminshall int returnValue; 73132531Sminshall int netin, netout, netex, ttyin, ttyout; 73227110Sminshall 73332531Sminshall /* Decide which rings should be processed */ 73432531Sminshall 73532531Sminshall netout = ring_full_count(&netoring) && 73632531Sminshall (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]); 73732531Sminshall ttyout = ring_full_count(&ttyoring); 73832531Sminshall 73932377Sminshall #if defined(TN3270) 74032531Sminshall ttyin = ring_empty_count(&ttyiring) && (shell_active == 0); 74132377Sminshall #else /* defined(TN3270) */ 74232531Sminshall ttyin = ring_empty_count(&ttyiring); 74332377Sminshall #endif /* defined(TN3270) */ 74432531Sminshall 74532531Sminshall #if defined(TN3270) 74632531Sminshall netin = ring_empty_count(&netiring); 74732377Sminshall # else /* !defined(TN3270) */ 74832531Sminshall netin = !ISend && ring_empty_count(&netiring); 74932377Sminshall # endif /* !defined(TN3270) */ 75032531Sminshall 75132531Sminshall netex = !SYNCHing; 75232531Sminshall 75332531Sminshall /* If we have seen a signal recently, reset things */ 75432377Sminshall # if defined(TN3270) && defined(unix) 75532377Sminshall if (HaveInput) { 75632377Sminshall HaveInput = 0; 75732377Sminshall signal(SIGIO, inputAvailable); 75832377Sminshall } 75932377Sminshall #endif /* defined(TN3270) && defined(unix) */ 76032377Sminshall 76132531Sminshall /* Call to system code to process rings */ 76227178Sminshall 76332531Sminshall returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); 76427178Sminshall 76532531Sminshall /* Now, look at the input rings, looking for work to do. */ 76632377Sminshall 76732531Sminshall if (ring_full_count(&ttyiring)) { 76832377Sminshall # if defined(TN3270) 76932377Sminshall if (In3270) { 770*34848Sminshall int c; 771*34848Sminshall 77233804Sminshall c = DataFromTerminal(ttyiring.consume, 77332528Sminshall ring_full_consecutive(&ttyiring)); 77432377Sminshall if (c) { 77532377Sminshall returnValue = 1; 77632667Sminshall ring_consumed(&ttyiring, c); 77732377Sminshall } 77832377Sminshall } else { 77932377Sminshall # endif /* defined(TN3270) */ 78032554Sminshall returnValue |= telsnd(); 78132377Sminshall # if defined(TN3270) 78227178Sminshall } 78332531Sminshall # endif /* defined(TN3270) */ 78427178Sminshall } 78532377Sminshall 78632528Sminshall if (ring_full_count(&netiring)) { 78732377Sminshall # if !defined(TN3270) 78832385Sminshall returnValue |= telrcv(); 78932377Sminshall # else /* !defined(TN3270) */ 79032377Sminshall returnValue = Push3270(); 79132377Sminshall # endif /* !defined(TN3270) */ 79232377Sminshall } 79332377Sminshall return returnValue; 79427178Sminshall } 79527178Sminshall 79627178Sminshall /* 79732377Sminshall * Select from tty and network... 79827088Sminshall */ 79932377Sminshall void 80032377Sminshall telnet() 80127088Sminshall { 80232531Sminshall sys_telnet_init(); 80327088Sminshall 80432377Sminshall # if !defined(TN3270) 80532377Sminshall if (telnetport) { 80632377Sminshall if (!hisopts[TELOPT_SGA]) { 80732377Sminshall willoption(TELOPT_SGA, 0); 80827110Sminshall } 80932377Sminshall if (!myopts[TELOPT_TTYPE]) { 810*34848Sminshall dooption(TELOPT_TTYPE); 81132377Sminshall } 81227178Sminshall } 81332377Sminshall # endif /* !defined(TN3270) */ 81427088Sminshall 81532377Sminshall # if !defined(TN3270) 81632377Sminshall for (;;) { 81732385Sminshall int schedValue; 81832385Sminshall 81932385Sminshall while ((schedValue = Scheduler(0)) != 0) { 82032385Sminshall if (schedValue == -1) { 82132385Sminshall setcommandmode(); 82232385Sminshall return; 82332385Sminshall } 82432385Sminshall } 82532385Sminshall 82632531Sminshall if (Scheduler(1) == -1) { 82732377Sminshall setcommandmode(); 82832377Sminshall return; 82932377Sminshall } 83032377Sminshall } 83132377Sminshall # else /* !defined(TN3270) */ 83232377Sminshall for (;;) { 83332377Sminshall int schedValue; 83427088Sminshall 83532377Sminshall while (!In3270 && !shell_active) { 83632531Sminshall if (Scheduler(1) == -1) { 83732377Sminshall setcommandmode(); 83832377Sminshall return; 83932377Sminshall } 84027088Sminshall } 84132377Sminshall 84232377Sminshall while ((schedValue = Scheduler(0)) != 0) { 84332377Sminshall if (schedValue == -1) { 84432377Sminshall setcommandmode(); 84532377Sminshall return; 84632377Sminshall } 84727088Sminshall } 84832377Sminshall /* If there is data waiting to go out to terminal, don't 84932377Sminshall * schedule any more data for the terminal. 85032377Sminshall */ 85134304Sminshall if (ring_full_count(&ttyoring)) { 85232377Sminshall schedValue = 1; 85327088Sminshall } else { 85432377Sminshall if (shell_active) { 85532377Sminshall if (shell_continue() == 0) { 85632377Sminshall ConnectScreen(); 85727088Sminshall } 85832377Sminshall } else if (In3270) { 85932377Sminshall schedValue = DoTerminalOutput(); 86032377Sminshall } 86127088Sminshall } 86232377Sminshall if (schedValue && (shell_active == 0)) { 86332531Sminshall if (Scheduler(1) == -1) { 86432377Sminshall setcommandmode(); 86532377Sminshall return; 86632377Sminshall } 86727088Sminshall } 86832377Sminshall } 86932377Sminshall # endif /* !defined(TN3270) */ 87027088Sminshall } 87132377Sminshall 872*34848Sminshall #if 0 /* XXX - this not being in is a bug */ 87327088Sminshall /* 87432554Sminshall * nextitem() 87532554Sminshall * 87632554Sminshall * Return the address of the next "item" in the TELNET data 87732554Sminshall * stream. This will be the address of the next character if 87832554Sminshall * the current address is a user data character, or it will 87932554Sminshall * be the address of the character following the TELNET command 88032554Sminshall * if the current address is a TELNET IAC ("I Am a Command") 88132554Sminshall * character. 88232554Sminshall */ 88332554Sminshall 88432554Sminshall static char * 88532554Sminshall nextitem(current) 88632554Sminshall char *current; 88732554Sminshall { 88832554Sminshall if ((*current&0xff) != IAC) { 88932554Sminshall return current+1; 89032554Sminshall } 89132554Sminshall switch (*(current+1)&0xff) { 89232554Sminshall case DO: 89332554Sminshall case DONT: 89432554Sminshall case WILL: 89532554Sminshall case WONT: 89632554Sminshall return current+3; 89732554Sminshall case SB: /* loop forever looking for the SE */ 89832554Sminshall { 89932554Sminshall register char *look = current+2; 90032554Sminshall 90132554Sminshall for (;;) { 90232554Sminshall if ((*look++&0xff) == IAC) { 90332554Sminshall if ((*look++&0xff) == SE) { 90432554Sminshall return look; 90532554Sminshall } 90632554Sminshall } 90732554Sminshall } 90832554Sminshall } 90932554Sminshall default: 91032554Sminshall return current+2; 91132554Sminshall } 91232554Sminshall } 913*34848Sminshall #endif /* 0 */ 91432554Sminshall 91532554Sminshall /* 91632554Sminshall * netclear() 91732554Sminshall * 91832554Sminshall * We are about to do a TELNET SYNCH operation. Clear 91932554Sminshall * the path to the network. 92032554Sminshall * 92132554Sminshall * Things are a bit tricky since we may have sent the first 92232554Sminshall * byte or so of a previous TELNET command into the network. 92332554Sminshall * So, we have to scan the network buffer from the beginning 92432554Sminshall * until we are up to where we want to be. 92532554Sminshall * 92632554Sminshall * A side effect of what we do, just to keep things 92732554Sminshall * simple, is to clear the urgent data pointer. The principal 92832554Sminshall * caller should be setting the urgent data pointer AFTER calling 92932554Sminshall * us in any case. 93032554Sminshall */ 93132554Sminshall 93232554Sminshall static void 93332554Sminshall netclear() 93432554Sminshall { 93532554Sminshall #if 0 /* XXX */ 93632554Sminshall register char *thisitem, *next; 93732554Sminshall char *good; 93832554Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 93932554Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 94032554Sminshall 94132554Sminshall thisitem = netobuf; 94232554Sminshall 94332554Sminshall while ((next = nextitem(thisitem)) <= netobuf.send) { 94432554Sminshall thisitem = next; 94532554Sminshall } 94632554Sminshall 94732554Sminshall /* Now, thisitem is first before/at boundary. */ 94832554Sminshall 94932554Sminshall good = netobuf; /* where the good bytes go */ 95032554Sminshall 95132554Sminshall while (netoring.add > thisitem) { 95232554Sminshall if (wewant(thisitem)) { 95332554Sminshall int length; 95432554Sminshall 95532554Sminshall next = thisitem; 95632554Sminshall do { 95732554Sminshall next = nextitem(next); 95832554Sminshall } while (wewant(next) && (nfrontp > next)); 95932554Sminshall length = next-thisitem; 96032554Sminshall memcpy(good, thisitem, length); 96132554Sminshall good += length; 96232554Sminshall thisitem = next; 96332554Sminshall } else { 96432554Sminshall thisitem = nextitem(thisitem); 96532554Sminshall } 96632554Sminshall } 96732554Sminshall 96832554Sminshall #endif /* 0 */ 96932554Sminshall } 97032554Sminshall 97132554Sminshall /* 97232377Sminshall * These routines add various telnet commands to the data stream. 97327088Sminshall */ 97432377Sminshall 97532554Sminshall static void 97632554Sminshall doflush() 97732554Sminshall { 97832554Sminshall NET2ADD(IAC, DO); 97932554Sminshall NETADD(TELOPT_TM); 98032554Sminshall flushline = 1; 98132554Sminshall flushout = 1; 98232554Sminshall ttyflush(1); /* Flush/drop output */ 98332554Sminshall /* do printoption AFTER flush, otherwise the output gets tossed... */ 98432554Sminshall printoption("<SENT", doopt, TELOPT_TM, 0); 98532554Sminshall } 98632554Sminshall 98732377Sminshall void 98832377Sminshall xmitAO() 98927088Sminshall { 99032377Sminshall NET2ADD(IAC, AO); 99132377Sminshall if (autoflush) { 99232377Sminshall doflush(); 99332377Sminshall } 99432377Sminshall } 99527088Sminshall 99632377Sminshall 99732377Sminshall void 99832377Sminshall xmitEL() 99927088Sminshall { 100032377Sminshall NET2ADD(IAC, EL); 100127088Sminshall } 100227088Sminshall 100332377Sminshall void 100432377Sminshall xmitEC() 100527088Sminshall { 100632377Sminshall NET2ADD(IAC, EC); 100727088Sminshall } 100827088Sminshall 100932377Sminshall 101032377Sminshall #if defined(NOT43) 101132377Sminshall int 101232377Sminshall #else /* defined(NOT43) */ 101332377Sminshall void 101432377Sminshall #endif /* defined(NOT43) */ 101532377Sminshall dosynch() 101627088Sminshall { 101732377Sminshall netclear(); /* clear the path to the network */ 101833294Sminshall NETADD(IAC); 101933294Sminshall setneturg(); 102033294Sminshall NETADD(DM); 102127088Sminshall 102232377Sminshall #if defined(NOT43) 102332377Sminshall return 0; 102432377Sminshall #endif /* defined(NOT43) */ 102527088Sminshall } 102627088Sminshall 102732377Sminshall void 102832377Sminshall intp() 102927088Sminshall { 103032377Sminshall NET2ADD(IAC, IP); 103132377Sminshall flushline = 1; 103232377Sminshall if (autoflush) { 103332377Sminshall doflush(); 103432377Sminshall } 103532377Sminshall if (autosynch) { 103632377Sminshall dosynch(); 103732377Sminshall } 103827088Sminshall } 103927186Sminshall 104032377Sminshall void 104132377Sminshall sendbrk() 104227186Sminshall { 104332377Sminshall NET2ADD(IAC, BREAK); 104432377Sminshall flushline = 1; 104532377Sminshall if (autoflush) { 104632377Sminshall doflush(); 104732377Sminshall } 104832377Sminshall if (autosynch) { 104932377Sminshall dosynch(); 105032377Sminshall } 105127186Sminshall } 1052