138905Sborman /* 238905Sborman * Copyright (c) 1989 Regents of the University of California. 338905Sborman * All rights reserved. 438905Sborman * 542673Sbostic * %sccs.include.redist.c% 638905Sborman */ 738905Sborman 838905Sborman #ifndef lint 9*60151Sdab static char sccsid[] = "@(#)state.c 5.14 (Berkeley) 05/20/93"; 1038905Sborman #endif /* not lint */ 1138905Sborman 1238905Sborman #include "telnetd.h" 1357212Sdab #if defined(AUTHENTICATION) 1446809Sdab #include <libtelnet/auth.h> 1546809Sdab #endif 1638905Sborman 1738905Sborman char doopt[] = { IAC, DO, '%', 'c', 0 }; 1838905Sborman char dont[] = { IAC, DONT, '%', 'c', 0 }; 1938905Sborman char will[] = { IAC, WILL, '%', 'c', 0 }; 2038905Sborman 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--; 71*60151Sdab #ifdef ENCRYPTION 7246809Sdab if (decrypt_input) 7346809Sdab c = (*decrypt_input)(c); 74*60151Sdab #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; 103*60151Sdab #ifdef ENCRYPTION 10446809Sdab if (decrypt_input) 10546809Sdab nc = (*decrypt_input)(nc & 0xff); 106*60151Sdab #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 { 119*60151Sdab #ifdef ENCRYPTION 12046809Sdab if (decrypt_input) 12146809Sdab (void)(*decrypt_input)(-1); 122*60151Sdab #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 } 42939503Sborman (void) sprintf(nfrontp, 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(); 443*60151Sdab #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: 53244364Sborman case TELOPT_ENVIRON: 53339531Sborman changeok++; 53439531Sborman break; 53539531Sborman 53638905Sborman #ifdef LINEMODE 53738905Sborman case TELOPT_LINEMODE: 53839531Sborman # ifdef KLUDGELINEMODE 53939531Sborman /* 54039531Sborman * Note client's desire to use linemode. 54139531Sborman */ 54239531Sborman lmodetype = REAL_LINEMODE; 54339531Sborman # endif /* KLUDGELINEMODE */ 54446809Sdab func = doclientstat; 54546809Sdab changeok++; 54646809Sdab break; 54739531Sborman #endif /* LINEMODE */ 54838905Sborman 54957212Sdab #ifdef AUTHENTICATION 55046809Sdab case TELOPT_AUTHENTICATION: 55146809Sdab func = auth_request; 55246809Sdab changeok++; 55346809Sdab break; 55446809Sdab #endif 55546809Sdab 55657212Sdab #ifdef ENCRYPTION 55746809Sdab case TELOPT_ENCRYPT: 55846809Sdab func = encrypt_send_support; 55946809Sdab changeok++; 56046809Sdab break; 561*60151Sdab #endif /* ENCRYPTION */ 56246809Sdab 56338905Sborman default: 56438905Sborman break; 56538905Sborman } 56639503Sborman if (changeok) { 56744364Sborman set_his_want_state_will(option); 56839503Sborman send_do(option, 0); 56939503Sborman } else { 57039503Sborman do_dont_resp[option]++; 57139503Sborman send_dont(option, 0); 57238905Sborman } 57344364Sborman } else { 57444364Sborman /* 57544364Sborman * Option processing that should happen when 57644364Sborman * we receive conformation of a change in 57744364Sborman * state that we had requested. 57844364Sborman */ 57944364Sborman switch (option) { 58044364Sborman case TELOPT_ECHO: 58144364Sborman not42 = 0; /* looks like a 4.2 system */ 58244364Sborman /* 58344364Sborman * Egads, he responded "WILL ECHO". Turn 58444364Sborman * it off right now! 58544364Sborman */ 58644364Sborman send_dont(option, 1); 58744364Sborman /* 58844364Sborman * "WILL ECHO". Kludge upon kludge! 58944364Sborman * A 4.2 client is now echoing user input at 59044364Sborman * the tty. This is probably undesireable and 59144364Sborman * it should be stopped. The client will 59244364Sborman * respond WONT TM to the DO TM that we send to 59344364Sborman * check for kludge linemode. When the WONT TM 59444364Sborman * arrives, linemode will be turned off and a 59544364Sborman * change propogated to the pty. This change 59644364Sborman * will cause us to process the new pty state 59744364Sborman * in localstat(), which will notice that 59844364Sborman * linemode is off and send a WILL ECHO 59944364Sborman * so that we are properly in character mode and 60044364Sborman * all is well. 60144364Sborman */ 60244364Sborman break; 60344364Sborman #ifdef LINEMODE 60444364Sborman case TELOPT_LINEMODE: 60544364Sborman # ifdef KLUDGELINEMODE 60644364Sborman /* 60744364Sborman * Note client's desire to use linemode. 60844364Sborman */ 60944364Sborman lmodetype = REAL_LINEMODE; 61044364Sborman # endif /* KLUDGELINEMODE */ 61146809Sdab func = doclientstat; 61246809Sdab break; 61344364Sborman #endif /* LINEMODE */ 61446809Sdab 61557212Sdab #ifdef AUTHENTICATION 61646809Sdab case TELOPT_AUTHENTICATION: 61746809Sdab func = auth_request; 61846809Sdab break; 61946809Sdab #endif 62046809Sdab 62157212Sdab #ifdef ENCRYPTION 62246809Sdab case TELOPT_ENCRYPT: 62346809Sdab func = encrypt_send_support; 62446809Sdab break; 625*60151Sdab #endif /* ENCRYPTION */ 62657597Sdab case TELOPT_LFLOW: 62758971Sdab func = flowstat; 62857597Sdab break; 62944364Sborman } 63039531Sborman } 63138905Sborman } 63244364Sborman set_his_state_will(option); 63346809Sdab if (func) 63446809Sdab (*func)(); 63538905Sborman } /* end of willoption */ 63638905Sborman 63746809Sdab void 63839503Sborman send_dont(option, init) 63939503Sborman int option, init; 64038905Sborman { 64139503Sborman if (init) { 64244364Sborman if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || 64344364Sborman his_want_state_is_wont(option)) 64439503Sborman return; 64544364Sborman set_his_want_state_wont(option); 64639503Sborman do_dont_resp[option]++; 64739503Sborman } 64839503Sborman (void) sprintf(nfrontp, dont, option); 64939503Sborman nfrontp += sizeof (doopt) - 2; 65046809Sdab 65146809Sdab DIAG(TD_OPTIONS, printoption("td: send dont", option)); 65239503Sborman } 65339503Sborman 65446809Sdab void 65539503Sborman wontoption(option) 65639503Sborman int option; 65739503Sborman { 65838905Sborman /* 65938905Sborman * Process client input. 66038905Sborman */ 66139503Sborman 66246809Sdab DIAG(TD_OPTIONS, printoption("td: recv wont", option)); 66344364Sborman 66439503Sborman if (do_dont_resp[option]) { 66539503Sborman do_dont_resp[option]--; 66644364Sborman if (do_dont_resp[option] && his_state_is_wont(option)) 66739503Sborman do_dont_resp[option]--; 66839503Sborman } 66939531Sborman if (do_dont_resp[option] == 0) { 67044364Sborman if (his_want_state_is_will(option)) { 67139503Sborman /* it is always ok to change to negative state */ 67238905Sborman switch (option) { 67338905Sborman case TELOPT_ECHO: 67439503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 67538905Sborman break; 67638905Sborman 67738905Sborman case TELOPT_BINARY: 67838905Sborman init_termbuf(); 67938905Sborman tty_binaryin(0); 68038905Sborman set_termbuf(); 68138905Sborman break; 68238905Sborman 68338905Sborman #ifdef LINEMODE 68438905Sborman case TELOPT_LINEMODE: 68538905Sborman # ifdef KLUDGELINEMODE 68638905Sborman /* 68738905Sborman * If real linemode is supported, then client is 68838905Sborman * asking to turn linemode off. 68938905Sborman */ 69044364Sborman if (lmodetype != REAL_LINEMODE) 69144364Sborman break; 69244364Sborman lmodetype = KLUDGE_LINEMODE; 69338905Sborman # endif /* KLUDGELINEMODE */ 69444364Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 69538905Sborman break; 69646809Sdab #endif /* LINEMODE */ 69738905Sborman 69838905Sborman case TELOPT_TM: 69938905Sborman /* 70039503Sborman * If we get a WONT TM, and had sent a DO TM, 70139503Sborman * don't respond with a DONT TM, just leave it 70239503Sborman * as is. Short circut the state machine to 70339531Sborman * achive this. 70438905Sborman */ 70544364Sborman set_his_want_state_wont(TELOPT_TM); 70638905Sborman return; 70738905Sborman 70838905Sborman case TELOPT_LFLOW: 70938905Sborman /* 71039503Sborman * If we are not going to support flow control 71139503Sborman * option, then let peer know that we can't 71239503Sborman * change the flow control characters. 71338905Sborman */ 71438905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 71538905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 71638905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 71738905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 71838905Sborman break; 71938905Sborman 72057212Sdab #if defined(AUTHENTICATION) 72146809Sdab case TELOPT_AUTHENTICATION: 72246809Sdab auth_finished(0, AUTH_REJECT); 72346809Sdab break; 72446809Sdab #endif 72546809Sdab 72644364Sborman /* 72744364Sborman * For options that we might spin waiting for 72844364Sborman * sub-negotiation, if the client turns off the 72944364Sborman * option rather than responding to the request, 73044364Sborman * we have to treat it here as if we got a response 73144364Sborman * to the sub-negotiation, (by updating the timers) 73244364Sborman * so that we'll break out of the loop. 73344364Sborman */ 73444364Sborman case TELOPT_TTYPE: 73544364Sborman settimer(ttypesubopt); 73644364Sborman break; 73744364Sborman 73844364Sborman case TELOPT_TSPEED: 73944364Sborman settimer(tspeedsubopt); 74044364Sborman break; 74144364Sborman 74244364Sborman case TELOPT_XDISPLOC: 74344364Sborman settimer(xdisplocsubopt); 74444364Sborman break; 74544364Sborman 74644364Sborman case TELOPT_ENVIRON: 74744364Sborman settimer(environsubopt); 74844364Sborman break; 74944364Sborman 75038905Sborman default: 75138905Sborman break; 75238905Sborman } 75344364Sborman set_his_want_state_wont(option); 75444364Sborman if (his_state_is_will(option)) 75544364Sborman send_dont(option, 0); 75639531Sborman } else { 75739531Sborman switch (option) { 75839531Sborman case TELOPT_TM: 75939531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 76057212Sdab if (lmodetype < NO_AUTOKLUDGE) { 76139531Sborman lmodetype = NO_LINEMODE; 76239531Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 76339531Sborman send_will(TELOPT_SGA, 1); 76445234Sborman send_will(TELOPT_ECHO, 1); 76539531Sborman } 76639531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 76746809Sdab break; 76846809Sdab 76957212Sdab #if defined(AUTHENTICATION) 77046809Sdab case TELOPT_AUTHENTICATION: 77146809Sdab auth_finished(0, AUTH_REJECT); 77246809Sdab break; 77346809Sdab #endif 77439531Sborman default: 77539531Sborman break; 77639531Sborman } 77739531Sborman } 77838905Sborman } 77944364Sborman set_his_state_wont(option); 78038905Sborman 78139503Sborman } /* end of wontoption */ 78238905Sborman 78346809Sdab void 78439503Sborman send_will(option, init) 78539503Sborman int option, init; 78639503Sborman { 78739503Sborman if (init) { 78844364Sborman if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| 78944364Sborman my_want_state_is_will(option)) 79039503Sborman return; 79144364Sborman set_my_want_state_will(option); 79239503Sborman will_wont_resp[option]++; 79338905Sborman } 79439503Sborman (void) sprintf(nfrontp, will, option); 79539503Sborman nfrontp += sizeof (doopt) - 2; 79646809Sdab 79746809Sdab DIAG(TD_OPTIONS, printoption("td: send will", option)); 79839503Sborman } 79938905Sborman 80045234Sborman #if !defined(LINEMODE) || !defined(KLUDGELINEMODE) 80145234Sborman /* 80245234Sborman * When we get a DONT SGA, we will try once to turn it 80345234Sborman * back on. If the other side responds DONT SGA, we 80445234Sborman * leave it at that. This is so that when we talk to 80545234Sborman * clients that understand KLUDGELINEMODE but not LINEMODE, 80645234Sborman * we'll keep them in char-at-a-time mode. 80745234Sborman */ 80845234Sborman int turn_on_sga = 0; 80945234Sborman #endif 81045234Sborman 81146809Sdab void 81239503Sborman dooption(option) 81339503Sborman int option; 81438905Sborman { 81538905Sborman int changeok = 0; 81638905Sborman 81738905Sborman /* 81838905Sborman * Process client input. 81938905Sborman */ 82039503Sborman 82146809Sdab DIAG(TD_OPTIONS, printoption("td: recv do", option)); 82244364Sborman 82339503Sborman if (will_wont_resp[option]) { 82439503Sborman will_wont_resp[option]--; 82544364Sborman if (will_wont_resp[option] && my_state_is_will(option)) 82639503Sborman will_wont_resp[option]--; 82739503Sborman } 82844364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { 82938905Sborman switch (option) { 83038905Sborman case TELOPT_ECHO: 83138905Sborman #ifdef LINEMODE 83245234Sborman # ifdef KLUDGELINEMODE 83345234Sborman if (lmodetype == NO_LINEMODE) 83445234Sborman # else 83545234Sborman if (his_state_is_wont(TELOPT_LINEMODE)) 83645234Sborman # endif 83738905Sborman #endif 83845234Sborman { 83938905Sborman init_termbuf(); 84038905Sborman tty_setecho(1); 84138905Sborman set_termbuf(); 84238905Sborman } 84338905Sborman changeok++; 84438905Sborman break; 84538905Sborman 84638905Sborman case TELOPT_BINARY: 84738905Sborman init_termbuf(); 84838905Sborman tty_binaryout(1); 84938905Sborman set_termbuf(); 85038905Sborman changeok++; 85138905Sborman break; 85238905Sborman 85338905Sborman case TELOPT_SGA: 85438905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 85538905Sborman /* 85639503Sborman * If kludge linemode is in use, then we must 85739503Sborman * process an incoming do SGA for linemode 85839503Sborman * purposes. 85938905Sborman */ 86038905Sborman if (lmodetype == KLUDGE_LINEMODE) { 86138905Sborman /* 86239503Sborman * Receipt of "do SGA" in kludge 86339503Sborman * linemode is the peer asking us to 86439503Sborman * turn off linemode. Make note of 86539503Sborman * the request. 86638905Sborman */ 86738905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 86838905Sborman /* 86939503Sborman * If linemode did not get turned off 87039503Sborman * then don't tell peer that we did. 87139503Sborman * Breaking here forces a wont SGA to 87239503Sborman * be returned. 87338905Sborman */ 87438905Sborman if (linemode) 87538905Sborman break; 87638905Sborman } 87745234Sborman #else 87845234Sborman turn_on_sga = 0; 87938905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 88038905Sborman changeok++; 88138905Sborman break; 88238905Sborman 88338905Sborman case TELOPT_STATUS: 88438905Sborman changeok++; 88538905Sborman break; 88638905Sborman 88738905Sborman case TELOPT_TM: 88839503Sborman /* 88939503Sborman * Special case for TM. We send a WILL, but 89039503Sborman * pretend we sent a WONT. 89139503Sborman */ 89239503Sborman send_will(option, 0); 89344364Sborman set_my_want_state_wont(option); 89444364Sborman set_my_state_wont(option); 89539503Sborman return; 89639503Sborman 89746809Sdab case TELOPT_LOGOUT: 89846809Sdab /* 89946809Sdab * When we get a LOGOUT option, respond 90046809Sdab * with a WILL LOGOUT, make sure that 90146809Sdab * it gets written out to the network, 90246809Sdab * and then just go away... 90346809Sdab */ 90446809Sdab set_my_want_state_will(TELOPT_LOGOUT); 90546809Sdab send_will(TELOPT_LOGOUT, 0); 90646809Sdab set_my_state_will(TELOPT_LOGOUT); 90746809Sdab (void)netflush(); 90846809Sdab cleanup(0); 90946809Sdab /* NOT REACHED */ 91046809Sdab break; 91146809Sdab 912*60151Sdab #ifdef ENCRYPTION 91346809Sdab case TELOPT_ENCRYPT: 91446809Sdab changeok++; 91546809Sdab break; 916*60151Sdab #endif /* ENCRYPTION */ 91738905Sborman case TELOPT_LINEMODE: 91838905Sborman case TELOPT_TTYPE: 91938905Sborman case TELOPT_NAWS: 92038905Sborman case TELOPT_TSPEED: 92138905Sborman case TELOPT_LFLOW: 92244364Sborman case TELOPT_XDISPLOC: 92344364Sborman case TELOPT_ENVIRON: 92438905Sborman default: 92538905Sborman break; 92638905Sborman } 92739503Sborman if (changeok) { 92844364Sborman set_my_want_state_will(option); 92939503Sborman send_will(option, 0); 93039503Sborman } else { 93139503Sborman will_wont_resp[option]++; 93239503Sborman send_wont(option, 0); 93338905Sborman } 93438905Sborman } 93544364Sborman set_my_state_will(option); 93638905Sborman 93738905Sborman } /* end of dooption */ 93838905Sborman 93946809Sdab void 94039503Sborman send_wont(option, init) 94139503Sborman int option, init; 94239503Sborman { 94339503Sborman if (init) { 94444364Sborman if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || 94544364Sborman my_want_state_is_wont(option)) 94639503Sborman return; 94744364Sborman set_my_want_state_wont(option); 94839503Sborman will_wont_resp[option]++; 94939503Sborman } 95039503Sborman (void) sprintf(nfrontp, wont, option); 95139503Sborman nfrontp += sizeof (wont) - 2; 95246809Sdab 95346809Sdab DIAG(TD_OPTIONS, printoption("td: send wont", option)); 95439503Sborman } 95538905Sborman 95646809Sdab void 95739503Sborman dontoption(option) 95839503Sborman int option; 95938905Sborman { 96038905Sborman /* 96138905Sborman * Process client input. 96238905Sborman */ 96340242Sborman 96446809Sdab 96546809Sdab DIAG(TD_OPTIONS, printoption("td: recv dont", option)); 96646809Sdab 96739503Sborman if (will_wont_resp[option]) { 96839503Sborman will_wont_resp[option]--; 96944364Sborman if (will_wont_resp[option] && my_state_is_wont(option)) 97039503Sborman will_wont_resp[option]--; 97139503Sborman } 97244364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { 97338905Sborman switch (option) { 97438905Sborman case TELOPT_BINARY: 97538905Sborman init_termbuf(); 97638905Sborman tty_binaryout(0); 97738905Sborman set_termbuf(); 97838905Sborman break; 97938905Sborman 98039503Sborman case TELOPT_ECHO: /* we should stop echoing */ 98138905Sborman #ifdef LINEMODE 98245234Sborman # ifdef KLUDGELINEMODE 98357212Sdab if ((lmodetype != REAL_LINEMODE) && 98457212Sdab (lmodetype != KLUDGE_LINEMODE)) 98545234Sborman # else 98645234Sborman if (his_state_is_wont(TELOPT_LINEMODE)) 98745234Sborman # endif 98838905Sborman #endif 98945234Sborman { 99038905Sborman init_termbuf(); 99138905Sborman tty_setecho(0); 99238905Sborman set_termbuf(); 99338905Sborman } 99438905Sborman break; 99538905Sborman 99638905Sborman case TELOPT_SGA: 99738905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 99838905Sborman /* 99939503Sborman * If kludge linemode is in use, then we 100039503Sborman * must process an incoming do SGA for 100139503Sborman * linemode purposes. 100238905Sborman */ 100357212Sdab if ((lmodetype == KLUDGE_LINEMODE) || 100457212Sdab (lmodetype == KLUDGE_OK)) { 100538905Sborman /* 100639503Sborman * The client is asking us to turn 100739503Sborman * linemode on. 100838905Sborman */ 100957212Sdab lmodetype = KLUDGE_LINEMODE; 101038905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 101138905Sborman /* 101239503Sborman * If we did not turn line mode on, 101339503Sborman * then what do we say? Will SGA? 101439503Sborman * This violates design of telnet. 101539503Sborman * Gross. Very Gross. 101638905Sborman */ 101738905Sborman } 101845234Sborman break; 101945234Sborman #else 102045234Sborman set_my_want_state_wont(option); 102145234Sborman if (my_state_is_will(option)) 102245234Sborman send_wont(option, 0); 102345234Sborman set_my_state_wont(option); 102445234Sborman if (turn_on_sga ^= 1) 102557212Sdab send_will(option, 1); 102645234Sborman return; 102738905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 102838905Sborman 102938905Sborman default: 103038905Sborman break; 103138905Sborman } 103238905Sborman 103344364Sborman set_my_want_state_wont(option); 103444364Sborman if (my_state_is_will(option)) 103544364Sborman send_wont(option, 0); 103638905Sborman } 103744364Sborman set_my_state_wont(option); 103838905Sborman 103938905Sborman } /* end of dontoption */ 104038905Sborman 104158971Sdab #ifdef ENV_HACK 104258971Sdab int env_var = -1; 104358971Sdab int env_value = -1; 104458971Sdab #else /* ENV_HACK */ 104558971Sdab # define env_var ENV_VAR 104658971Sdab # define env_value ENV_VALUE 104758971Sdab #endif /* ENV_HACK */ 104858971Sdab 104938905Sborman /* 105038905Sborman * suboption() 105138905Sborman * 105238905Sborman * Look at the sub-option buffer, and try to be helpful to the other 105338905Sborman * side. 105438905Sborman * 105538905Sborman * Currently we recognize: 105638905Sborman * 105738905Sborman * Terminal type is 105838905Sborman * Linemode 105938905Sborman * Window size 106038905Sborman * Terminal speed 106138905Sborman */ 106246809Sdab void 106338905Sborman suboption() 106438905Sborman { 106538905Sborman register int subchar; 106638905Sborman 106746809Sdab DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); 106846809Sdab 106938905Sborman subchar = SB_GET(); 107038905Sborman switch (subchar) { 107138905Sborman case TELOPT_TSPEED: { 107238905Sborman register int xspeed, rspeed; 107338905Sborman 107444364Sborman if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ 107538905Sborman break; 107638905Sborman 107738905Sborman settimer(tspeedsubopt); 107838905Sborman 107938905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 108038905Sborman return; 108138905Sborman 108246809Sdab xspeed = atoi((char *)subpointer); 108338905Sborman 108438905Sborman while (SB_GET() != ',' && !SB_EOF()); 108538905Sborman if (SB_EOF()) 108638905Sborman return; 108738905Sborman 108846809Sdab rspeed = atoi((char *)subpointer); 108938905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 109038905Sborman 109138905Sborman break; 109238905Sborman 109338905Sborman } /* end of case TELOPT_TSPEED */ 109438905Sborman 109538905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 109644364Sborman static char terminalname[41]; 109738905Sborman 109844364Sborman if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ 109938905Sborman break; 110038905Sborman settimer(ttypesubopt); 110138905Sborman 110246809Sdab if (SB_EOF() || SB_GET() != TELQUAL_IS) { 110338905Sborman return; /* ??? XXX but, this is the most robust */ 110438905Sborman } 110538905Sborman 110644364Sborman terminaltype = terminalname; 110738905Sborman 110838905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 110938905Sborman !SB_EOF()) { 111038905Sborman register int c; 111138905Sborman 111238905Sborman c = SB_GET(); 111338905Sborman if (isupper(c)) { 111438905Sborman c = tolower(c); 111538905Sborman } 111638905Sborman *terminaltype++ = c; /* accumulate name */ 111738905Sborman } 111838905Sborman *terminaltype = 0; 111938905Sborman terminaltype = terminalname; 112038905Sborman break; 112138905Sborman } /* end of case TELOPT_TTYPE */ 112238905Sborman 112338905Sborman case TELOPT_NAWS: { 112438905Sborman register int xwinsize, ywinsize; 112538905Sborman 112644364Sborman if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ 112738905Sborman break; 112838905Sborman 112938905Sborman if (SB_EOF()) 113038905Sborman return; 113138905Sborman xwinsize = SB_GET() << 8; 113238905Sborman if (SB_EOF()) 113338905Sborman return; 113438905Sborman xwinsize |= SB_GET(); 113538905Sborman if (SB_EOF()) 113638905Sborman return; 113738905Sborman ywinsize = SB_GET() << 8; 113838905Sborman if (SB_EOF()) 113938905Sborman return; 114038905Sborman ywinsize |= SB_GET(); 114138905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 114238905Sborman 114338905Sborman break; 114438905Sborman 114538905Sborman } /* end of case TELOPT_NAWS */ 114638905Sborman 114738905Sborman #ifdef LINEMODE 114838905Sborman case TELOPT_LINEMODE: { 114938905Sborman register int request; 115038905Sborman 115144364Sborman if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ 115238905Sborman break; 115338905Sborman /* 115438905Sborman * Process linemode suboptions. 115538905Sborman */ 115646809Sdab if (SB_EOF()) 115746809Sdab break; /* garbage was sent */ 115846809Sdab request = SB_GET(); /* get will/wont */ 115938905Sborman 116046809Sdab if (SB_EOF()) 116146809Sdab break; /* another garbage check */ 116246809Sdab 116338905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 116438905Sborman /* 116538905Sborman * Process suboption buffer of slc's 116638905Sborman */ 116738905Sborman start_slc(1); 116838905Sborman do_opt_slc(subpointer, subend - subpointer); 116946809Sdab (void) end_slc(0); 117046809Sdab break; 117138905Sborman } else if (request == LM_MODE) { 117246809Sdab if (SB_EOF()) 117346809Sdab return; 117438905Sborman useeditmode = SB_GET(); /* get mode flag */ 117538905Sborman clientstat(LM_MODE, 0, 0); 117646809Sdab break; 117738905Sborman } 117838905Sborman 117946809Sdab if (SB_EOF()) 118046809Sdab break; 118138905Sborman switch (SB_GET()) { /* what suboption? */ 118238905Sborman case LM_FORWARDMASK: 118338905Sborman /* 118438905Sborman * According to spec, only server can send request for 118538905Sborman * forwardmask, and client can only return a positive response. 118638905Sborman * So don't worry about it. 118738905Sborman */ 118838905Sborman 118938905Sborman default: 119038905Sborman break; 119138905Sborman } 119240242Sborman break; 119338905Sborman } /* end of case TELOPT_LINEMODE */ 119438905Sborman #endif 119538905Sborman case TELOPT_STATUS: { 119638905Sborman int mode; 119738905Sborman 119846809Sdab if (SB_EOF()) 119946809Sdab break; 120038905Sborman mode = SB_GET(); 120138905Sborman switch (mode) { 120238905Sborman case TELQUAL_SEND: 120344364Sborman if (my_state_is_will(TELOPT_STATUS)) 120438905Sborman send_status(); 120538905Sborman break; 120638905Sborman 120738905Sborman case TELQUAL_IS: 120838905Sborman break; 120938905Sborman 121038905Sborman default: 121138905Sborman break; 121238905Sborman } 121340242Sborman break; 121440242Sborman } /* end of case TELOPT_STATUS */ 121538905Sborman 121644364Sborman case TELOPT_XDISPLOC: { 121744364Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 121844364Sborman return; 121944364Sborman settimer(xdisplocsubopt); 122044364Sborman subpointer[SB_LEN()] = '\0'; 122146809Sdab (void)setenv("DISPLAY", (char *)subpointer, 1); 122244364Sborman break; 122344364Sborman } /* end of case TELOPT_XDISPLOC */ 122444364Sborman 122544364Sborman case TELOPT_ENVIRON: { 122644364Sborman register int c; 122744364Sborman register char *cp, *varp, *valp; 122844364Sborman 122944364Sborman if (SB_EOF()) 123044364Sborman return; 123144364Sborman c = SB_GET(); 123244364Sborman if (c == TELQUAL_IS) 123344364Sborman settimer(environsubopt); 123444364Sborman else if (c != TELQUAL_INFO) 123544364Sborman return; 123644364Sborman 123758971Sdab #ifdef ENV_HACK 123858971Sdab /* 123958971Sdab * We only want to do this if we haven't already decided 124058971Sdab * whether or not the other side has its VALUE and VAR 124158971Sdab * reversed. 124258971Sdab */ 124358971Sdab if (env_var < 0) { 124458971Sdab register int last = -1; /* invalid value */ 124558971Sdab int empty = 0; 124658971Sdab int got_var = 0, got_value = 0, got_uservar = 0; 124758971Sdab 124858971Sdab /* 124958971Sdab * The other side might have its VALUE and VAR values 125058971Sdab * reversed. To be interoperable, we need to determine 125158971Sdab * which way it is. If the first recognized character 125258971Sdab * is a VAR or VALUE, then that will tell us what 125358971Sdab * type of client it is. If the fist recognized 125458971Sdab * character is a USERVAR, then we continue scanning 125558971Sdab * the suboption looking for two consecutive 125658971Sdab * VAR or VALUE fields. We should not get two 125758971Sdab * consecutive VALUE fields, so finding two 125858971Sdab * consecutive VALUE or VAR fields will tell us 125958971Sdab * what the client is. 126058971Sdab */ 126158971Sdab SB_SAVE(); 126258971Sdab while (!SB_EOF()) { 126358971Sdab c = SB_GET(); 126458971Sdab switch(c) { 126558971Sdab case ENV_VAR: 126658971Sdab if (last < 0 || last == ENV_VAR 126758971Sdab || (empty && (last == ENV_VALUE))) 126858971Sdab goto env_var_ok; 126958971Sdab got_var++; 127058971Sdab last = ENV_VAR; 127158971Sdab break; 127258971Sdab case ENV_VALUE: 127358971Sdab if (last < 0 || last == ENV_VALUE 127458971Sdab || (empty && (last == ENV_VAR))) 127558971Sdab goto env_var_wrong; 127658971Sdab got_value++; 127758971Sdab last = ENV_VALUE; 127858971Sdab break; 127958971Sdab case ENV_USERVAR: 128058971Sdab /* count strings of USERVAR as one */ 128158971Sdab if (last != ENV_USERVAR) 128258971Sdab got_uservar++; 128358971Sdab if (empty) { 128458971Sdab if (last == ENV_VALUE) 128558971Sdab goto env_var_ok; 128658971Sdab if (last == ENV_VAR) 128758971Sdab goto env_var_wrong; 128858971Sdab } 128958971Sdab last = ENV_USERVAR; 129058971Sdab break; 129158971Sdab case ENV_ESC: 129258971Sdab if (!SB_EOF()) 129358971Sdab c = SB_GET(); 129458971Sdab /* FALL THROUGH */ 129558971Sdab default: 129658971Sdab empty = 0; 129758971Sdab continue; 129858971Sdab } 129958971Sdab empty = 1; 130058971Sdab } 130158971Sdab if (empty) { 130258971Sdab if (last == ENV_VALUE) 130358971Sdab goto env_var_ok; 130458971Sdab if (last == ENV_VAR) 130558971Sdab goto env_var_wrong; 130658971Sdab } 130758971Sdab /* 130858971Sdab * Ok, the first thing was a USERVAR, and there 130958971Sdab * are not two consecutive VAR or VALUE commands, 131058971Sdab * and none of the VAR or VALUE commands are empty. 131158971Sdab * If the client has sent us a well-formed option, 131258971Sdab * then the number of VALUEs received should always 131358971Sdab * be less than or equal to the number of VARs and 131458971Sdab * USERVARs received. 131558971Sdab * 131658971Sdab * If we got exactly as many VALUEs as VARs and 131758971Sdab * USERVARs, the client has the same definitions. 131858971Sdab * 131958971Sdab * If we got exactly as many VARs as VALUEs and 132058971Sdab * USERVARS, the client has reversed definitions. 132158971Sdab */ 1322*60151Sdab if (got_uservar + got_var == got_value) { 132358971Sdab env_var_ok: 132458971Sdab env_var = ENV_VAR; 132558971Sdab env_value = ENV_VALUE; 1326*60151Sdab } else if (got_uservar + got_value == got_var) { 132758971Sdab env_var_wrong: 132858971Sdab env_var = ENV_VALUE; 132958971Sdab env_value = ENV_VAR; 133058971Sdab DIAG(TD_OPTIONS, {sprintf(nfrontp, 133158971Sdab "ENVIRON VALUE and VAR are reversed!\r\n"); 133258971Sdab nfrontp += strlen(nfrontp);}); 133358971Sdab 133458971Sdab } 133558971Sdab } 133658971Sdab SB_RESTORE(); 133758971Sdab #endif 133858971Sdab 133957212Sdab while (!SB_EOF()) { 134057212Sdab c = SB_GET(); 134158971Sdab if ((c == env_var) || (c == ENV_USERVAR)) 134257212Sdab break; 134357212Sdab } 134444364Sborman 134544364Sborman if (SB_EOF()) 134644364Sborman return; 134744364Sborman 134846809Sdab cp = varp = (char *)subpointer; 134944364Sborman valp = 0; 135044364Sborman 135144364Sborman while (!SB_EOF()) { 135258971Sdab c = SB_GET(); 135358971Sdab #ifdef ENV_HACK 135458971Sdab if (c == env_var) 135558971Sdab c = ENV_VAR; 135658971Sdab else if (c == env_value) 135758971Sdab c = ENV_VALUE; 135858971Sdab #endif 135958971Sdab switch (c) { 136058971Sdab 136144364Sborman case ENV_VALUE: 136244364Sborman *cp = '\0'; 136346809Sdab cp = valp = (char *)subpointer; 136444364Sborman break; 136544364Sborman 136644364Sborman case ENV_VAR: 136757212Sdab case ENV_USERVAR: 136844364Sborman *cp = '\0'; 136944364Sborman if (valp) 137046809Sdab (void)setenv(varp, valp, 1); 137144364Sborman else 137244364Sborman unsetenv(varp); 137346809Sdab cp = varp = (char *)subpointer; 137444364Sborman valp = 0; 137544364Sborman break; 137644364Sborman 137744364Sborman case ENV_ESC: 137844364Sborman if (SB_EOF()) 137944364Sborman break; 138044364Sborman c = SB_GET(); 138144364Sborman /* FALL THROUGH */ 138244364Sborman default: 138344364Sborman *cp++ = c; 138444364Sborman break; 138544364Sborman } 138644364Sborman } 138744364Sborman *cp = '\0'; 138844364Sborman if (valp) 138946809Sdab (void)setenv(varp, valp, 1); 139044364Sborman else 139144364Sborman unsetenv(varp); 139244364Sborman break; 139344364Sborman } /* end of case TELOPT_ENVIRON */ 139457212Sdab #if defined(AUTHENTICATION) 139546809Sdab case TELOPT_AUTHENTICATION: 139646809Sdab if (SB_EOF()) 139746809Sdab break; 139846809Sdab switch(SB_GET()) { 139946809Sdab case TELQUAL_SEND: 140046809Sdab case TELQUAL_REPLY: 140146809Sdab /* 140246809Sdab * These are sent by us and cannot be sent by 140346809Sdab * the client. 140446809Sdab */ 140546809Sdab break; 140646809Sdab case TELQUAL_IS: 140746809Sdab auth_is(subpointer, SB_LEN()); 140846809Sdab break; 140947611Sdab case TELQUAL_NAME: 141047611Sdab auth_name(subpointer, SB_LEN()); 141147611Sdab break; 141246809Sdab } 141346809Sdab break; 141446809Sdab #endif 1415*60151Sdab #ifdef ENCRYPTION 141646809Sdab case TELOPT_ENCRYPT: 141746809Sdab if (SB_EOF()) 141846809Sdab break; 141946809Sdab switch(SB_GET()) { 142046809Sdab case ENCRYPT_SUPPORT: 142146809Sdab encrypt_support(subpointer, SB_LEN()); 142246809Sdab break; 142346809Sdab case ENCRYPT_IS: 142446809Sdab encrypt_is(subpointer, SB_LEN()); 142546809Sdab break; 142646809Sdab case ENCRYPT_REPLY: 142746809Sdab encrypt_reply(subpointer, SB_LEN()); 142846809Sdab break; 142946809Sdab case ENCRYPT_START: 143047611Sdab encrypt_start(subpointer, SB_LEN()); 143146809Sdab break; 143246809Sdab case ENCRYPT_END: 143346809Sdab encrypt_end(); 143446809Sdab break; 143546809Sdab case ENCRYPT_REQSTART: 143647611Sdab encrypt_request_start(subpointer, SB_LEN()); 143746809Sdab break; 143846809Sdab case ENCRYPT_REQEND: 143946809Sdab /* 144046809Sdab * We can always send an REQEND so that we cannot 144146809Sdab * get stuck encrypting. We should only get this 144246809Sdab * if we have been able to get in the correct mode 144346809Sdab * anyhow. 144446809Sdab */ 144546809Sdab encrypt_request_end(); 144646809Sdab break; 144747611Sdab case ENCRYPT_ENC_KEYID: 144847611Sdab encrypt_enc_keyid(subpointer, SB_LEN()); 144947611Sdab break; 145047611Sdab case ENCRYPT_DEC_KEYID: 145147611Sdab encrypt_dec_keyid(subpointer, SB_LEN()); 145247611Sdab break; 145346809Sdab default: 145446809Sdab break; 145546809Sdab } 145646809Sdab break; 1457*60151Sdab #endif /* ENCRYPTION */ 145844364Sborman 145938905Sborman default: 146038905Sborman break; 146138905Sborman } /* end of switch */ 146238905Sborman 146338905Sborman } /* end of suboption */ 146438905Sborman 146546809Sdab void 146646809Sdab doclientstat() 146746809Sdab { 146846809Sdab clientstat(TELOPT_LINEMODE, WILL, 0); 146946809Sdab } 147046809Sdab 147138905Sborman #define ADD(c) *ncp++ = c; 147238905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 147346809Sdab void 147438905Sborman send_status() 147538905Sborman { 147644364Sborman unsigned char statusbuf[256]; 147744364Sborman register unsigned char *ncp; 147844364Sborman register unsigned char i; 147938905Sborman 148038905Sborman ncp = statusbuf; 148138905Sborman 148238905Sborman netflush(); /* get rid of anything waiting to go out */ 148338905Sborman 148438905Sborman ADD(IAC); 148538905Sborman ADD(SB); 148638905Sborman ADD(TELOPT_STATUS); 148738905Sborman ADD(TELQUAL_IS); 148838905Sborman 148946809Sdab /* 149046809Sdab * We check the want_state rather than the current state, 149146809Sdab * because if we received a DO/WILL for an option that we 149246809Sdab * don't support, and the other side didn't send a DONT/WONT 149346809Sdab * in response to our WONT/DONT, then the "state" will be 149446809Sdab * WILL/DO, and the "want_state" will be WONT/DONT. We 149546809Sdab * need to go by the latter. 149646809Sdab */ 149738905Sborman for (i = 0; i < NTELOPTS; i++) { 149846809Sdab if (my_want_state_is_will(i)) { 149938905Sborman ADD(WILL); 150038905Sborman ADD_DATA(i); 150138905Sborman if (i == IAC) 150238905Sborman ADD(IAC); 150338905Sborman } 150446809Sdab if (his_want_state_is_will(i)) { 150538905Sborman ADD(DO); 150638905Sborman ADD_DATA(i); 150738905Sborman if (i == IAC) 150838905Sborman ADD(IAC); 150938905Sborman } 151038905Sborman } 151138905Sborman 151246809Sdab if (his_want_state_is_will(TELOPT_LFLOW)) { 151346809Sdab ADD(SB); 151446809Sdab ADD(TELOPT_LFLOW); 151557212Sdab if (flowmode) { 151657212Sdab ADD(LFLOW_ON); 151757212Sdab } else { 151857212Sdab ADD(LFLOW_OFF); 151957212Sdab } 152046809Sdab ADD(SE); 152157212Sdab 152257212Sdab if (restartany >= 0) { 152357212Sdab ADD(SB) 152457212Sdab ADD(TELOPT_LFLOW); 152557212Sdab if (restartany) { 152657212Sdab ADD(LFLOW_RESTART_ANY); 152757212Sdab } else { 152857212Sdab ADD(LFLOW_RESTART_XON); 152957212Sdab } 153057212Sdab ADD(SE) 153157212Sdab ADD(SB); 153257212Sdab } 153346809Sdab } 153446809Sdab 153538905Sborman #ifdef LINEMODE 153646809Sdab if (his_want_state_is_will(TELOPT_LINEMODE)) { 153744364Sborman unsigned char *cp, *cpe; 153838905Sborman int len; 153938905Sborman 154038905Sborman ADD(SB); 154138905Sborman ADD(TELOPT_LINEMODE); 154238905Sborman ADD(LM_MODE); 154338905Sborman ADD_DATA(editmode); 154438905Sborman if (editmode == IAC) 154538905Sborman ADD(IAC); 154638905Sborman ADD(SE); 154738905Sborman 154838905Sborman ADD(SB); 154938905Sborman ADD(TELOPT_LINEMODE); 155038905Sborman ADD(LM_SLC); 155138905Sborman start_slc(0); 155238905Sborman send_slc(); 155338905Sborman len = end_slc(&cp); 155438905Sborman for (cpe = cp + len; cp < cpe; cp++) 155538905Sborman ADD_DATA(*cp); 155638905Sborman ADD(SE); 155738905Sborman } 155838905Sborman #endif /* LINEMODE */ 155938905Sborman 156038905Sborman ADD(IAC); 156138905Sborman ADD(SE); 156238905Sborman 156338905Sborman writenet(statusbuf, ncp - statusbuf); 156438905Sborman netflush(); /* Send it on its way */ 156546809Sdab 156646809Sdab DIAG(TD_OPTIONS, 156746809Sdab {printsub('>', statusbuf, ncp - statusbuf); netflush();}); 156838905Sborman } 1569