138905Sborman /* 261451Sbostic * Copyright (c) 1989, 1993 361451Sbostic * The Regents of the University of California. All rights reserved. 438905Sborman * 542673Sbostic * %sccs.include.redist.c% 638905Sborman */ 738905Sborman 838905Sborman #ifndef lint 9*65158Sdab static char sccsid[] = "@(#)state.c 8.2 (Berkeley) 12/15/93"; 1038905Sborman #endif /* not lint */ 1138905Sborman 1238905Sborman #include "telnetd.h" 1357212Sdab #if defined(AUTHENTICATION) 1446809Sdab #include <libtelnet/auth.h> 1546809Sdab #endif 1638905Sborman 17*65158Sdab unsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; 18*65158Sdab unsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; 19*65158Sdab unsigned char will[] = { IAC, WILL, '%', 'c', 0 }; 20*65158Sdab unsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; 2138905Sborman int not42 = 1; 2238905Sborman 2338905Sborman /* 2438905Sborman * Buffer for sub-options, and macros 2538905Sborman * for suboptions buffer manipulations 2638905Sborman */ 2746809Sdab unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer; 2838905Sborman 2958971Sdab #define SB_CLEAR() subpointer = subbuffer 3038905Sborman #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 3138905Sborman #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 3238905Sborman *subpointer++ = (c); \ 3338905Sborman } 3438905Sborman #define SB_GET() ((*subpointer++)&0xff) 3538905Sborman #define SB_EOF() (subpointer >= subend) 3644364Sborman #define SB_LEN() (subend - subpointer) 3738905Sborman 3858971Sdab #ifdef ENV_HACK 3958971Sdab unsigned char *subsave; 4058971Sdab #define SB_SAVE() subsave = subpointer; 4158971Sdab #define SB_RESTORE() subpointer = subsave; 4258971Sdab #endif 4338905Sborman 4438905Sborman 4538905Sborman /* 4638905Sborman * State for recv fsm 4738905Sborman */ 4838905Sborman #define TS_DATA 0 /* base state */ 4938905Sborman #define TS_IAC 1 /* look for double IAC's */ 5038905Sborman #define TS_CR 2 /* CR-LF ->'s CR */ 5138905Sborman #define TS_SB 3 /* throw away begin's... */ 5238905Sborman #define TS_SE 4 /* ...end's (suboption negotiation) */ 5338905Sborman #define TS_WILL 5 /* will option negotiation */ 5438905Sborman #define TS_WONT 6 /* wont " */ 5538905Sborman #define TS_DO 7 /* do " */ 5638905Sborman #define TS_DONT 8 /* dont " */ 5738905Sborman 5846809Sdab void 5938905Sborman telrcv() 6038905Sborman { 6138905Sborman register int c; 6238905Sborman static int state = TS_DATA; 6340242Sborman #if defined(CRAY2) && defined(UNICOS5) 6438905Sborman char *opfrontp = pfrontp; 6538905Sborman #endif 6638905Sborman 6738905Sborman while (ncc > 0) { 6838905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6938905Sborman break; 7038905Sborman c = *netip++ & 0377, ncc--; 7160151Sdab #ifdef ENCRYPTION 7246809Sdab if (decrypt_input) 7346809Sdab c = (*decrypt_input)(c); 7460151Sdab #endif /* ENCRYPTION */ 7538905Sborman switch (state) { 7638905Sborman 7738905Sborman case TS_CR: 7838905Sborman state = TS_DATA; 7938905Sborman /* Strip off \n or \0 after a \r */ 8038905Sborman if ((c == 0) || (c == '\n')) { 8138905Sborman break; 8238905Sborman } 8338905Sborman /* FALL THROUGH */ 8438905Sborman 8538905Sborman case TS_DATA: 8638905Sborman if (c == IAC) { 8738905Sborman state = TS_IAC; 8838905Sborman break; 8938905Sborman } 9038905Sborman /* 9138905Sborman * We now map \r\n ==> \r for pragmatic reasons. 9238905Sborman * Many client implementations send \r\n when 9338905Sborman * the user hits the CarriageReturn key. 9438905Sborman * 9538905Sborman * We USED to map \r\n ==> \n, since \r\n says 9638905Sborman * that we want to be in column 1 of the next 9738905Sborman * printable line, and \n is the standard 9838905Sborman * unix way of saying that (\r is only good 9938905Sborman * if CRMOD is set, which it normally is). 10038905Sborman */ 10144364Sborman if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { 10246809Sdab int nc = *netip; 10360151Sdab #ifdef ENCRYPTION 10446809Sdab if (decrypt_input) 10546809Sdab nc = (*decrypt_input)(nc & 0xff); 10660151Sdab #endif /* ENCRYPTION */ 10746809Sdab #ifdef LINEMODE 10838905Sborman /* 10938905Sborman * If we are operating in linemode, 11038905Sborman * convert to local end-of-line. 11138905Sborman */ 11246809Sdab if (linemode && (ncc > 0) && (('\n' == nc) || 11346809Sdab ((0 == nc) && tty_iscrnl())) ) { 11438905Sborman netip++; ncc--; 11538905Sborman c = '\n'; 11646809Sdab } else 11746809Sdab #endif 11846809Sdab { 11960151Sdab #ifdef ENCRYPTION 12046809Sdab if (decrypt_input) 12146809Sdab (void)(*decrypt_input)(-1); 12260151Sdab #endif /* ENCRYPTION */ 12338905Sborman state = TS_CR; 12438905Sborman } 12538905Sborman } 12638905Sborman *pfrontp++ = c; 12738905Sborman break; 12838905Sborman 12938905Sborman case TS_IAC: 13038905Sborman gotiac: switch (c) { 13138905Sborman 13238905Sborman /* 13338905Sborman * Send the process on the pty side an 13438905Sborman * interrupt. Do this with a NULL or 13538905Sborman * interrupt char; depending on the tty mode. 13638905Sborman */ 13738905Sborman case IP: 13846809Sdab DIAG(TD_OPTIONS, 13946809Sdab printoption("td: recv IAC", c)); 14038905Sborman interrupt(); 14138905Sborman break; 14238905Sborman 14338905Sborman case BREAK: 14446809Sdab DIAG(TD_OPTIONS, 14546809Sdab printoption("td: recv IAC", c)); 14638905Sborman sendbrk(); 14738905Sborman break; 14838905Sborman 14938905Sborman /* 15038905Sborman * Are You There? 15138905Sborman */ 15238905Sborman case AYT: 15346809Sdab DIAG(TD_OPTIONS, 15446809Sdab printoption("td: recv IAC", c)); 15545234Sborman recv_ayt(); 15638905Sborman break; 15738905Sborman 15838905Sborman /* 15938905Sborman * Abort Output 16038905Sborman */ 16138905Sborman case AO: 16238905Sborman { 16346809Sdab DIAG(TD_OPTIONS, 16446809Sdab printoption("td: recv IAC", c)); 16538905Sborman ptyflush(); /* half-hearted */ 16638905Sborman init_termbuf(); 16738905Sborman 16838905Sborman if (slctab[SLC_AO].sptr && 16945234Sborman *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { 17040242Sborman *pfrontp++ = 17140242Sborman (unsigned char)*slctab[SLC_AO].sptr; 17238905Sborman } 17338905Sborman 17438905Sborman netclear(); /* clear buffer back */ 17538905Sborman *nfrontp++ = IAC; 17638905Sborman *nfrontp++ = DM; 17738905Sborman neturg = nfrontp-1; /* off by one XXX */ 17846809Sdab DIAG(TD_OPTIONS, 17946809Sdab printoption("td: send IAC", DM)); 18038905Sborman break; 18138905Sborman } 18238905Sborman 18338905Sborman /* 18438905Sborman * Erase Character and 18538905Sborman * Erase Line 18638905Sborman */ 18738905Sborman case EC: 18838905Sborman case EL: 18938905Sborman { 19040242Sborman cc_t ch; 19138905Sborman 19246809Sdab DIAG(TD_OPTIONS, 19346809Sdab printoption("td: recv IAC", c)); 19438905Sborman ptyflush(); /* half-hearted */ 19538905Sborman init_termbuf(); 19644364Sborman if (c == EC) 19744364Sborman ch = *slctab[SLC_EC].sptr; 19844364Sborman else 19944364Sborman ch = *slctab[SLC_EL].sptr; 20045234Sborman if (ch != (cc_t)(_POSIX_VDISABLE)) 20140242Sborman *pfrontp++ = (unsigned char)ch; 20238905Sborman break; 20338905Sborman } 20438905Sborman 20538905Sborman /* 20638905Sborman * Check for urgent data... 20738905Sborman */ 20838905Sborman case DM: 20946809Sdab DIAG(TD_OPTIONS, 21046809Sdab printoption("td: recv IAC", c)); 21138905Sborman SYNCHing = stilloob(net); 21238905Sborman settimer(gotDM); 21338905Sborman break; 21438905Sborman 21538905Sborman 21638905Sborman /* 21738905Sborman * Begin option subnegotiation... 21838905Sborman */ 21938905Sborman case SB: 22038905Sborman state = TS_SB; 22138905Sborman SB_CLEAR(); 22238905Sborman continue; 22338905Sborman 22438905Sborman case WILL: 22538905Sborman state = TS_WILL; 22638905Sborman continue; 22738905Sborman 22838905Sborman case WONT: 22938905Sborman state = TS_WONT; 23038905Sborman continue; 23138905Sborman 23238905Sborman case DO: 23338905Sborman state = TS_DO; 23438905Sborman continue; 23538905Sborman 23638905Sborman case DONT: 23738905Sborman state = TS_DONT; 23838905Sborman continue; 23938905Sborman case EOR: 24044364Sborman if (his_state_is_will(TELOPT_EOR)) 24138905Sborman doeof(); 24238905Sborman break; 24338905Sborman 24438905Sborman /* 24538905Sborman * Handle RFC 10xx Telnet linemode option additions 24638905Sborman * to command stream (EOF, SUSP, ABORT). 24738905Sborman */ 24838905Sborman case xEOF: 24938905Sborman doeof(); 25038905Sborman break; 25138905Sborman 25238905Sborman case SUSP: 25338905Sborman sendsusp(); 25438905Sborman break; 25538905Sborman 25638905Sborman case ABORT: 25738905Sborman sendbrk(); 25838905Sborman break; 25938905Sborman 26038905Sborman case IAC: 26138905Sborman *pfrontp++ = c; 26238905Sborman break; 26338905Sborman } 26438905Sborman state = TS_DATA; 26538905Sborman break; 26638905Sborman 26738905Sborman case TS_SB: 26838905Sborman if (c == IAC) { 26938905Sborman state = TS_SE; 27038905Sborman } else { 27138905Sborman SB_ACCUM(c); 27238905Sborman } 27338905Sborman break; 27438905Sborman 27538905Sborman case TS_SE: 27638905Sborman if (c != SE) { 27738905Sborman if (c != IAC) { 27838905Sborman /* 27938905Sborman * bad form of suboption negotiation. 28038905Sborman * handle it in such a way as to avoid 28138905Sborman * damage to local state. Parse 28238905Sborman * suboption buffer found so far, 28338905Sborman * then treat remaining stream as 28438905Sborman * another command sequence. 28538905Sborman */ 28646809Sdab 28746809Sdab /* for DIAGNOSTICS */ 28844364Sborman SB_ACCUM(IAC); 28944364Sborman SB_ACCUM(c); 29044364Sborman subpointer -= 2; 29146809Sdab 29238905Sborman SB_TERM(); 29338905Sborman suboption(); 29438905Sborman state = TS_IAC; 29538905Sborman goto gotiac; 29638905Sborman } 29738905Sborman SB_ACCUM(c); 29838905Sborman state = TS_SB; 29938905Sborman } else { 30046809Sdab /* for DIAGNOSTICS */ 30144364Sborman SB_ACCUM(IAC); 30244364Sborman SB_ACCUM(SE); 30344364Sborman subpointer -= 2; 30446809Sdab 30538905Sborman SB_TERM(); 30638905Sborman suboption(); /* handle sub-option */ 30738905Sborman state = TS_DATA; 30838905Sborman } 30938905Sborman break; 31038905Sborman 31138905Sborman case TS_WILL: 31239503Sborman willoption(c); 31338905Sborman state = TS_DATA; 31438905Sborman continue; 31538905Sborman 31638905Sborman case TS_WONT: 31739503Sborman wontoption(c); 31838905Sborman state = TS_DATA; 31938905Sborman continue; 32038905Sborman 32138905Sborman case TS_DO: 32239503Sborman dooption(c); 32338905Sborman state = TS_DATA; 32438905Sborman continue; 32538905Sborman 32638905Sborman case TS_DONT: 32739503Sborman dontoption(c); 32838905Sborman state = TS_DATA; 32938905Sborman continue; 33038905Sborman 33138905Sborman default: 33238905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 33338905Sborman printf("telnetd: panic state=%d\n", state); 33438905Sborman exit(1); 33538905Sborman } 33638905Sborman } 33740242Sborman #if defined(CRAY2) && defined(UNICOS5) 33838905Sborman if (!linemode) { 33938905Sborman char xptyobuf[BUFSIZ+NETSLOP]; 34038905Sborman char xbuf2[BUFSIZ]; 34138905Sborman register char *cp; 34238905Sborman int n = pfrontp - opfrontp, oc; 34338905Sborman bcopy(opfrontp, xptyobuf, n); 34438905Sborman pfrontp = opfrontp; 34538905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, 34638905Sborman xbuf2, &oc, BUFSIZ); 34738905Sborman for (cp = xbuf2; oc > 0; --oc) 34838905Sborman if ((*nfrontp++ = *cp++) == IAC) 34938905Sborman *nfrontp++ = IAC; 35038905Sborman } 35140242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 35238905Sborman } /* end of telrcv */ 35338905Sborman 35438905Sborman /* 35538905Sborman * The will/wont/do/dont state machines are based on Dave Borman's 35644364Sborman * Telnet option processing state machine. 35738905Sborman * 35838905Sborman * These correspond to the following states: 35938905Sborman * my_state = the last negotiated state 36038905Sborman * want_state = what I want the state to go to 36138905Sborman * want_resp = how many requests I have sent 36238905Sborman * All state defaults are negative, and resp defaults to 0. 36338905Sborman * 36438905Sborman * When initiating a request to change state to new_state: 36538905Sborman * 36638905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 36738905Sborman * do nothing; 36838905Sborman * } else { 36938905Sborman * want_state = new_state; 37038905Sborman * send new_state; 37138905Sborman * want_resp++; 37238905Sborman * } 37338905Sborman * 37438905Sborman * When receiving new_state: 37538905Sborman * 37638905Sborman * if (want_resp) { 37738905Sborman * want_resp--; 37838905Sborman * if (want_resp && (new_state == my_state)) 37938905Sborman * want_resp--; 38038905Sborman * } 38138905Sborman * if ((want_resp == 0) && (new_state != want_state)) { 38238905Sborman * if (ok_to_switch_to new_state) 38338905Sborman * want_state = new_state; 38438905Sborman * else 38538905Sborman * want_resp++; 38638905Sborman * send want_state; 38738905Sborman * } 38838905Sborman * my_state = new_state; 38938905Sborman * 39038905Sborman * Note that new_state is implied in these functions by the function itself. 39138905Sborman * will and do imply positive new_state, wont and dont imply negative. 39238905Sborman * 39338905Sborman * Finally, there is one catch. If we send a negative response to a 39438905Sborman * positive request, my_state will be the positive while want_state will 39538905Sborman * remain negative. my_state will revert to negative when the negative 39638905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells 39738905Sborman * us not only the last negotiated state, but also tells us what the peer 39838905Sborman * wants to be doing as well. It is important to understand this difference 39938905Sborman * as we may wish to be processing data streams based on our desired state 40038905Sborman * (want_state) or based on what the peer thinks the state is (my_state). 40138905Sborman * 40238905Sborman * This all works fine because if the peer sends a positive request, the data 40338905Sborman * that we receive prior to negative acknowlegment will probably be affected 40438905Sborman * by the positive state, and we can process it as such (if we can; if we 40538905Sborman * can't then it really doesn't matter). If it is that important, then the 40638905Sborman * peer probably should be buffering until this option state negotiation 40738905Sborman * is complete. 40838905Sborman * 40938905Sborman */ 41046809Sdab void 41139503Sborman send_do(option, init) 41239503Sborman int option, init; 41338905Sborman { 41439503Sborman if (init) { 41544364Sborman if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || 41644364Sborman his_want_state_is_will(option)) 41739503Sborman return; 41839531Sborman /* 41939531Sborman * Special case for TELOPT_TM: We send a DO, but pretend 42039531Sborman * that we sent a DONT, so that we can send more DOs if 42139531Sborman * we want to. 42239531Sborman */ 42339531Sborman if (option == TELOPT_TM) 42444364Sborman set_his_want_state_wont(option); 42539531Sborman else 42644364Sborman set_his_want_state_will(option); 42739503Sborman do_dont_resp[option]++; 42839503Sborman } 429*65158Sdab (void) sprintf(nfrontp, (char *)doopt, option); 43039503Sborman nfrontp += sizeof (dont) - 2; 43146809Sdab 43246809Sdab DIAG(TD_OPTIONS, printoption("td: send do", option)); 43339503Sborman } 43439503Sborman 43557212Sdab #ifdef AUTHENTICATION 43646809Sdab extern void auth_request(); 43746809Sdab #endif 43846809Sdab #ifdef LINEMODE 43946809Sdab extern void doclientstat(); 44046809Sdab #endif 44157212Sdab #ifdef ENCRYPTION 44246809Sdab extern void encrypt_send_support(); 44360151Sdab #endif /* ENCRYPTION */ 44446809Sdab 44546809Sdab void 44639503Sborman willoption(option) 44739503Sborman int option; 44839503Sborman { 44938905Sborman int changeok = 0; 45046809Sdab void (*func)() = 0; 45138905Sborman 45239503Sborman /* 45339503Sborman * process input from peer. 45439503Sborman */ 45539503Sborman 45646809Sdab DIAG(TD_OPTIONS, printoption("td: recv will", option)); 45744364Sborman 45839503Sborman if (do_dont_resp[option]) { 45939503Sborman do_dont_resp[option]--; 46044364Sborman if (do_dont_resp[option] && his_state_is_will(option)) 46139503Sborman do_dont_resp[option]--; 46239503Sborman } 46339531Sborman if (do_dont_resp[option] == 0) { 46444364Sborman if (his_want_state_is_wont(option)) { 46538905Sborman switch (option) { 46638905Sborman 46738905Sborman case TELOPT_BINARY: 46838905Sborman init_termbuf(); 46938905Sborman tty_binaryin(1); 47038905Sborman set_termbuf(); 47138905Sborman changeok++; 47238905Sborman break; 47338905Sborman 47438905Sborman case TELOPT_ECHO: 47538905Sborman /* 47644364Sborman * See comments below for more info. 47738905Sborman */ 47844364Sborman not42 = 0; /* looks like a 4.2 system */ 47938905Sborman break; 48038905Sborman 48138905Sborman case TELOPT_TM: 48238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 48338905Sborman /* 48439503Sborman * This telnetd implementation does not really 48539503Sborman * support timing marks, it just uses them to 48639503Sborman * support the kludge linemode stuff. If we 48739503Sborman * receive a will or wont TM in response to our 48839503Sborman * do TM request that may have been sent to 48939503Sborman * determine kludge linemode support, process 49039503Sborman * it, otherwise TM should get a negative 49139503Sborman * response back. 49238905Sborman */ 49338905Sborman /* 49438905Sborman * Handle the linemode kludge stuff. 49538905Sborman * If we are not currently supporting any 49638905Sborman * linemode at all, then we assume that this 49738905Sborman * is the client telling us to use kludge 49838905Sborman * linemode in response to our query. Set the 49938905Sborman * linemode type that is to be supported, note 50038905Sborman * that the client wishes to use linemode, and 50138905Sborman * eat the will TM as though it never arrived. 50238905Sborman */ 50338905Sborman if (lmodetype < KLUDGE_LINEMODE) { 50438905Sborman lmodetype = KLUDGE_LINEMODE; 50538905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 50639503Sborman send_wont(TELOPT_SGA, 1); 50757212Sdab } else if (lmodetype == NO_AUTOKLUDGE) { 50857212Sdab lmodetype = KLUDGE_OK; 50938905Sborman } 51038905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 51138905Sborman /* 51239531Sborman * We never respond to a WILL TM, and 51344364Sborman * we leave the state WONT. 51438905Sborman */ 51538905Sborman return; 51638905Sborman 51738905Sborman case TELOPT_LFLOW: 51838905Sborman /* 51939503Sborman * If we are going to support flow control 52039503Sborman * option, then don't worry peer that we can't 52139503Sborman * change the flow control characters. 52238905Sborman */ 52338905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 52438905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 52538905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 52638905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 52738905Sborman case TELOPT_TTYPE: 52838905Sborman case TELOPT_SGA: 52938905Sborman case TELOPT_NAWS: 53038905Sborman case TELOPT_TSPEED: 53144364Sborman case TELOPT_XDISPLOC: 532*65158Sdab case TELOPT_NEW_ENVIRON: 533*65158Sdab case TELOPT_OLD_ENVIRON: 53439531Sborman changeok++; 53539531Sborman break; 53639531Sborman 53738905Sborman #ifdef LINEMODE 53838905Sborman case TELOPT_LINEMODE: 53939531Sborman # ifdef KLUDGELINEMODE 54039531Sborman /* 54139531Sborman * Note client's desire to use linemode. 54239531Sborman */ 54339531Sborman lmodetype = REAL_LINEMODE; 54439531Sborman # endif /* KLUDGELINEMODE */ 54546809Sdab func = doclientstat; 54646809Sdab changeok++; 54746809Sdab break; 54839531Sborman #endif /* LINEMODE */ 54938905Sborman 55057212Sdab #ifdef AUTHENTICATION 55146809Sdab case TELOPT_AUTHENTICATION: 55246809Sdab func = auth_request; 55346809Sdab changeok++; 55446809Sdab break; 55546809Sdab #endif 55646809Sdab 55757212Sdab #ifdef ENCRYPTION 55846809Sdab case TELOPT_ENCRYPT: 55946809Sdab func = encrypt_send_support; 56046809Sdab changeok++; 56146809Sdab break; 56260151Sdab #endif /* ENCRYPTION */ 56346809Sdab 56438905Sborman default: 56538905Sborman break; 56638905Sborman } 56739503Sborman if (changeok) { 56844364Sborman set_his_want_state_will(option); 56939503Sborman send_do(option, 0); 57039503Sborman } else { 57139503Sborman do_dont_resp[option]++; 57239503Sborman send_dont(option, 0); 57338905Sborman } 57444364Sborman } else { 57544364Sborman /* 57644364Sborman * Option processing that should happen when 57744364Sborman * we receive conformation of a change in 57844364Sborman * state that we had requested. 57944364Sborman */ 58044364Sborman switch (option) { 58144364Sborman case TELOPT_ECHO: 58244364Sborman not42 = 0; /* looks like a 4.2 system */ 58344364Sborman /* 58444364Sborman * Egads, he responded "WILL ECHO". Turn 58544364Sborman * it off right now! 58644364Sborman */ 58744364Sborman send_dont(option, 1); 58844364Sborman /* 58944364Sborman * "WILL ECHO". Kludge upon kludge! 59044364Sborman * A 4.2 client is now echoing user input at 59144364Sborman * the tty. This is probably undesireable and 59244364Sborman * it should be stopped. The client will 59344364Sborman * respond WONT TM to the DO TM that we send to 59444364Sborman * check for kludge linemode. When the WONT TM 59544364Sborman * arrives, linemode will be turned off and a 59644364Sborman * change propogated to the pty. This change 59744364Sborman * will cause us to process the new pty state 59844364Sborman * in localstat(), which will notice that 59944364Sborman * linemode is off and send a WILL ECHO 60044364Sborman * so that we are properly in character mode and 60144364Sborman * all is well. 60244364Sborman */ 60344364Sborman break; 60444364Sborman #ifdef LINEMODE 60544364Sborman case TELOPT_LINEMODE: 60644364Sborman # ifdef KLUDGELINEMODE 60744364Sborman /* 60844364Sborman * Note client's desire to use linemode. 60944364Sborman */ 61044364Sborman lmodetype = REAL_LINEMODE; 61144364Sborman # endif /* KLUDGELINEMODE */ 61246809Sdab func = doclientstat; 61346809Sdab break; 61444364Sborman #endif /* LINEMODE */ 61546809Sdab 61657212Sdab #ifdef AUTHENTICATION 61746809Sdab case TELOPT_AUTHENTICATION: 61846809Sdab func = auth_request; 61946809Sdab break; 62046809Sdab #endif 62146809Sdab 62257212Sdab #ifdef ENCRYPTION 62346809Sdab case TELOPT_ENCRYPT: 62446809Sdab func = encrypt_send_support; 62546809Sdab break; 62660151Sdab #endif /* ENCRYPTION */ 62757597Sdab case TELOPT_LFLOW: 62858971Sdab func = flowstat; 62957597Sdab break; 63044364Sborman } 63139531Sborman } 63238905Sborman } 63344364Sborman set_his_state_will(option); 63446809Sdab if (func) 63546809Sdab (*func)(); 63638905Sborman } /* end of willoption */ 63738905Sborman 63846809Sdab void 63939503Sborman send_dont(option, init) 64039503Sborman int option, init; 64138905Sborman { 64239503Sborman if (init) { 64344364Sborman if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || 64444364Sborman his_want_state_is_wont(option)) 64539503Sborman return; 64644364Sborman set_his_want_state_wont(option); 64739503Sborman do_dont_resp[option]++; 64839503Sborman } 649*65158Sdab (void) sprintf(nfrontp, (char *)dont, option); 65039503Sborman nfrontp += sizeof (doopt) - 2; 65146809Sdab 65246809Sdab DIAG(TD_OPTIONS, printoption("td: send dont", option)); 65339503Sborman } 65439503Sborman 65546809Sdab void 65639503Sborman wontoption(option) 65739503Sborman int option; 65839503Sborman { 65938905Sborman /* 66038905Sborman * Process client input. 66138905Sborman */ 66239503Sborman 66346809Sdab DIAG(TD_OPTIONS, printoption("td: recv wont", option)); 66444364Sborman 66539503Sborman if (do_dont_resp[option]) { 66639503Sborman do_dont_resp[option]--; 66744364Sborman if (do_dont_resp[option] && his_state_is_wont(option)) 66839503Sborman do_dont_resp[option]--; 66939503Sborman } 67039531Sborman if (do_dont_resp[option] == 0) { 67144364Sborman if (his_want_state_is_will(option)) { 67239503Sborman /* it is always ok to change to negative state */ 67338905Sborman switch (option) { 67438905Sborman case TELOPT_ECHO: 67539503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 67638905Sborman break; 67738905Sborman 67838905Sborman case TELOPT_BINARY: 67938905Sborman init_termbuf(); 68038905Sborman tty_binaryin(0); 68138905Sborman set_termbuf(); 68238905Sborman break; 68338905Sborman 68438905Sborman #ifdef LINEMODE 68538905Sborman case TELOPT_LINEMODE: 68638905Sborman # ifdef KLUDGELINEMODE 68738905Sborman /* 68838905Sborman * If real linemode is supported, then client is 68938905Sborman * asking to turn linemode off. 69038905Sborman */ 69144364Sborman if (lmodetype != REAL_LINEMODE) 69244364Sborman break; 69344364Sborman lmodetype = KLUDGE_LINEMODE; 69438905Sborman # endif /* KLUDGELINEMODE */ 69544364Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 69638905Sborman break; 69746809Sdab #endif /* LINEMODE */ 69838905Sborman 69938905Sborman case TELOPT_TM: 70038905Sborman /* 70139503Sborman * If we get a WONT TM, and had sent a DO TM, 70239503Sborman * don't respond with a DONT TM, just leave it 70339503Sborman * as is. Short circut the state machine to 70439531Sborman * achive this. 70538905Sborman */ 70644364Sborman set_his_want_state_wont(TELOPT_TM); 70738905Sborman return; 70838905Sborman 70938905Sborman case TELOPT_LFLOW: 71038905Sborman /* 71139503Sborman * If we are not going to support flow control 71239503Sborman * option, then let peer know that we can't 71339503Sborman * change the flow control characters. 71438905Sborman */ 71538905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 71638905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 71738905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 71838905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 71938905Sborman break; 72038905Sborman 72157212Sdab #if defined(AUTHENTICATION) 72246809Sdab case TELOPT_AUTHENTICATION: 72346809Sdab auth_finished(0, AUTH_REJECT); 72446809Sdab break; 72546809Sdab #endif 72646809Sdab 72744364Sborman /* 72844364Sborman * For options that we might spin waiting for 72944364Sborman * sub-negotiation, if the client turns off the 73044364Sborman * option rather than responding to the request, 73144364Sborman * we have to treat it here as if we got a response 73244364Sborman * to the sub-negotiation, (by updating the timers) 73344364Sborman * so that we'll break out of the loop. 73444364Sborman */ 73544364Sborman case TELOPT_TTYPE: 73644364Sborman settimer(ttypesubopt); 73744364Sborman break; 73844364Sborman 73944364Sborman case TELOPT_TSPEED: 74044364Sborman settimer(tspeedsubopt); 74144364Sborman break; 74244364Sborman 74344364Sborman case TELOPT_XDISPLOC: 74444364Sborman settimer(xdisplocsubopt); 74544364Sborman break; 74644364Sborman 747*65158Sdab case TELOPT_OLD_ENVIRON: 748*65158Sdab settimer(oenvironsubopt); 749*65158Sdab break; 750*65158Sdab 751*65158Sdab case TELOPT_NEW_ENVIRON: 75244364Sborman settimer(environsubopt); 75344364Sborman break; 75444364Sborman 75538905Sborman default: 75638905Sborman break; 75738905Sborman } 75844364Sborman set_his_want_state_wont(option); 75944364Sborman if (his_state_is_will(option)) 76044364Sborman send_dont(option, 0); 76139531Sborman } else { 76239531Sborman switch (option) { 76339531Sborman case TELOPT_TM: 76439531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 76557212Sdab if (lmodetype < NO_AUTOKLUDGE) { 76639531Sborman lmodetype = NO_LINEMODE; 76739531Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 76839531Sborman send_will(TELOPT_SGA, 1); 76945234Sborman send_will(TELOPT_ECHO, 1); 77039531Sborman } 77139531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 77246809Sdab break; 77346809Sdab 77457212Sdab #if defined(AUTHENTICATION) 77546809Sdab case TELOPT_AUTHENTICATION: 77646809Sdab auth_finished(0, AUTH_REJECT); 77746809Sdab break; 77846809Sdab #endif 77939531Sborman default: 78039531Sborman break; 78139531Sborman } 78239531Sborman } 78338905Sborman } 78444364Sborman set_his_state_wont(option); 78538905Sborman 78639503Sborman } /* end of wontoption */ 78738905Sborman 78846809Sdab void 78939503Sborman send_will(option, init) 79039503Sborman int option, init; 79139503Sborman { 79239503Sborman if (init) { 79344364Sborman if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| 79444364Sborman my_want_state_is_will(option)) 79539503Sborman return; 79644364Sborman set_my_want_state_will(option); 79739503Sborman will_wont_resp[option]++; 79838905Sborman } 799*65158Sdab (void) sprintf(nfrontp, (char *)will, option); 80039503Sborman nfrontp += sizeof (doopt) - 2; 80146809Sdab 80246809Sdab DIAG(TD_OPTIONS, printoption("td: send will", option)); 80339503Sborman } 80438905Sborman 80545234Sborman #if !defined(LINEMODE) || !defined(KLUDGELINEMODE) 80645234Sborman /* 80745234Sborman * When we get a DONT SGA, we will try once to turn it 80845234Sborman * back on. If the other side responds DONT SGA, we 80945234Sborman * leave it at that. This is so that when we talk to 81045234Sborman * clients that understand KLUDGELINEMODE but not LINEMODE, 81145234Sborman * we'll keep them in char-at-a-time mode. 81245234Sborman */ 81345234Sborman int turn_on_sga = 0; 81445234Sborman #endif 81545234Sborman 81646809Sdab void 81739503Sborman dooption(option) 81839503Sborman int option; 81938905Sborman { 82038905Sborman int changeok = 0; 82138905Sborman 82238905Sborman /* 82338905Sborman * Process client input. 82438905Sborman */ 82539503Sborman 82646809Sdab DIAG(TD_OPTIONS, printoption("td: recv do", option)); 82744364Sborman 82839503Sborman if (will_wont_resp[option]) { 82939503Sborman will_wont_resp[option]--; 83044364Sborman if (will_wont_resp[option] && my_state_is_will(option)) 83139503Sborman will_wont_resp[option]--; 83239503Sborman } 83344364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { 83438905Sborman switch (option) { 83538905Sborman case TELOPT_ECHO: 83638905Sborman #ifdef LINEMODE 83745234Sborman # ifdef KLUDGELINEMODE 83845234Sborman if (lmodetype == NO_LINEMODE) 83945234Sborman # else 84045234Sborman if (his_state_is_wont(TELOPT_LINEMODE)) 84145234Sborman # endif 84238905Sborman #endif 84345234Sborman { 84438905Sborman init_termbuf(); 84538905Sborman tty_setecho(1); 84638905Sborman set_termbuf(); 84738905Sborman } 84838905Sborman changeok++; 84938905Sborman break; 85038905Sborman 85138905Sborman case TELOPT_BINARY: 85238905Sborman init_termbuf(); 85338905Sborman tty_binaryout(1); 85438905Sborman set_termbuf(); 85538905Sborman changeok++; 85638905Sborman break; 85738905Sborman 85838905Sborman case TELOPT_SGA: 85938905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 86038905Sborman /* 86139503Sborman * If kludge linemode is in use, then we must 86239503Sborman * process an incoming do SGA for linemode 86339503Sborman * purposes. 86438905Sborman */ 86538905Sborman if (lmodetype == KLUDGE_LINEMODE) { 86638905Sborman /* 86739503Sborman * Receipt of "do SGA" in kludge 86839503Sborman * linemode is the peer asking us to 86939503Sborman * turn off linemode. Make note of 87039503Sborman * the request. 87138905Sborman */ 87238905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 87338905Sborman /* 87439503Sborman * If linemode did not get turned off 87539503Sborman * then don't tell peer that we did. 87639503Sborman * Breaking here forces a wont SGA to 87739503Sborman * be returned. 87838905Sborman */ 87938905Sborman if (linemode) 88038905Sborman break; 88138905Sborman } 88245234Sborman #else 88345234Sborman turn_on_sga = 0; 88438905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 88538905Sborman changeok++; 88638905Sborman break; 88738905Sborman 88838905Sborman case TELOPT_STATUS: 88938905Sborman changeok++; 89038905Sborman break; 89138905Sborman 89238905Sborman case TELOPT_TM: 89339503Sborman /* 89439503Sborman * Special case for TM. We send a WILL, but 89539503Sborman * pretend we sent a WONT. 89639503Sborman */ 89739503Sborman send_will(option, 0); 89844364Sborman set_my_want_state_wont(option); 89944364Sborman set_my_state_wont(option); 90039503Sborman return; 90139503Sborman 90246809Sdab case TELOPT_LOGOUT: 90346809Sdab /* 90446809Sdab * When we get a LOGOUT option, respond 90546809Sdab * with a WILL LOGOUT, make sure that 90646809Sdab * it gets written out to the network, 90746809Sdab * and then just go away... 90846809Sdab */ 90946809Sdab set_my_want_state_will(TELOPT_LOGOUT); 91046809Sdab send_will(TELOPT_LOGOUT, 0); 91146809Sdab set_my_state_will(TELOPT_LOGOUT); 91246809Sdab (void)netflush(); 91346809Sdab cleanup(0); 91446809Sdab /* NOT REACHED */ 91546809Sdab break; 91646809Sdab 91760151Sdab #ifdef ENCRYPTION 91846809Sdab case TELOPT_ENCRYPT: 91946809Sdab changeok++; 92046809Sdab break; 92160151Sdab #endif /* ENCRYPTION */ 92238905Sborman case TELOPT_LINEMODE: 92338905Sborman case TELOPT_TTYPE: 92438905Sborman case TELOPT_NAWS: 92538905Sborman case TELOPT_TSPEED: 92638905Sborman case TELOPT_LFLOW: 92744364Sborman case TELOPT_XDISPLOC: 928*65158Sdab #ifdef TELOPT_ENVIRON 929*65158Sdab case TELOPT_NEW_ENVIRON: 930*65158Sdab #endif 931*65158Sdab case TELOPT_OLD_ENVIRON: 93238905Sborman default: 93338905Sborman break; 93438905Sborman } 93539503Sborman if (changeok) { 93644364Sborman set_my_want_state_will(option); 93739503Sborman send_will(option, 0); 93839503Sborman } else { 93939503Sborman will_wont_resp[option]++; 94039503Sborman send_wont(option, 0); 94138905Sborman } 94238905Sborman } 94344364Sborman set_my_state_will(option); 94438905Sborman 94538905Sborman } /* end of dooption */ 94638905Sborman 94746809Sdab void 94839503Sborman send_wont(option, init) 94939503Sborman int option, init; 95039503Sborman { 95139503Sborman if (init) { 95244364Sborman if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || 95344364Sborman my_want_state_is_wont(option)) 95439503Sborman return; 95544364Sborman set_my_want_state_wont(option); 95639503Sborman will_wont_resp[option]++; 95739503Sborman } 958*65158Sdab (void) sprintf(nfrontp, (char *)wont, option); 95939503Sborman nfrontp += sizeof (wont) - 2; 96046809Sdab 96146809Sdab DIAG(TD_OPTIONS, printoption("td: send wont", option)); 96239503Sborman } 96338905Sborman 96446809Sdab void 96539503Sborman dontoption(option) 96639503Sborman int option; 96738905Sborman { 96838905Sborman /* 96938905Sborman * Process client input. 97038905Sborman */ 97140242Sborman 97246809Sdab 97346809Sdab DIAG(TD_OPTIONS, printoption("td: recv dont", option)); 97446809Sdab 97539503Sborman if (will_wont_resp[option]) { 97639503Sborman will_wont_resp[option]--; 97744364Sborman if (will_wont_resp[option] && my_state_is_wont(option)) 97839503Sborman will_wont_resp[option]--; 97939503Sborman } 98044364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { 98138905Sborman switch (option) { 98238905Sborman case TELOPT_BINARY: 98338905Sborman init_termbuf(); 98438905Sborman tty_binaryout(0); 98538905Sborman set_termbuf(); 98638905Sborman break; 98738905Sborman 98839503Sborman case TELOPT_ECHO: /* we should stop echoing */ 98938905Sborman #ifdef LINEMODE 99045234Sborman # ifdef KLUDGELINEMODE 99157212Sdab if ((lmodetype != REAL_LINEMODE) && 99257212Sdab (lmodetype != KLUDGE_LINEMODE)) 99345234Sborman # else 99445234Sborman if (his_state_is_wont(TELOPT_LINEMODE)) 99545234Sborman # endif 99638905Sborman #endif 99745234Sborman { 99838905Sborman init_termbuf(); 99938905Sborman tty_setecho(0); 100038905Sborman set_termbuf(); 100138905Sborman } 100238905Sborman break; 100338905Sborman 100438905Sborman case TELOPT_SGA: 100538905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 100638905Sborman /* 100739503Sborman * If kludge linemode is in use, then we 100839503Sborman * must process an incoming do SGA for 100939503Sborman * linemode purposes. 101038905Sborman */ 101157212Sdab if ((lmodetype == KLUDGE_LINEMODE) || 101257212Sdab (lmodetype == KLUDGE_OK)) { 101338905Sborman /* 101439503Sborman * The client is asking us to turn 101539503Sborman * linemode on. 101638905Sborman */ 101757212Sdab lmodetype = KLUDGE_LINEMODE; 101838905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 101938905Sborman /* 102039503Sborman * If we did not turn line mode on, 102139503Sborman * then what do we say? Will SGA? 102239503Sborman * This violates design of telnet. 102339503Sborman * Gross. Very Gross. 102438905Sborman */ 102538905Sborman } 102645234Sborman break; 102745234Sborman #else 102845234Sborman set_my_want_state_wont(option); 102945234Sborman if (my_state_is_will(option)) 103045234Sborman send_wont(option, 0); 103145234Sborman set_my_state_wont(option); 103245234Sborman if (turn_on_sga ^= 1) 103357212Sdab send_will(option, 1); 103445234Sborman return; 103538905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 103638905Sborman 103738905Sborman default: 103838905Sborman break; 103938905Sborman } 104038905Sborman 104144364Sborman set_my_want_state_wont(option); 104244364Sborman if (my_state_is_will(option)) 104344364Sborman send_wont(option, 0); 104438905Sborman } 104544364Sborman set_my_state_wont(option); 104638905Sborman 104738905Sborman } /* end of dontoption */ 104838905Sborman 104958971Sdab #ifdef ENV_HACK 1050*65158Sdab int env_ovar = -1; 1051*65158Sdab int env_ovalue = -1; 105258971Sdab #else /* ENV_HACK */ 1053*65158Sdab # define env_ovar OLD_ENV_VAR 1054*65158Sdab # define env_ovalue OLD_ENV_VALUE 105558971Sdab #endif /* ENV_HACK */ 105658971Sdab 105738905Sborman /* 105838905Sborman * suboption() 105938905Sborman * 106038905Sborman * Look at the sub-option buffer, and try to be helpful to the other 106138905Sborman * side. 106238905Sborman * 106338905Sborman * Currently we recognize: 106438905Sborman * 106538905Sborman * Terminal type is 106638905Sborman * Linemode 106738905Sborman * Window size 106838905Sborman * Terminal speed 106938905Sborman */ 107046809Sdab void 107138905Sborman suboption() 107238905Sborman { 107338905Sborman register int subchar; 107438905Sborman 107546809Sdab DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); 107646809Sdab 107738905Sborman subchar = SB_GET(); 107838905Sborman switch (subchar) { 107938905Sborman case TELOPT_TSPEED: { 108038905Sborman register int xspeed, rspeed; 108138905Sborman 108244364Sborman if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ 108338905Sborman break; 108438905Sborman 108538905Sborman settimer(tspeedsubopt); 108638905Sborman 108738905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 108838905Sborman return; 108938905Sborman 109046809Sdab xspeed = atoi((char *)subpointer); 109138905Sborman 109238905Sborman while (SB_GET() != ',' && !SB_EOF()); 109338905Sborman if (SB_EOF()) 109438905Sborman return; 109538905Sborman 109646809Sdab rspeed = atoi((char *)subpointer); 109738905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 109838905Sborman 109938905Sborman break; 110038905Sborman 110138905Sborman } /* end of case TELOPT_TSPEED */ 110238905Sborman 110338905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 110444364Sborman static char terminalname[41]; 110538905Sborman 110644364Sborman if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ 110738905Sborman break; 110838905Sborman settimer(ttypesubopt); 110938905Sborman 111046809Sdab if (SB_EOF() || SB_GET() != TELQUAL_IS) { 111138905Sborman return; /* ??? XXX but, this is the most robust */ 111238905Sborman } 111338905Sborman 111444364Sborman terminaltype = terminalname; 111538905Sborman 111638905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 111738905Sborman !SB_EOF()) { 111838905Sborman register int c; 111938905Sborman 112038905Sborman c = SB_GET(); 112138905Sborman if (isupper(c)) { 112238905Sborman c = tolower(c); 112338905Sborman } 112438905Sborman *terminaltype++ = c; /* accumulate name */ 112538905Sborman } 112638905Sborman *terminaltype = 0; 112738905Sborman terminaltype = terminalname; 112838905Sborman break; 112938905Sborman } /* end of case TELOPT_TTYPE */ 113038905Sborman 113138905Sborman case TELOPT_NAWS: { 113238905Sborman register int xwinsize, ywinsize; 113338905Sborman 113444364Sborman if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ 113538905Sborman break; 113638905Sborman 113738905Sborman if (SB_EOF()) 113838905Sborman return; 113938905Sborman xwinsize = SB_GET() << 8; 114038905Sborman if (SB_EOF()) 114138905Sborman return; 114238905Sborman xwinsize |= SB_GET(); 114338905Sborman if (SB_EOF()) 114438905Sborman return; 114538905Sborman ywinsize = SB_GET() << 8; 114638905Sborman if (SB_EOF()) 114738905Sborman return; 114838905Sborman ywinsize |= SB_GET(); 114938905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 115038905Sborman 115138905Sborman break; 115238905Sborman 115338905Sborman } /* end of case TELOPT_NAWS */ 115438905Sborman 115538905Sborman #ifdef LINEMODE 115638905Sborman case TELOPT_LINEMODE: { 115738905Sborman register int request; 115838905Sborman 115944364Sborman if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ 116038905Sborman break; 116138905Sborman /* 116238905Sborman * Process linemode suboptions. 116338905Sborman */ 116446809Sdab if (SB_EOF()) 116546809Sdab break; /* garbage was sent */ 116646809Sdab request = SB_GET(); /* get will/wont */ 116738905Sborman 116846809Sdab if (SB_EOF()) 116946809Sdab break; /* another garbage check */ 117046809Sdab 117138905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 117238905Sborman /* 117338905Sborman * Process suboption buffer of slc's 117438905Sborman */ 117538905Sborman start_slc(1); 117638905Sborman do_opt_slc(subpointer, subend - subpointer); 117746809Sdab (void) end_slc(0); 117846809Sdab break; 117938905Sborman } else if (request == LM_MODE) { 118046809Sdab if (SB_EOF()) 118146809Sdab return; 118238905Sborman useeditmode = SB_GET(); /* get mode flag */ 118338905Sborman clientstat(LM_MODE, 0, 0); 118446809Sdab break; 118538905Sborman } 118638905Sborman 118746809Sdab if (SB_EOF()) 118846809Sdab break; 118938905Sborman switch (SB_GET()) { /* what suboption? */ 119038905Sborman case LM_FORWARDMASK: 119138905Sborman /* 119238905Sborman * According to spec, only server can send request for 119338905Sborman * forwardmask, and client can only return a positive response. 119438905Sborman * So don't worry about it. 119538905Sborman */ 119638905Sborman 119738905Sborman default: 119838905Sborman break; 119938905Sborman } 120040242Sborman break; 120138905Sborman } /* end of case TELOPT_LINEMODE */ 120238905Sborman #endif 120338905Sborman case TELOPT_STATUS: { 120438905Sborman int mode; 120538905Sborman 120646809Sdab if (SB_EOF()) 120746809Sdab break; 120838905Sborman mode = SB_GET(); 120938905Sborman switch (mode) { 121038905Sborman case TELQUAL_SEND: 121144364Sborman if (my_state_is_will(TELOPT_STATUS)) 121238905Sborman send_status(); 121338905Sborman break; 121438905Sborman 121538905Sborman case TELQUAL_IS: 121638905Sborman break; 121738905Sborman 121838905Sborman default: 121938905Sborman break; 122038905Sborman } 122140242Sborman break; 122240242Sborman } /* end of case TELOPT_STATUS */ 122338905Sborman 122444364Sborman case TELOPT_XDISPLOC: { 122544364Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 122644364Sborman return; 122744364Sborman settimer(xdisplocsubopt); 122844364Sborman subpointer[SB_LEN()] = '\0'; 122946809Sdab (void)setenv("DISPLAY", (char *)subpointer, 1); 123044364Sborman break; 123144364Sborman } /* end of case TELOPT_XDISPLOC */ 123244364Sborman 1233*65158Sdab #ifdef TELOPT_NEW_ENVIRON 1234*65158Sdab case TELOPT_NEW_ENVIRON: 1235*65158Sdab #endif 1236*65158Sdab case TELOPT_OLD_ENVIRON: { 123744364Sborman register int c; 123844364Sborman register char *cp, *varp, *valp; 123944364Sborman 124044364Sborman if (SB_EOF()) 124144364Sborman return; 124244364Sborman c = SB_GET(); 1243*65158Sdab if (c == TELQUAL_IS) { 1244*65158Sdab if (subchar == TELOPT_OLD_ENVIRON) 1245*65158Sdab settimer(oenvironsubopt); 1246*65158Sdab else 1247*65158Sdab settimer(environsubopt); 1248*65158Sdab } else if (c != TELQUAL_INFO) { 124944364Sborman return; 1250*65158Sdab } 125144364Sborman 1252*65158Sdab #ifdef TELOPT_NEW_ENVIRON 1253*65158Sdab if (subchar == TELOPT_NEW_ENVIRON) { 1254*65158Sdab while (!SB_EOF()) { 1255*65158Sdab c = SB_GET(); 1256*65158Sdab if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) 1257*65158Sdab break; 1258*65158Sdab } 1259*65158Sdab } else 1260*65158Sdab #endif 1261*65158Sdab { 126258971Sdab #ifdef ENV_HACK 1263*65158Sdab /* 1264*65158Sdab * We only want to do this if we haven't already decided 1265*65158Sdab * whether or not the other side has its VALUE and VAR 1266*65158Sdab * reversed. 1267*65158Sdab */ 1268*65158Sdab if (env_ovar < 0) { 126958971Sdab register int last = -1; /* invalid value */ 127058971Sdab int empty = 0; 127158971Sdab int got_var = 0, got_value = 0, got_uservar = 0; 127258971Sdab 127358971Sdab /* 127458971Sdab * The other side might have its VALUE and VAR values 127558971Sdab * reversed. To be interoperable, we need to determine 127658971Sdab * which way it is. If the first recognized character 127758971Sdab * is a VAR or VALUE, then that will tell us what 127858971Sdab * type of client it is. If the fist recognized 127958971Sdab * character is a USERVAR, then we continue scanning 128058971Sdab * the suboption looking for two consecutive 128158971Sdab * VAR or VALUE fields. We should not get two 128258971Sdab * consecutive VALUE fields, so finding two 128358971Sdab * consecutive VALUE or VAR fields will tell us 128458971Sdab * what the client is. 128558971Sdab */ 128658971Sdab SB_SAVE(); 128758971Sdab while (!SB_EOF()) { 128858971Sdab c = SB_GET(); 128958971Sdab switch(c) { 1290*65158Sdab case OLD_ENV_VAR: 1291*65158Sdab if (last < 0 || last == OLD_ENV_VAR 1292*65158Sdab || (empty && (last == OLD_ENV_VALUE))) 1293*65158Sdab goto env_ovar_ok; 129458971Sdab got_var++; 1295*65158Sdab last = OLD_ENV_VAR; 129658971Sdab break; 1297*65158Sdab case OLD_ENV_VALUE: 1298*65158Sdab if (last < 0 || last == OLD_ENV_VALUE 1299*65158Sdab || (empty && (last == OLD_ENV_VAR))) 1300*65158Sdab goto env_ovar_wrong; 130158971Sdab got_value++; 1302*65158Sdab last = OLD_ENV_VALUE; 130358971Sdab break; 130458971Sdab case ENV_USERVAR: 130558971Sdab /* count strings of USERVAR as one */ 130658971Sdab if (last != ENV_USERVAR) 130758971Sdab got_uservar++; 130858971Sdab if (empty) { 1309*65158Sdab if (last == OLD_ENV_VALUE) 1310*65158Sdab goto env_ovar_ok; 1311*65158Sdab if (last == OLD_ENV_VAR) 1312*65158Sdab goto env_ovar_wrong; 131358971Sdab } 131458971Sdab last = ENV_USERVAR; 131558971Sdab break; 131658971Sdab case ENV_ESC: 131758971Sdab if (!SB_EOF()) 131858971Sdab c = SB_GET(); 131958971Sdab /* FALL THROUGH */ 132058971Sdab default: 132158971Sdab empty = 0; 132258971Sdab continue; 132358971Sdab } 132458971Sdab empty = 1; 132558971Sdab } 132658971Sdab if (empty) { 1327*65158Sdab if (last == OLD_ENV_VALUE) 1328*65158Sdab goto env_ovar_ok; 1329*65158Sdab if (last == OLD_ENV_VAR) 1330*65158Sdab goto env_ovar_wrong; 133158971Sdab } 133258971Sdab /* 133358971Sdab * Ok, the first thing was a USERVAR, and there 133458971Sdab * are not two consecutive VAR or VALUE commands, 133558971Sdab * and none of the VAR or VALUE commands are empty. 133658971Sdab * If the client has sent us a well-formed option, 133758971Sdab * then the number of VALUEs received should always 133858971Sdab * be less than or equal to the number of VARs and 133958971Sdab * USERVARs received. 134058971Sdab * 134158971Sdab * If we got exactly as many VALUEs as VARs and 134258971Sdab * USERVARs, the client has the same definitions. 134358971Sdab * 134458971Sdab * If we got exactly as many VARs as VALUEs and 134558971Sdab * USERVARS, the client has reversed definitions. 134658971Sdab */ 134760151Sdab if (got_uservar + got_var == got_value) { 1348*65158Sdab env_ovar_ok: 1349*65158Sdab env_ovar = OLD_ENV_VAR; 1350*65158Sdab env_ovalue = OLD_ENV_VALUE; 135160151Sdab } else if (got_uservar + got_value == got_var) { 1352*65158Sdab env_ovar_wrong: 1353*65158Sdab env_ovar = OLD_ENV_VALUE; 1354*65158Sdab env_ovalue = OLD_ENV_VAR; 135558971Sdab DIAG(TD_OPTIONS, {sprintf(nfrontp, 135658971Sdab "ENVIRON VALUE and VAR are reversed!\r\n"); 135758971Sdab nfrontp += strlen(nfrontp);}); 135858971Sdab 135958971Sdab } 1360*65158Sdab } 1361*65158Sdab SB_RESTORE(); 136258971Sdab #endif 136358971Sdab 1364*65158Sdab while (!SB_EOF()) { 136557212Sdab c = SB_GET(); 1366*65158Sdab if ((c == env_ovar) || (c == ENV_USERVAR)) 136757212Sdab break; 1368*65158Sdab } 136957212Sdab } 137044364Sborman 137144364Sborman if (SB_EOF()) 137244364Sborman return; 137344364Sborman 137446809Sdab cp = varp = (char *)subpointer; 137544364Sborman valp = 0; 137644364Sborman 137744364Sborman while (!SB_EOF()) { 137858971Sdab c = SB_GET(); 1379*65158Sdab if (subchar == TELOPT_OLD_ENVIRON) { 1380*65158Sdab if (c == env_ovar) 1381*65158Sdab c = NEW_ENV_VAR; 1382*65158Sdab else if (c == env_ovalue) 1383*65158Sdab c = NEW_ENV_VALUE; 1384*65158Sdab } 138558971Sdab switch (c) { 138658971Sdab 1387*65158Sdab case NEW_ENV_VALUE: 138844364Sborman *cp = '\0'; 138946809Sdab cp = valp = (char *)subpointer; 139044364Sborman break; 139144364Sborman 1392*65158Sdab case NEW_ENV_VAR: 139357212Sdab case ENV_USERVAR: 139444364Sborman *cp = '\0'; 139544364Sborman if (valp) 139646809Sdab (void)setenv(varp, valp, 1); 139744364Sborman else 139844364Sborman unsetenv(varp); 139946809Sdab cp = varp = (char *)subpointer; 140044364Sborman valp = 0; 140144364Sborman break; 140244364Sborman 140344364Sborman case ENV_ESC: 140444364Sborman if (SB_EOF()) 140544364Sborman break; 140644364Sborman c = SB_GET(); 140744364Sborman /* FALL THROUGH */ 140844364Sborman default: 140944364Sborman *cp++ = c; 141044364Sborman break; 141144364Sborman } 141244364Sborman } 141344364Sborman *cp = '\0'; 141444364Sborman if (valp) 141546809Sdab (void)setenv(varp, valp, 1); 141644364Sborman else 141744364Sborman unsetenv(varp); 141844364Sborman break; 1419*65158Sdab } /* end of case TELOPT_NEW_ENVIRON */ 142057212Sdab #if defined(AUTHENTICATION) 142146809Sdab case TELOPT_AUTHENTICATION: 142246809Sdab if (SB_EOF()) 142346809Sdab break; 142446809Sdab switch(SB_GET()) { 142546809Sdab case TELQUAL_SEND: 142646809Sdab case TELQUAL_REPLY: 142746809Sdab /* 142846809Sdab * These are sent by us and cannot be sent by 142946809Sdab * the client. 143046809Sdab */ 143146809Sdab break; 143246809Sdab case TELQUAL_IS: 143346809Sdab auth_is(subpointer, SB_LEN()); 143446809Sdab break; 143547611Sdab case TELQUAL_NAME: 143647611Sdab auth_name(subpointer, SB_LEN()); 143747611Sdab break; 143846809Sdab } 143946809Sdab break; 144046809Sdab #endif 144160151Sdab #ifdef ENCRYPTION 144246809Sdab case TELOPT_ENCRYPT: 144346809Sdab if (SB_EOF()) 144446809Sdab break; 144546809Sdab switch(SB_GET()) { 144646809Sdab case ENCRYPT_SUPPORT: 144746809Sdab encrypt_support(subpointer, SB_LEN()); 144846809Sdab break; 144946809Sdab case ENCRYPT_IS: 145046809Sdab encrypt_is(subpointer, SB_LEN()); 145146809Sdab break; 145246809Sdab case ENCRYPT_REPLY: 145346809Sdab encrypt_reply(subpointer, SB_LEN()); 145446809Sdab break; 145546809Sdab case ENCRYPT_START: 145647611Sdab encrypt_start(subpointer, SB_LEN()); 145746809Sdab break; 145846809Sdab case ENCRYPT_END: 145946809Sdab encrypt_end(); 146046809Sdab break; 146146809Sdab case ENCRYPT_REQSTART: 146247611Sdab encrypt_request_start(subpointer, SB_LEN()); 146346809Sdab break; 146446809Sdab case ENCRYPT_REQEND: 146546809Sdab /* 146646809Sdab * We can always send an REQEND so that we cannot 146746809Sdab * get stuck encrypting. We should only get this 146846809Sdab * if we have been able to get in the correct mode 146946809Sdab * anyhow. 147046809Sdab */ 147146809Sdab encrypt_request_end(); 147246809Sdab break; 147347611Sdab case ENCRYPT_ENC_KEYID: 147447611Sdab encrypt_enc_keyid(subpointer, SB_LEN()); 147547611Sdab break; 147647611Sdab case ENCRYPT_DEC_KEYID: 147747611Sdab encrypt_dec_keyid(subpointer, SB_LEN()); 147847611Sdab break; 147946809Sdab default: 148046809Sdab break; 148146809Sdab } 148246809Sdab break; 148360151Sdab #endif /* ENCRYPTION */ 148444364Sborman 148538905Sborman default: 148638905Sborman break; 148738905Sborman } /* end of switch */ 148838905Sborman 148938905Sborman } /* end of suboption */ 149038905Sborman 149146809Sdab void 149246809Sdab doclientstat() 149346809Sdab { 149446809Sdab clientstat(TELOPT_LINEMODE, WILL, 0); 149546809Sdab } 149646809Sdab 149738905Sborman #define ADD(c) *ncp++ = c; 149838905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 149946809Sdab void 150038905Sborman send_status() 150138905Sborman { 150244364Sborman unsigned char statusbuf[256]; 150344364Sborman register unsigned char *ncp; 150444364Sborman register unsigned char i; 150538905Sborman 150638905Sborman ncp = statusbuf; 150738905Sborman 150838905Sborman netflush(); /* get rid of anything waiting to go out */ 150938905Sborman 151038905Sborman ADD(IAC); 151138905Sborman ADD(SB); 151238905Sborman ADD(TELOPT_STATUS); 151338905Sborman ADD(TELQUAL_IS); 151438905Sborman 151546809Sdab /* 151646809Sdab * We check the want_state rather than the current state, 151746809Sdab * because if we received a DO/WILL for an option that we 151846809Sdab * don't support, and the other side didn't send a DONT/WONT 151946809Sdab * in response to our WONT/DONT, then the "state" will be 152046809Sdab * WILL/DO, and the "want_state" will be WONT/DONT. We 152146809Sdab * need to go by the latter. 152246809Sdab */ 1523*65158Sdab for (i = 0; i < (unsigned char)NTELOPTS; i++) { 152446809Sdab if (my_want_state_is_will(i)) { 152538905Sborman ADD(WILL); 152638905Sborman ADD_DATA(i); 152738905Sborman if (i == IAC) 152838905Sborman ADD(IAC); 152938905Sborman } 153046809Sdab if (his_want_state_is_will(i)) { 153138905Sborman ADD(DO); 153238905Sborman ADD_DATA(i); 153338905Sborman if (i == IAC) 153438905Sborman ADD(IAC); 153538905Sborman } 153638905Sborman } 153738905Sborman 153846809Sdab if (his_want_state_is_will(TELOPT_LFLOW)) { 153946809Sdab ADD(SB); 154046809Sdab ADD(TELOPT_LFLOW); 154157212Sdab if (flowmode) { 154257212Sdab ADD(LFLOW_ON); 154357212Sdab } else { 154457212Sdab ADD(LFLOW_OFF); 154557212Sdab } 154646809Sdab ADD(SE); 154757212Sdab 154857212Sdab if (restartany >= 0) { 154957212Sdab ADD(SB) 155057212Sdab ADD(TELOPT_LFLOW); 155157212Sdab if (restartany) { 155257212Sdab ADD(LFLOW_RESTART_ANY); 155357212Sdab } else { 155457212Sdab ADD(LFLOW_RESTART_XON); 155557212Sdab } 155657212Sdab ADD(SE) 155757212Sdab ADD(SB); 155857212Sdab } 155946809Sdab } 156046809Sdab 156138905Sborman #ifdef LINEMODE 156246809Sdab if (his_want_state_is_will(TELOPT_LINEMODE)) { 156344364Sborman unsigned char *cp, *cpe; 156438905Sborman int len; 156538905Sborman 156638905Sborman ADD(SB); 156738905Sborman ADD(TELOPT_LINEMODE); 156838905Sborman ADD(LM_MODE); 156938905Sborman ADD_DATA(editmode); 157038905Sborman if (editmode == IAC) 157138905Sborman ADD(IAC); 157238905Sborman ADD(SE); 157338905Sborman 157438905Sborman ADD(SB); 157538905Sborman ADD(TELOPT_LINEMODE); 157638905Sborman ADD(LM_SLC); 157738905Sborman start_slc(0); 157838905Sborman send_slc(); 157938905Sborman len = end_slc(&cp); 158038905Sborman for (cpe = cp + len; cp < cpe; cp++) 158138905Sborman ADD_DATA(*cp); 158238905Sborman ADD(SE); 158338905Sborman } 158438905Sborman #endif /* LINEMODE */ 158538905Sborman 158638905Sborman ADD(IAC); 158738905Sborman ADD(SE); 158838905Sborman 158938905Sborman writenet(statusbuf, ncp - statusbuf); 159038905Sborman netflush(); /* Send it on its way */ 159146809Sdab 159246809Sdab DIAG(TD_OPTIONS, 159346809Sdab {printsub('>', statusbuf, ncp - statusbuf); netflush();}); 159438905Sborman } 1595