133685Sbostic /* 233685Sbostic * Copyright (c) 1988 Regents of the University of California. 333685Sbostic * All rights reserved. 433685Sbostic * 533685Sbostic * Redistribution and use in source and binary forms are permitted 634898Sbostic * provided that the above copyright notice and this paragraph are 734898Sbostic * duplicated in all such forms and that any documentation, 834898Sbostic * advertising materials, and other materials related to such 934898Sbostic * distribution and use acknowledge that the software was developed 1034898Sbostic * by the University of California, Berkeley. The name of the 1134898Sbostic * University may not be used to endorse or promote products derived 1234898Sbostic * from this software without specific prior written permission. 1334898Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434898Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534898Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1633685Sbostic */ 1711758Ssam 1821580Sdist #ifndef lint 19*37219Sminshall static char sccsid[] = "@(#)telnet.c 5.39 (Berkeley) 03/20/89"; 2033685Sbostic #endif /* not lint */ 2121580Sdist 229217Ssam #include <sys/types.h> 239217Ssam 2432377Sminshall #if defined(unix) 2533804Sminshall #include <signal.h> 2632377Sminshall /* By the way, we need to include curses.h before telnet.h since, 2732377Sminshall * among other things, telnet.h #defines 'DO', which is a variable 2832377Sminshall * declared in curses.h. 2932377Sminshall */ 3032377Sminshall #include <curses.h> 3132377Sminshall #endif /* defined(unix) */ 3232377Sminshall 3312212Ssam #include <arpa/telnet.h> 3432377Sminshall 3532377Sminshall #if defined(unix) 3627186Sminshall #include <strings.h> 3732377Sminshall #else /* defined(unix) */ 3832377Sminshall #include <string.h> 3932377Sminshall #endif /* defined(unix) */ 409217Ssam 4132381Sminshall #include "ring.h" 4232381Sminshall 4332377Sminshall #include "defines.h" 4432377Sminshall #include "externs.h" 4532377Sminshall #include "types.h" 4632377Sminshall #include "general.h" 4727178Sminshall 4827178Sminshall 4927228Sminshall #define strip(x) ((x)&0x7f) 506000Sroot 5127088Sminshall 5232377Sminshall static char subbuffer[SUBBUFSIZE], 5332377Sminshall *subpointer, *subend; /* buffer for sub-options */ 5427676Sminshall #define SB_CLEAR() subpointer = subbuffer; 5527676Sminshall #define SB_TERM() subend = subpointer; 5627676Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 5727676Sminshall *subpointer++ = (c); \ 5827676Sminshall } 5927676Sminshall 606000Sroot char hisopts[256]; 616000Sroot char myopts[256]; 626000Sroot 636000Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 646000Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 656000Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 666000Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 676000Sroot 6832377Sminshall int 6932377Sminshall connected, 7032377Sminshall showoptions, 7132377Sminshall In3270, /* Are we in 3270 mode? */ 7232377Sminshall ISend, /* trying to send network data in */ 7332377Sminshall debug = 0, 7432377Sminshall crmod, 7532377Sminshall netdata, /* Print out network data flow */ 7632377Sminshall crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ 7734848Sminshall #if defined(TN3270) 7836241Sminshall noasynchtty = 0,/* User specified "-noasynch" on command line */ 7936241Sminshall noasynchnet = 0,/* User specified "-noasynch" on command line */ 8032377Sminshall askedSGA = 0, /* We have talked about suppress go ahead */ 8134848Sminshall #endif /* defined(TN3270) */ 8233286Sminshall telnetport, 8332531Sminshall SYNCHing, /* we are in TELNET SYNCH mode */ 8432531Sminshall flushout, /* flush output */ 8532531Sminshall autoflush = 0, /* flush output when interrupting? */ 8632531Sminshall autosynch, /* send interrupt characters with SYNCH? */ 87*37219Sminshall localflow, /* we handle flow control locally */ 8832531Sminshall localchars, /* we recognize interrupt/quit */ 8932531Sminshall donelclchars, /* the user has set "localchars" */ 9032531Sminshall donebinarytoggle, /* the user has put us in binary */ 9132531Sminshall dontlecho, /* do we suppress local echoing right now? */ 9232531Sminshall globalmode; 9327088Sminshall 9432377Sminshall #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 956000Sroot 9632377Sminshall char 9732377Sminshall *prompt = 0, 9832377Sminshall escape, 9932377Sminshall echoc; 10027186Sminshall 10127186Sminshall /* 1026000Sroot * Telnet receiver states for fsm 1036000Sroot */ 1046000Sroot #define TS_DATA 0 1056000Sroot #define TS_IAC 1 1066000Sroot #define TS_WILL 2 1076000Sroot #define TS_WONT 3 1086000Sroot #define TS_DO 4 1096000Sroot #define TS_DONT 5 11027021Sminshall #define TS_CR 6 11127676Sminshall #define TS_SB 7 /* sub-option collection */ 11227676Sminshall #define TS_SE 8 /* looking for sub-option end */ 1136000Sroot 11432377Sminshall static int telrcv_state; 1156000Sroot 11632377Sminshall jmp_buf toplevel = { 0 }; 11732377Sminshall jmp_buf peerdied; 1186000Sroot 11932377Sminshall int flushline; 12027021Sminshall 12132377Sminshall /* 12232377Sminshall * The following are some clocks used to decide how to interpret 12332377Sminshall * the relationship between various variables. 12432377Sminshall */ 1256000Sroot 12632377Sminshall Clocks clocks; 12732377Sminshall 12832377Sminshall Modelist modelist[] = { 12932377Sminshall { "telnet command mode", COMMAND_LINE }, 13032377Sminshall { "character-at-a-time mode", 0 }, 13132377Sminshall { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 13232377Sminshall { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 13332377Sminshall { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 13432377Sminshall { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 13532377Sminshall { "3270 mode", 0 }, 13632377Sminshall }; 1376000Sroot 13832377Sminshall 13932377Sminshall /* 14032377Sminshall * Initialize telnet environment. 14132377Sminshall */ 1426000Sroot 14332377Sminshall init_telnet() 14432377Sminshall { 14532377Sminshall SB_CLEAR(); 14632377Sminshall ClearArray(hisopts); 14732377Sminshall ClearArray(myopts); 1486000Sroot 149*37219Sminshall connected = In3270 = ISend = localflow = donebinarytoggle = 0; 1506000Sroot 15132377Sminshall SYNCHing = 0; 1526000Sroot 15332377Sminshall /* Don't change NetTrace */ 1546000Sroot 15532377Sminshall escape = CONTROL(']'); 15632377Sminshall echoc = CONTROL('E'); 1576000Sroot 15832377Sminshall flushline = 1; 15932377Sminshall telrcv_state = TS_DATA; 16032377Sminshall } 16132554Sminshall 1626000Sroot 16332554Sminshall #include <varargs.h> 1646000Sroot 16534848Sminshall /*VARARGS*/ 16632554Sminshall static void 16732554Sminshall printring(va_alist) 16832554Sminshall va_dcl 16932554Sminshall { 17032554Sminshall va_list ap; 17132554Sminshall char buffer[100]; /* where things go */ 17232554Sminshall char *ptr; 17332554Sminshall char *format; 17432554Sminshall char *string; 17532554Sminshall Ring *ring; 17632554Sminshall int i; 17732554Sminshall 17832554Sminshall va_start(ap); 17932554Sminshall 18032554Sminshall ring = va_arg(ap, Ring *); 18132554Sminshall format = va_arg(ap, char *); 18232554Sminshall ptr = buffer; 18332554Sminshall 18432554Sminshall while ((i = *format++) != 0) { 18532554Sminshall if (i == '%') { 18632554Sminshall i = *format++; 18732554Sminshall switch (i) { 18832554Sminshall case 'c': 18932554Sminshall *ptr++ = va_arg(ap, int); 19032554Sminshall break; 19132554Sminshall case 's': 19232554Sminshall string = va_arg(ap, char *); 19332554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 19432554Sminshall ring_supply_data(ring, string, strlen(string)); 19532554Sminshall ptr = buffer; 19632554Sminshall break; 19732554Sminshall case 0: 19832554Sminshall ExitString("printring: trailing %%.\n", 1); 19932554Sminshall /*NOTREACHED*/ 20032554Sminshall default: 20132554Sminshall ExitString("printring: unknown format character.\n", 1); 20232554Sminshall /*NOTREACHED*/ 20332554Sminshall } 20432554Sminshall } else { 20532554Sminshall *ptr++ = i; 20632554Sminshall } 20732554Sminshall } 20832554Sminshall ring_supply_data(ring, buffer, ptr-buffer); 20932554Sminshall } 21032554Sminshall 21132554Sminshall 21232377Sminshall void 21327676Sminshall willoption(option, reply) 21427676Sminshall int option, reply; 2156000Sroot { 2166000Sroot char *fmt; 2176000Sroot 2186000Sroot switch (option) { 2196000Sroot 2206000Sroot case TELOPT_ECHO: 22132377Sminshall # if defined(TN3270) 22232377Sminshall /* 22332377Sminshall * The following is a pain in the rear-end. 22432377Sminshall * Various IBM servers (some versions of Wiscnet, 22532377Sminshall * possibly Fibronics/Spartacus, and who knows who 22632377Sminshall * else) will NOT allow us to send "DO SGA" too early 22732377Sminshall * in the setup proceedings. On the other hand, 22832377Sminshall * 4.2 servers (telnetd) won't set SGA correctly. 22932377Sminshall * So, we are stuck. Empirically (but, based on 23032377Sminshall * a VERY small sample), the IBM servers don't send 23132377Sminshall * out anything about ECHO, so we postpone our sending 23232377Sminshall * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 23332377Sminshall * DO send). 23432377Sminshall */ 23532377Sminshall { 23632377Sminshall if (askedSGA == 0) { 23732377Sminshall askedSGA = 1; 23832377Sminshall if (!hisopts[TELOPT_SGA]) { 23932377Sminshall willoption(TELOPT_SGA, 0); 24032377Sminshall } 24132377Sminshall } 24232377Sminshall } 24332377Sminshall /* Fall through */ 24432377Sminshall case TELOPT_EOR: 24532377Sminshall case TELOPT_BINARY: 24632377Sminshall #endif /* defined(TN3270) */ 2476000Sroot case TELOPT_SGA: 24827110Sminshall settimer(modenegotiated); 2496000Sroot hisopts[option] = 1; 2506000Sroot fmt = doopt; 25127110Sminshall setconnmode(); /* possibly set new tty mode */ 2526000Sroot break; 2536000Sroot 2546000Sroot case TELOPT_TM: 25527110Sminshall return; /* Never reply to TM will's/wont's */ 2566000Sroot 2576000Sroot default: 2586000Sroot fmt = dont; 2596000Sroot break; 2606000Sroot } 26132554Sminshall printring(&netoring, fmt, option); 26227676Sminshall if (reply) 26332377Sminshall printoption(">SENT", fmt, option, reply); 26427676Sminshall else 26532377Sminshall printoption("<SENT", fmt, option, reply); 2666000Sroot } 2676000Sroot 26832377Sminshall void 26927676Sminshall wontoption(option, reply) 27027676Sminshall int option, reply; 2716000Sroot { 2726000Sroot char *fmt; 2736000Sroot 2746000Sroot switch (option) { 2756000Sroot 2766000Sroot case TELOPT_ECHO: 2776000Sroot case TELOPT_SGA: 27827110Sminshall settimer(modenegotiated); 2796000Sroot hisopts[option] = 0; 2806000Sroot fmt = dont; 28127110Sminshall setconnmode(); /* Set new tty mode */ 2826000Sroot break; 2836000Sroot 28427110Sminshall case TELOPT_TM: 28527110Sminshall return; /* Never reply to TM will's/wont's */ 28627110Sminshall 2876000Sroot default: 28836466Sminshall hisopts[option] = 0; 2896000Sroot fmt = dont; 2906000Sroot } 29132554Sminshall printring(&netoring, fmt, option); 29227676Sminshall if (reply) 29332377Sminshall printoption(">SENT", fmt, option, reply); 29427676Sminshall else 29532377Sminshall printoption("<SENT", fmt, option, reply); 2966000Sroot } 2976000Sroot 29832377Sminshall static void 2996000Sroot dooption(option) 3006000Sroot int option; 3016000Sroot { 3026000Sroot char *fmt; 3036000Sroot 3046000Sroot switch (option) { 3056000Sroot 3066000Sroot case TELOPT_TM: 30713231Ssam fmt = will; 30813231Ssam break; 30913231Ssam 31032377Sminshall # if defined(TN3270) 311*37219Sminshall case TELOPT_EOR: /* end of record */ 312*37219Sminshall case TELOPT_BINARY: /* binary mode */ 31332377Sminshall # endif /* defined(TN3270) */ 314*37219Sminshall case TELOPT_NAWS: /* window size */ 315*37219Sminshall case TELOPT_TSPEED: /* terminal speed */ 316*37219Sminshall case TELOPT_LFLOW: /* local flow control */ 31727676Sminshall case TELOPT_TTYPE: /* terminal type option */ 31827110Sminshall case TELOPT_SGA: /* no big deal */ 3196000Sroot fmt = will; 32027110Sminshall myopts[option] = 1; 3216000Sroot break; 3226000Sroot 32327110Sminshall case TELOPT_ECHO: /* We're never going to echo... */ 3246000Sroot default: 3256000Sroot fmt = wont; 3266000Sroot break; 3276000Sroot } 32832554Sminshall printring(&netoring, fmt, option); 32932377Sminshall printoption(">SENT", fmt, option, 0); 3306000Sroot } 33127676Sminshall 33227676Sminshall /* 33327676Sminshall * suboption() 33427676Sminshall * 33527676Sminshall * Look at the sub-option buffer, and try to be helpful to the other 33627676Sminshall * side. 33727676Sminshall * 33827676Sminshall * Currently we recognize: 33927676Sminshall * 34027676Sminshall * Terminal type, send request. 341*37219Sminshall * Terminal speed (send request). 342*37219Sminshall * Local flow control (is request). 34327676Sminshall */ 34427676Sminshall 34532377Sminshall static void 34627676Sminshall suboption() 34727676Sminshall { 34832377Sminshall printsub("<", subbuffer, subend-subbuffer+1); 34927676Sminshall switch (subbuffer[0]&0xff) { 35027676Sminshall case TELOPT_TTYPE: 35127676Sminshall if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 35227676Sminshall ; 35327676Sminshall } else { 35427676Sminshall char *name; 35527676Sminshall char namebuf[41]; 35632377Sminshall extern char *getenv(); 35727676Sminshall int len; 35827676Sminshall 35932377Sminshall #if defined(TN3270) 36032531Sminshall if (tn3270_ttype()) { 36132377Sminshall return; 36232377Sminshall } 36332377Sminshall #endif /* defined(TN3270) */ 36427676Sminshall name = getenv("TERM"); 36527676Sminshall if ((name == 0) || ((len = strlen(name)) > 40)) { 36627676Sminshall name = "UNKNOWN"; 36733492Sminshall len = strlen(name); 36827676Sminshall } 36927676Sminshall if ((len + 4+2) < NETROOM()) { 37027676Sminshall strcpy(namebuf, name); 37127676Sminshall upcase(namebuf); 37232554Sminshall printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 37327676Sminshall TELQUAL_IS, namebuf, IAC, SE); 37432381Sminshall /* XXX */ 37532381Sminshall /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ 37632377Sminshall } else { 37732381Sminshall ExitString("No room in buffer for terminal type.\n", 37832377Sminshall 1); 37932377Sminshall /*NOTREACHED*/ 38027676Sminshall } 38127676Sminshall } 382*37219Sminshall break; 383*37219Sminshall case TELOPT_TSPEED: 384*37219Sminshall if ((subbuffer[1]&0xff) == TELQUAL_SEND) { 385*37219Sminshall long *ospeed,*ispeed; 386*37219Sminshall char speedbuf[41]; 387*37219Sminshall char *getenv(); 388*37219Sminshall int len; 38927676Sminshall 390*37219Sminshall TerminalSpeeds(&ispeed, &ospeed); 391*37219Sminshall 392*37219Sminshall sprintf(speedbuf, "%d,%d", ospeed, ispeed); 393*37219Sminshall len = strlen(speedbuf); 394*37219Sminshall 395*37219Sminshall if ((len + 4+2) < NETROOM()) { 396*37219Sminshall printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TSPEED, 397*37219Sminshall TELQUAL_IS, speedbuf, IAC, SE); 398*37219Sminshall } 399*37219Sminshall } 400*37219Sminshall break; 401*37219Sminshall case TELOPT_LFLOW: 402*37219Sminshall if ((subbuffer[1]&0xff) == 1) { 403*37219Sminshall localflow = 1; 404*37219Sminshall } else if ((subbuffer[1]&0xff) == 0) { 405*37219Sminshall localflow = 0; 406*37219Sminshall } 407*37219Sminshall setcommandmode(); 408*37219Sminshall setconnmode(); 409*37219Sminshall break; 41027676Sminshall default: 41127676Sminshall break; 41227676Sminshall } 41327676Sminshall } 41432377Sminshall 41527088Sminshall 41633804Sminshall int 41732377Sminshall telrcv() 41827110Sminshall { 41932377Sminshall register int c; 42032385Sminshall register int scc; 42132385Sminshall register char *sbp; 42232385Sminshall int count; 42332385Sminshall int returnValue = 0; 42427088Sminshall 42532385Sminshall scc = 0; 42632385Sminshall count = 0; 42732385Sminshall while (TTYROOM() > 2) { 42832385Sminshall if (scc == 0) { 42932385Sminshall if (count) { 43032528Sminshall ring_consumed(&netiring, count); 43132385Sminshall returnValue = 1; 43232385Sminshall count = 0; 43332385Sminshall } 43432528Sminshall sbp = netiring.consume; 43532528Sminshall scc = ring_full_consecutive(&netiring); 43632385Sminshall if (scc == 0) { 43732385Sminshall /* No more data coming in */ 43832385Sminshall break; 43932385Sminshall } 44032385Sminshall } 44132385Sminshall 44232385Sminshall c = *sbp++ & 0xff, scc--; count++; 44332385Sminshall 44432377Sminshall switch (telrcv_state) { 44527110Sminshall 44632377Sminshall case TS_CR: 44732377Sminshall telrcv_state = TS_DATA; 44835518Sminshall if (c == '\0') { 44935518Sminshall break; /* Ignore \0 after CR */ 45035518Sminshall } else if ((c == '\n') && (!hisopts[TELOPT_ECHO]) && !crmod) { 45135518Sminshall TTYADD(c); 45235518Sminshall break; 45332377Sminshall } 45435518Sminshall /* Else, fall through */ 45527088Sminshall 45632377Sminshall case TS_DATA: 45732377Sminshall if (c == IAC) { 45832377Sminshall telrcv_state = TS_IAC; 45933804Sminshall break; 46032377Sminshall } 46132377Sminshall # if defined(TN3270) 46232377Sminshall if (In3270) { 46332377Sminshall *Ifrontp++ = c; 46432385Sminshall while (scc > 0) { 46532385Sminshall c = *sbp++ & 0377, scc--; count++; 46632377Sminshall if (c == IAC) { 46732377Sminshall telrcv_state = TS_IAC; 46834304Sminshall break; 46932377Sminshall } 47032377Sminshall *Ifrontp++ = c; 47132377Sminshall } 47232377Sminshall } else 47332377Sminshall # endif /* defined(TN3270) */ 47435518Sminshall /* 47535518Sminshall * The 'crmod' hack (see following) is needed 47635518Sminshall * since we can't * set CRMOD on output only. 47735518Sminshall * Machines like MULTICS like to send \r without 47835518Sminshall * \n; since we must turn off CRMOD to get proper 47935518Sminshall * input, the mapping is done here (sigh). 48035518Sminshall */ 48132377Sminshall if ((c == '\r') && !hisopts[TELOPT_BINARY]) { 48235518Sminshall if (scc > 0) { 48335518Sminshall c = *sbp&0xff; 48435518Sminshall if (c == 0) { 48535518Sminshall sbp++, scc--; count++; 48635518Sminshall /* a "true" CR */ 48732377Sminshall TTYADD('\r'); 48835518Sminshall } else if (!hisopts[TELOPT_ECHO] && 48935518Sminshall (c == '\n')) { 49035518Sminshall sbp++, scc--; count++; 49132377Sminshall TTYADD('\n'); 49235518Sminshall } else { 49335518Sminshall TTYADD('\r'); 49435518Sminshall if (crmod) { 49535518Sminshall TTYADD('\n'); 49632377Sminshall } 49732377Sminshall } 49835518Sminshall } else { 49935518Sminshall telrcv_state = TS_CR; 50035518Sminshall TTYADD('\r'); 50135518Sminshall if (crmod) { 50235518Sminshall TTYADD('\n'); 50335518Sminshall } 50432377Sminshall } 50532377Sminshall } else { 50632377Sminshall TTYADD(c); 50732377Sminshall } 50832377Sminshall continue; 50927088Sminshall 51032377Sminshall case TS_IAC: 51132377Sminshall switch (c) { 51232377Sminshall 51332377Sminshall case WILL: 51432377Sminshall telrcv_state = TS_WILL; 51532377Sminshall continue; 51627261Sminshall 51732377Sminshall case WONT: 51832377Sminshall telrcv_state = TS_WONT; 51932377Sminshall continue; 52027261Sminshall 52132377Sminshall case DO: 52232377Sminshall telrcv_state = TS_DO; 52332377Sminshall continue; 52427261Sminshall 52532377Sminshall case DONT: 52632377Sminshall telrcv_state = TS_DONT; 52732377Sminshall continue; 52827261Sminshall 52932377Sminshall case DM: 53032377Sminshall /* 53132377Sminshall * We may have missed an urgent notification, 53232377Sminshall * so make sure we flush whatever is in the 53332377Sminshall * buffer currently. 53432377Sminshall */ 53532377Sminshall SYNCHing = 1; 53632377Sminshall ttyflush(1); 53732554Sminshall SYNCHing = stilloob(); 53832377Sminshall settimer(gotDM); 53932377Sminshall break; 54027088Sminshall 54132377Sminshall case NOP: 54232377Sminshall case GA: 54332377Sminshall break; 54427088Sminshall 54532377Sminshall case SB: 54632377Sminshall SB_CLEAR(); 54732377Sminshall telrcv_state = TS_SB; 54832377Sminshall continue; 54927261Sminshall 55032377Sminshall # if defined(TN3270) 55132377Sminshall case EOR: 55232377Sminshall if (In3270) { 55332377Sminshall Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 55432377Sminshall if (Ibackp == Ifrontp) { 55532377Sminshall Ibackp = Ifrontp = Ibuf; 55632377Sminshall ISend = 0; /* should have been! */ 55732377Sminshall } else { 55832377Sminshall ISend = 1; 55927088Sminshall } 56027088Sminshall } 56127088Sminshall break; 56232377Sminshall # endif /* defined(TN3270) */ 56332377Sminshall 56432377Sminshall case IAC: 56532377Sminshall # if !defined(TN3270) 56632377Sminshall TTYADD(IAC); 56732377Sminshall # else /* !defined(TN3270) */ 56832377Sminshall if (In3270) { 56932377Sminshall *Ifrontp++ = IAC; 57032377Sminshall } else { 57132377Sminshall TTYADD(IAC); 57232377Sminshall } 57332377Sminshall # endif /* !defined(TN3270) */ 57427088Sminshall break; 57532377Sminshall 57627088Sminshall default: 57727088Sminshall break; 57827088Sminshall } 57932377Sminshall telrcv_state = TS_DATA; 58032377Sminshall continue; 58127088Sminshall 58232377Sminshall case TS_WILL: 58332377Sminshall printoption(">RCVD", will, c, !hisopts[c]); 58432377Sminshall if (c == TELOPT_TM) { 58532377Sminshall if (flushout) { 58632377Sminshall flushout = 0; 58732377Sminshall } 58832377Sminshall } else if (!hisopts[c]) { 58932377Sminshall willoption(c, 1); 59032377Sminshall } 59132377Sminshall SetIn3270(); 59232377Sminshall telrcv_state = TS_DATA; 59332377Sminshall continue; 59427110Sminshall 59532377Sminshall case TS_WONT: 59632377Sminshall printoption(">RCVD", wont, c, hisopts[c]); 59732377Sminshall if (c == TELOPT_TM) { 59832377Sminshall if (flushout) { 59932377Sminshall flushout = 0; 60032377Sminshall } 60132377Sminshall } else if (hisopts[c]) { 60232377Sminshall wontoption(c, 1); 60332377Sminshall } 60432377Sminshall SetIn3270(); 60532377Sminshall telrcv_state = TS_DATA; 60632377Sminshall continue; 60727088Sminshall 60832377Sminshall case TS_DO: 60932377Sminshall printoption(">RCVD", doopt, c, !myopts[c]); 61032377Sminshall if (!myopts[c]) 61132377Sminshall dooption(c); 61232377Sminshall SetIn3270(); 613*37219Sminshall if (c == TELOPT_NAWS) { 614*37219Sminshall sendnaws(); 615*37219Sminshall } else if (c == TELOPT_LFLOW) { 616*37219Sminshall localflow = 1; 617*37219Sminshall setcommandmode(); 618*37219Sminshall setconnmode(); 619*37219Sminshall } 62032377Sminshall telrcv_state = TS_DATA; 62132377Sminshall continue; 62227088Sminshall 62332377Sminshall case TS_DONT: 62432377Sminshall printoption(">RCVD", dont, c, myopts[c]); 62532377Sminshall if (myopts[c]) { 62632377Sminshall myopts[c] = 0; 62732554Sminshall printring(&netoring, wont, c); 62832377Sminshall flushline = 1; 62932377Sminshall setconnmode(); /* set new tty mode (maybe) */ 63032377Sminshall printoption(">SENT", wont, c, 0); 63132377Sminshall } 63232377Sminshall SetIn3270(); 63332377Sminshall telrcv_state = TS_DATA; 63432377Sminshall continue; 63527088Sminshall 63632377Sminshall case TS_SB: 63732377Sminshall if (c == IAC) { 63832377Sminshall telrcv_state = TS_SE; 63932377Sminshall } else { 64032377Sminshall SB_ACCUM(c); 64132377Sminshall } 64232377Sminshall continue; 64327088Sminshall 64432377Sminshall case TS_SE: 64532377Sminshall if (c != SE) { 64632377Sminshall if (c != IAC) { 64732377Sminshall SB_ACCUM(IAC); 64832377Sminshall } 64932377Sminshall SB_ACCUM(c); 65032377Sminshall telrcv_state = TS_SB; 65132377Sminshall } else { 65232377Sminshall SB_TERM(); 65332377Sminshall suboption(); /* handle sub-option */ 65432377Sminshall SetIn3270(); 65532377Sminshall telrcv_state = TS_DATA; 65632377Sminshall } 65727088Sminshall } 65827088Sminshall } 65932667Sminshall if (count) 66032667Sminshall ring_consumed(&netiring, count); 66132385Sminshall return returnValue||count; 66227088Sminshall } 66332385Sminshall 66432385Sminshall static int 66532554Sminshall telsnd() 66632385Sminshall { 66732385Sminshall int tcc; 66832385Sminshall int count; 66932385Sminshall int returnValue = 0; 67032385Sminshall char *tbp; 67132385Sminshall 67232385Sminshall tcc = 0; 67332385Sminshall count = 0; 67432385Sminshall while (NETROOM() > 2) { 67532385Sminshall register int sc; 67632385Sminshall register int c; 67732385Sminshall 67832385Sminshall if (tcc == 0) { 67932385Sminshall if (count) { 68032528Sminshall ring_consumed(&ttyiring, count); 68132385Sminshall returnValue = 1; 68232385Sminshall count = 0; 68332385Sminshall } 68432528Sminshall tbp = ttyiring.consume; 68532528Sminshall tcc = ring_full_consecutive(&ttyiring); 68632385Sminshall if (tcc == 0) { 68732385Sminshall break; 68832385Sminshall } 68932385Sminshall } 69032385Sminshall c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; 69132385Sminshall if (sc == escape) { 69232385Sminshall command(0); 69332385Sminshall tcc = 0; 69432385Sminshall flushline = 1; 69532385Sminshall break; 69632385Sminshall } else if (MODE_LINE(globalmode) && (sc == echoc)) { 69732385Sminshall if (tcc > 0 && strip(*tbp) == echoc) { 69832385Sminshall tcc--; tbp++; count++; 69932385Sminshall } else { 70032385Sminshall dontlecho = !dontlecho; 70132385Sminshall settimer(echotoggle); 70232385Sminshall setconnmode(); 70332385Sminshall flushline = 1; 70432385Sminshall break; 70532385Sminshall } 70632385Sminshall } 70732385Sminshall if (localchars) { 70832385Sminshall if (TerminalSpecialChars(sc) == 0) { 70932385Sminshall break; 71032385Sminshall } 71132385Sminshall } 71232385Sminshall if (!myopts[TELOPT_BINARY]) { 71332385Sminshall switch (c) { 71432385Sminshall case '\n': 71532385Sminshall /* 71632385Sminshall * If we are in CRMOD mode (\r ==> \n) 71732385Sminshall * on our local machine, then probably 71832385Sminshall * a newline (unix) is CRLF (TELNET). 71932385Sminshall */ 72032385Sminshall if (MODE_LOCAL_CHARS(globalmode)) { 72132385Sminshall NETADD('\r'); 72232385Sminshall } 72332385Sminshall NETADD('\n'); 72432385Sminshall flushline = 1; 72532385Sminshall break; 72632385Sminshall case '\r': 72732385Sminshall if (!crlf) { 72832385Sminshall NET2ADD('\r', '\0'); 72932385Sminshall } else { 73032385Sminshall NET2ADD('\r', '\n'); 73132385Sminshall } 73232385Sminshall flushline = 1; 73332385Sminshall break; 73432385Sminshall case IAC: 73532385Sminshall NET2ADD(IAC, IAC); 73632385Sminshall break; 73732385Sminshall default: 73832385Sminshall NETADD(c); 73932385Sminshall break; 74032385Sminshall } 74132385Sminshall } else if (c == IAC) { 74232385Sminshall NET2ADD(IAC, IAC); 74332385Sminshall } else { 74432385Sminshall NETADD(c); 74532385Sminshall } 74632385Sminshall } 74732667Sminshall if (count) 74832667Sminshall ring_consumed(&ttyiring, count); 74932385Sminshall return returnValue||count; /* Non-zero if we did anything */ 75032385Sminshall } 75132377Sminshall 75227088Sminshall /* 75332377Sminshall * Scheduler() 75432377Sminshall * 75532377Sminshall * Try to do something. 75632377Sminshall * 75732377Sminshall * If we do something useful, return 1; else return 0. 75832377Sminshall * 75927110Sminshall */ 76027110Sminshall 76127110Sminshall 76232377Sminshall int 76332377Sminshall Scheduler(block) 76432377Sminshall int block; /* should we block in the select ? */ 76527110Sminshall { 76632377Sminshall /* One wants to be a bit careful about setting returnValue 76732377Sminshall * to one, since a one implies we did some useful work, 76832377Sminshall * and therefore probably won't be called to block next 76932377Sminshall * time (TN3270 mode only). 77032377Sminshall */ 77132531Sminshall int returnValue; 77232531Sminshall int netin, netout, netex, ttyin, ttyout; 77327110Sminshall 77432531Sminshall /* Decide which rings should be processed */ 77532531Sminshall 77632531Sminshall netout = ring_full_count(&netoring) && 77732531Sminshall (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]); 77832531Sminshall ttyout = ring_full_count(&ttyoring); 77932531Sminshall 78032377Sminshall #if defined(TN3270) 78132531Sminshall ttyin = ring_empty_count(&ttyiring) && (shell_active == 0); 78232377Sminshall #else /* defined(TN3270) */ 78332531Sminshall ttyin = ring_empty_count(&ttyiring); 78432377Sminshall #endif /* defined(TN3270) */ 78532531Sminshall 78632531Sminshall #if defined(TN3270) 78732531Sminshall netin = ring_empty_count(&netiring); 78832377Sminshall # else /* !defined(TN3270) */ 78932531Sminshall netin = !ISend && ring_empty_count(&netiring); 79032377Sminshall # endif /* !defined(TN3270) */ 79132531Sminshall 79232531Sminshall netex = !SYNCHing; 79332531Sminshall 79432531Sminshall /* If we have seen a signal recently, reset things */ 79532377Sminshall # if defined(TN3270) && defined(unix) 79632377Sminshall if (HaveInput) { 79732377Sminshall HaveInput = 0; 79832377Sminshall signal(SIGIO, inputAvailable); 79932377Sminshall } 80032377Sminshall #endif /* defined(TN3270) && defined(unix) */ 80132377Sminshall 80232531Sminshall /* Call to system code to process rings */ 80327178Sminshall 80432531Sminshall returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); 80527178Sminshall 80632531Sminshall /* Now, look at the input rings, looking for work to do. */ 80732377Sminshall 80832531Sminshall if (ring_full_count(&ttyiring)) { 80932377Sminshall # if defined(TN3270) 81032377Sminshall if (In3270) { 81134848Sminshall int c; 81234848Sminshall 81333804Sminshall c = DataFromTerminal(ttyiring.consume, 81432528Sminshall ring_full_consecutive(&ttyiring)); 81532377Sminshall if (c) { 81632377Sminshall returnValue = 1; 81732667Sminshall ring_consumed(&ttyiring, c); 81832377Sminshall } 81932377Sminshall } else { 82032377Sminshall # endif /* defined(TN3270) */ 82132554Sminshall returnValue |= telsnd(); 82232377Sminshall # if defined(TN3270) 82327178Sminshall } 82432531Sminshall # endif /* defined(TN3270) */ 82527178Sminshall } 82632377Sminshall 82732528Sminshall if (ring_full_count(&netiring)) { 82832377Sminshall # if !defined(TN3270) 82932385Sminshall returnValue |= telrcv(); 83032377Sminshall # else /* !defined(TN3270) */ 83132377Sminshall returnValue = Push3270(); 83232377Sminshall # endif /* !defined(TN3270) */ 83332377Sminshall } 83432377Sminshall return returnValue; 83527178Sminshall } 83627178Sminshall 83727178Sminshall /* 83832377Sminshall * Select from tty and network... 83927088Sminshall */ 84032377Sminshall void 84132377Sminshall telnet() 84227088Sminshall { 84332531Sminshall sys_telnet_init(); 84427088Sminshall 84532377Sminshall # if !defined(TN3270) 84632377Sminshall if (telnetport) { 84732377Sminshall if (!hisopts[TELOPT_SGA]) { 84832377Sminshall willoption(TELOPT_SGA, 0); 84927110Sminshall } 85032377Sminshall if (!myopts[TELOPT_TTYPE]) { 85134848Sminshall dooption(TELOPT_TTYPE); 85232377Sminshall } 853*37219Sminshall if (!myopts[TELOPT_NAWS]) { 854*37219Sminshall dooption(TELOPT_NAWS); 855*37219Sminshall } 856*37219Sminshall if (!myopts[TELOPT_TSPEED]) { 857*37219Sminshall dooption(TELOPT_TSPEED); 858*37219Sminshall } 859*37219Sminshall if (!myopts[TELOPT_LFLOW]) { 860*37219Sminshall dooption(TELOPT_LFLOW); 861*37219Sminshall } 86227178Sminshall } 86332377Sminshall # endif /* !defined(TN3270) */ 86427088Sminshall 86532377Sminshall # if !defined(TN3270) 86632377Sminshall for (;;) { 86732385Sminshall int schedValue; 86832385Sminshall 86932385Sminshall while ((schedValue = Scheduler(0)) != 0) { 87032385Sminshall if (schedValue == -1) { 87132385Sminshall setcommandmode(); 87232385Sminshall return; 87332385Sminshall } 87432385Sminshall } 87532385Sminshall 87632531Sminshall if (Scheduler(1) == -1) { 87732377Sminshall setcommandmode(); 87832377Sminshall return; 87932377Sminshall } 88032377Sminshall } 88132377Sminshall # else /* !defined(TN3270) */ 88232377Sminshall for (;;) { 88332377Sminshall int schedValue; 88427088Sminshall 88532377Sminshall while (!In3270 && !shell_active) { 88632531Sminshall if (Scheduler(1) == -1) { 88732377Sminshall setcommandmode(); 88832377Sminshall return; 88932377Sminshall } 89027088Sminshall } 89132377Sminshall 89232377Sminshall while ((schedValue = Scheduler(0)) != 0) { 89332377Sminshall if (schedValue == -1) { 89432377Sminshall setcommandmode(); 89532377Sminshall return; 89632377Sminshall } 89727088Sminshall } 89832377Sminshall /* If there is data waiting to go out to terminal, don't 89932377Sminshall * schedule any more data for the terminal. 90032377Sminshall */ 90134304Sminshall if (ring_full_count(&ttyoring)) { 90232377Sminshall schedValue = 1; 90327088Sminshall } else { 90432377Sminshall if (shell_active) { 90532377Sminshall if (shell_continue() == 0) { 90632377Sminshall ConnectScreen(); 90727088Sminshall } 90832377Sminshall } else if (In3270) { 90932377Sminshall schedValue = DoTerminalOutput(); 91032377Sminshall } 91127088Sminshall } 91232377Sminshall if (schedValue && (shell_active == 0)) { 91332531Sminshall if (Scheduler(1) == -1) { 91432377Sminshall setcommandmode(); 91532377Sminshall return; 91632377Sminshall } 91727088Sminshall } 91832377Sminshall } 91932377Sminshall # endif /* !defined(TN3270) */ 92027088Sminshall } 92132377Sminshall 92234848Sminshall #if 0 /* XXX - this not being in is a bug */ 92327088Sminshall /* 92432554Sminshall * nextitem() 92532554Sminshall * 92632554Sminshall * Return the address of the next "item" in the TELNET data 92732554Sminshall * stream. This will be the address of the next character if 92832554Sminshall * the current address is a user data character, or it will 92932554Sminshall * be the address of the character following the TELNET command 93032554Sminshall * if the current address is a TELNET IAC ("I Am a Command") 93132554Sminshall * character. 93232554Sminshall */ 93332554Sminshall 93432554Sminshall static char * 93532554Sminshall nextitem(current) 93632554Sminshall char *current; 93732554Sminshall { 93832554Sminshall if ((*current&0xff) != IAC) { 93932554Sminshall return current+1; 94032554Sminshall } 94132554Sminshall switch (*(current+1)&0xff) { 94232554Sminshall case DO: 94332554Sminshall case DONT: 94432554Sminshall case WILL: 94532554Sminshall case WONT: 94632554Sminshall return current+3; 94732554Sminshall case SB: /* loop forever looking for the SE */ 94832554Sminshall { 94932554Sminshall register char *look = current+2; 95032554Sminshall 95132554Sminshall for (;;) { 95232554Sminshall if ((*look++&0xff) == IAC) { 95332554Sminshall if ((*look++&0xff) == SE) { 95432554Sminshall return look; 95532554Sminshall } 95632554Sminshall } 95732554Sminshall } 95832554Sminshall } 95932554Sminshall default: 96032554Sminshall return current+2; 96132554Sminshall } 96232554Sminshall } 96334848Sminshall #endif /* 0 */ 96432554Sminshall 96532554Sminshall /* 96632554Sminshall * netclear() 96732554Sminshall * 96832554Sminshall * We are about to do a TELNET SYNCH operation. Clear 96932554Sminshall * the path to the network. 97032554Sminshall * 97132554Sminshall * Things are a bit tricky since we may have sent the first 97232554Sminshall * byte or so of a previous TELNET command into the network. 97332554Sminshall * So, we have to scan the network buffer from the beginning 97432554Sminshall * until we are up to where we want to be. 97532554Sminshall * 97632554Sminshall * A side effect of what we do, just to keep things 97732554Sminshall * simple, is to clear the urgent data pointer. The principal 97832554Sminshall * caller should be setting the urgent data pointer AFTER calling 97932554Sminshall * us in any case. 98032554Sminshall */ 98132554Sminshall 98232554Sminshall static void 98332554Sminshall netclear() 98432554Sminshall { 98532554Sminshall #if 0 /* XXX */ 98632554Sminshall register char *thisitem, *next; 98732554Sminshall char *good; 98832554Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 98932554Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 99032554Sminshall 99132554Sminshall thisitem = netobuf; 99232554Sminshall 99332554Sminshall while ((next = nextitem(thisitem)) <= netobuf.send) { 99432554Sminshall thisitem = next; 99532554Sminshall } 99632554Sminshall 99732554Sminshall /* Now, thisitem is first before/at boundary. */ 99832554Sminshall 99932554Sminshall good = netobuf; /* where the good bytes go */ 100032554Sminshall 100132554Sminshall while (netoring.add > thisitem) { 100232554Sminshall if (wewant(thisitem)) { 100332554Sminshall int length; 100432554Sminshall 100532554Sminshall next = thisitem; 100632554Sminshall do { 100732554Sminshall next = nextitem(next); 100832554Sminshall } while (wewant(next) && (nfrontp > next)); 100932554Sminshall length = next-thisitem; 101032554Sminshall memcpy(good, thisitem, length); 101132554Sminshall good += length; 101232554Sminshall thisitem = next; 101332554Sminshall } else { 101432554Sminshall thisitem = nextitem(thisitem); 101532554Sminshall } 101632554Sminshall } 101732554Sminshall 101832554Sminshall #endif /* 0 */ 101932554Sminshall } 102032554Sminshall 102132554Sminshall /* 102232377Sminshall * These routines add various telnet commands to the data stream. 102327088Sminshall */ 102432377Sminshall 102532554Sminshall static void 102632554Sminshall doflush() 102732554Sminshall { 102832554Sminshall NET2ADD(IAC, DO); 102932554Sminshall NETADD(TELOPT_TM); 103032554Sminshall flushline = 1; 103132554Sminshall flushout = 1; 103232554Sminshall ttyflush(1); /* Flush/drop output */ 103332554Sminshall /* do printoption AFTER flush, otherwise the output gets tossed... */ 103432554Sminshall printoption("<SENT", doopt, TELOPT_TM, 0); 103532554Sminshall } 103632554Sminshall 103732377Sminshall void 103832377Sminshall xmitAO() 103927088Sminshall { 104032377Sminshall NET2ADD(IAC, AO); 104132377Sminshall if (autoflush) { 104232377Sminshall doflush(); 104332377Sminshall } 104432377Sminshall } 104527088Sminshall 104632377Sminshall 104732377Sminshall void 104832377Sminshall xmitEL() 104927088Sminshall { 105032377Sminshall NET2ADD(IAC, EL); 105127088Sminshall } 105227088Sminshall 105332377Sminshall void 105432377Sminshall xmitEC() 105527088Sminshall { 105632377Sminshall NET2ADD(IAC, EC); 105727088Sminshall } 105827088Sminshall 105932377Sminshall 106032377Sminshall #if defined(NOT43) 106132377Sminshall int 106232377Sminshall #else /* defined(NOT43) */ 106332377Sminshall void 106432377Sminshall #endif /* defined(NOT43) */ 106532377Sminshall dosynch() 106627088Sminshall { 106732377Sminshall netclear(); /* clear the path to the network */ 106833294Sminshall NETADD(IAC); 106933294Sminshall setneturg(); 107033294Sminshall NETADD(DM); 107127088Sminshall 107232377Sminshall #if defined(NOT43) 107332377Sminshall return 0; 107432377Sminshall #endif /* defined(NOT43) */ 107527088Sminshall } 107627088Sminshall 107732377Sminshall void 107832377Sminshall intp() 107927088Sminshall { 108032377Sminshall NET2ADD(IAC, IP); 108132377Sminshall flushline = 1; 108232377Sminshall if (autoflush) { 108332377Sminshall doflush(); 108432377Sminshall } 108532377Sminshall if (autosynch) { 108632377Sminshall dosynch(); 108732377Sminshall } 108827088Sminshall } 108927186Sminshall 109032377Sminshall void 109132377Sminshall sendbrk() 109227186Sminshall { 109332377Sminshall NET2ADD(IAC, BREAK); 109432377Sminshall flushline = 1; 109532377Sminshall if (autoflush) { 109632377Sminshall doflush(); 109732377Sminshall } 109832377Sminshall if (autosynch) { 109932377Sminshall dosynch(); 110032377Sminshall } 110127186Sminshall } 1102*37219Sminshall /* 1103*37219Sminshall * Send a window size update to the remote system. 1104*37219Sminshall */ 1105*37219Sminshall 1106*37219Sminshall void 1107*37219Sminshall sendnaws() 1108*37219Sminshall { 1109*37219Sminshall long rows, cols; 1110*37219Sminshall 1111*37219Sminshall #define NETADDCHAR(x) \ 1112*37219Sminshall { \ 1113*37219Sminshall if (((x) & 0xff) == 0xff) { \ 1114*37219Sminshall NET2ADD(IAC, IAC) \ 1115*37219Sminshall } else { \ 1116*37219Sminshall NETADD(x) \ 1117*37219Sminshall } \ 1118*37219Sminshall } 1119*37219Sminshall #define NETADDSHORT(x) { NETADDCHAR(x >> 8); NETADDCHAR(x & 0xff) } 1120*37219Sminshall 1121*37219Sminshall if (TerminalWindowSize(&rows, &cols) == 0) { /* Failed */ 1122*37219Sminshall return; 1123*37219Sminshall } 1124*37219Sminshall 1125*37219Sminshall if (NETROOM() >= 3 + 8 + 2) { 1126*37219Sminshall NET2ADD(IAC, SB); 1127*37219Sminshall NETADD(TELOPT_NAWS); 1128*37219Sminshall NETADDSHORT(cols); 1129*37219Sminshall NETADDSHORT(rows); 1130*37219Sminshall NET2ADD(IAC, SE); 1131*37219Sminshall } 1132*37219Sminshall } 1133