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*57597Sdab static char sccsid[] = "@(#)state.c 5.12 (Berkeley) 01/19/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 2938905Sborman #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 3838905Sborman 3938905Sborman 4038905Sborman /* 4138905Sborman * State for recv fsm 4238905Sborman */ 4338905Sborman #define TS_DATA 0 /* base state */ 4438905Sborman #define TS_IAC 1 /* look for double IAC's */ 4538905Sborman #define TS_CR 2 /* CR-LF ->'s CR */ 4638905Sborman #define TS_SB 3 /* throw away begin's... */ 4738905Sborman #define TS_SE 4 /* ...end's (suboption negotiation) */ 4838905Sborman #define TS_WILL 5 /* will option negotiation */ 4938905Sborman #define TS_WONT 6 /* wont " */ 5038905Sborman #define TS_DO 7 /* do " */ 5138905Sborman #define TS_DONT 8 /* dont " */ 5238905Sborman 5346809Sdab void 5438905Sborman telrcv() 5538905Sborman { 5638905Sborman register int c; 5738905Sborman static int state = TS_DATA; 5840242Sborman #if defined(CRAY2) && defined(UNICOS5) 5938905Sborman char *opfrontp = pfrontp; 6038905Sborman #endif 6138905Sborman 6238905Sborman while (ncc > 0) { 6338905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6438905Sborman break; 6538905Sborman c = *netip++ & 0377, ncc--; 6657212Sdab #if defined(ENCRYPTION) 6746809Sdab if (decrypt_input) 6846809Sdab c = (*decrypt_input)(c); 6946809Sdab #endif 7038905Sborman switch (state) { 7138905Sborman 7238905Sborman case TS_CR: 7338905Sborman state = TS_DATA; 7438905Sborman /* Strip off \n or \0 after a \r */ 7538905Sborman if ((c == 0) || (c == '\n')) { 7638905Sborman break; 7738905Sborman } 7838905Sborman /* FALL THROUGH */ 7938905Sborman 8038905Sborman case TS_DATA: 8138905Sborman if (c == IAC) { 8238905Sborman state = TS_IAC; 8338905Sborman break; 8438905Sborman } 8538905Sborman /* 8638905Sborman * We now map \r\n ==> \r for pragmatic reasons. 8738905Sborman * Many client implementations send \r\n when 8838905Sborman * the user hits the CarriageReturn key. 8938905Sborman * 9038905Sborman * We USED to map \r\n ==> \n, since \r\n says 9138905Sborman * that we want to be in column 1 of the next 9238905Sborman * printable line, and \n is the standard 9338905Sborman * unix way of saying that (\r is only good 9438905Sborman * if CRMOD is set, which it normally is). 9538905Sborman */ 9644364Sborman if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { 9746809Sdab int nc = *netip; 9857212Sdab #if defined(ENCRYPTION) 9946809Sdab if (decrypt_input) 10046809Sdab nc = (*decrypt_input)(nc & 0xff); 10146809Sdab #endif 10246809Sdab #ifdef LINEMODE 10338905Sborman /* 10438905Sborman * If we are operating in linemode, 10538905Sborman * convert to local end-of-line. 10638905Sborman */ 10746809Sdab if (linemode && (ncc > 0) && (('\n' == nc) || 10846809Sdab ((0 == nc) && tty_iscrnl())) ) { 10938905Sborman netip++; ncc--; 11038905Sborman c = '\n'; 11146809Sdab } else 11246809Sdab #endif 11346809Sdab { 11457212Sdab #if defined(ENCRYPTION) 11546809Sdab if (decrypt_input) 11646809Sdab (void)(*decrypt_input)(-1); 11746809Sdab #endif 11838905Sborman state = TS_CR; 11938905Sborman } 12038905Sborman } 12138905Sborman *pfrontp++ = c; 12238905Sborman break; 12338905Sborman 12438905Sborman case TS_IAC: 12538905Sborman gotiac: switch (c) { 12638905Sborman 12738905Sborman /* 12838905Sborman * Send the process on the pty side an 12938905Sborman * interrupt. Do this with a NULL or 13038905Sborman * interrupt char; depending on the tty mode. 13138905Sborman */ 13238905Sborman case IP: 13346809Sdab DIAG(TD_OPTIONS, 13446809Sdab printoption("td: recv IAC", c)); 13538905Sborman interrupt(); 13638905Sborman break; 13738905Sborman 13838905Sborman case BREAK: 13946809Sdab DIAG(TD_OPTIONS, 14046809Sdab printoption("td: recv IAC", c)); 14138905Sborman sendbrk(); 14238905Sborman break; 14338905Sborman 14438905Sborman /* 14538905Sborman * Are You There? 14638905Sborman */ 14738905Sborman case AYT: 14846809Sdab DIAG(TD_OPTIONS, 14946809Sdab printoption("td: recv IAC", c)); 15045234Sborman recv_ayt(); 15138905Sborman break; 15238905Sborman 15338905Sborman /* 15438905Sborman * Abort Output 15538905Sborman */ 15638905Sborman case AO: 15738905Sborman { 15846809Sdab DIAG(TD_OPTIONS, 15946809Sdab printoption("td: recv IAC", c)); 16038905Sborman ptyflush(); /* half-hearted */ 16138905Sborman init_termbuf(); 16238905Sborman 16338905Sborman if (slctab[SLC_AO].sptr && 16445234Sborman *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { 16540242Sborman *pfrontp++ = 16640242Sborman (unsigned char)*slctab[SLC_AO].sptr; 16738905Sborman } 16838905Sborman 16938905Sborman netclear(); /* clear buffer back */ 17038905Sborman *nfrontp++ = IAC; 17138905Sborman *nfrontp++ = DM; 17238905Sborman neturg = nfrontp-1; /* off by one XXX */ 17346809Sdab DIAG(TD_OPTIONS, 17446809Sdab printoption("td: send IAC", DM)); 17538905Sborman break; 17638905Sborman } 17738905Sborman 17838905Sborman /* 17938905Sborman * Erase Character and 18038905Sborman * Erase Line 18138905Sborman */ 18238905Sborman case EC: 18338905Sborman case EL: 18438905Sborman { 18540242Sborman cc_t ch; 18638905Sborman 18746809Sdab DIAG(TD_OPTIONS, 18846809Sdab printoption("td: recv IAC", c)); 18938905Sborman ptyflush(); /* half-hearted */ 19038905Sborman init_termbuf(); 19144364Sborman if (c == EC) 19244364Sborman ch = *slctab[SLC_EC].sptr; 19344364Sborman else 19444364Sborman ch = *slctab[SLC_EL].sptr; 19545234Sborman if (ch != (cc_t)(_POSIX_VDISABLE)) 19640242Sborman *pfrontp++ = (unsigned char)ch; 19738905Sborman break; 19838905Sborman } 19938905Sborman 20038905Sborman /* 20138905Sborman * Check for urgent data... 20238905Sborman */ 20338905Sborman case DM: 20446809Sdab DIAG(TD_OPTIONS, 20546809Sdab printoption("td: recv IAC", c)); 20638905Sborman SYNCHing = stilloob(net); 20738905Sborman settimer(gotDM); 20838905Sborman break; 20938905Sborman 21038905Sborman 21138905Sborman /* 21238905Sborman * Begin option subnegotiation... 21338905Sborman */ 21438905Sborman case SB: 21538905Sborman state = TS_SB; 21638905Sborman SB_CLEAR(); 21738905Sborman continue; 21838905Sborman 21938905Sborman case WILL: 22038905Sborman state = TS_WILL; 22138905Sborman continue; 22238905Sborman 22338905Sborman case WONT: 22438905Sborman state = TS_WONT; 22538905Sborman continue; 22638905Sborman 22738905Sborman case DO: 22838905Sborman state = TS_DO; 22938905Sborman continue; 23038905Sborman 23138905Sborman case DONT: 23238905Sborman state = TS_DONT; 23338905Sborman continue; 23438905Sborman case EOR: 23544364Sborman if (his_state_is_will(TELOPT_EOR)) 23638905Sborman doeof(); 23738905Sborman break; 23838905Sborman 23938905Sborman /* 24038905Sborman * Handle RFC 10xx Telnet linemode option additions 24138905Sborman * to command stream (EOF, SUSP, ABORT). 24238905Sborman */ 24338905Sborman case xEOF: 24438905Sborman doeof(); 24538905Sborman break; 24638905Sborman 24738905Sborman case SUSP: 24838905Sborman sendsusp(); 24938905Sborman break; 25038905Sborman 25138905Sborman case ABORT: 25238905Sborman sendbrk(); 25338905Sborman break; 25438905Sborman 25538905Sborman case IAC: 25638905Sborman *pfrontp++ = c; 25738905Sborman break; 25838905Sborman } 25938905Sborman state = TS_DATA; 26038905Sborman break; 26138905Sborman 26238905Sborman case TS_SB: 26338905Sborman if (c == IAC) { 26438905Sborman state = TS_SE; 26538905Sborman } else { 26638905Sborman SB_ACCUM(c); 26738905Sborman } 26838905Sborman break; 26938905Sborman 27038905Sborman case TS_SE: 27138905Sborman if (c != SE) { 27238905Sborman if (c != IAC) { 27338905Sborman /* 27438905Sborman * bad form of suboption negotiation. 27538905Sborman * handle it in such a way as to avoid 27638905Sborman * damage to local state. Parse 27738905Sborman * suboption buffer found so far, 27838905Sborman * then treat remaining stream as 27938905Sborman * another command sequence. 28038905Sborman */ 28146809Sdab 28246809Sdab /* for DIAGNOSTICS */ 28344364Sborman SB_ACCUM(IAC); 28444364Sborman SB_ACCUM(c); 28544364Sborman subpointer -= 2; 28646809Sdab 28738905Sborman SB_TERM(); 28838905Sborman suboption(); 28938905Sborman state = TS_IAC; 29038905Sborman goto gotiac; 29138905Sborman } 29238905Sborman SB_ACCUM(c); 29338905Sborman state = TS_SB; 29438905Sborman } else { 29546809Sdab /* for DIAGNOSTICS */ 29644364Sborman SB_ACCUM(IAC); 29744364Sborman SB_ACCUM(SE); 29844364Sborman subpointer -= 2; 29946809Sdab 30038905Sborman SB_TERM(); 30138905Sborman suboption(); /* handle sub-option */ 30238905Sborman state = TS_DATA; 30338905Sborman } 30438905Sborman break; 30538905Sborman 30638905Sborman case TS_WILL: 30739503Sborman willoption(c); 30838905Sborman state = TS_DATA; 30938905Sborman continue; 31038905Sborman 31138905Sborman case TS_WONT: 31239503Sborman wontoption(c); 31338905Sborman state = TS_DATA; 31438905Sborman continue; 31538905Sborman 31638905Sborman case TS_DO: 31739503Sborman dooption(c); 31838905Sborman state = TS_DATA; 31938905Sborman continue; 32038905Sborman 32138905Sborman case TS_DONT: 32239503Sborman dontoption(c); 32338905Sborman state = TS_DATA; 32438905Sborman continue; 32538905Sborman 32638905Sborman default: 32738905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 32838905Sborman printf("telnetd: panic state=%d\n", state); 32938905Sborman exit(1); 33038905Sborman } 33138905Sborman } 33240242Sborman #if defined(CRAY2) && defined(UNICOS5) 33338905Sborman if (!linemode) { 33438905Sborman char xptyobuf[BUFSIZ+NETSLOP]; 33538905Sborman char xbuf2[BUFSIZ]; 33638905Sborman register char *cp; 33738905Sborman int n = pfrontp - opfrontp, oc; 33838905Sborman bcopy(opfrontp, xptyobuf, n); 33938905Sborman pfrontp = opfrontp; 34038905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, 34138905Sborman xbuf2, &oc, BUFSIZ); 34238905Sborman for (cp = xbuf2; oc > 0; --oc) 34338905Sborman if ((*nfrontp++ = *cp++) == IAC) 34438905Sborman *nfrontp++ = IAC; 34538905Sborman } 34640242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 34738905Sborman } /* end of telrcv */ 34838905Sborman 34938905Sborman /* 35038905Sborman * The will/wont/do/dont state machines are based on Dave Borman's 35144364Sborman * Telnet option processing state machine. 35238905Sborman * 35338905Sborman * These correspond to the following states: 35438905Sborman * my_state = the last negotiated state 35538905Sborman * want_state = what I want the state to go to 35638905Sborman * want_resp = how many requests I have sent 35738905Sborman * All state defaults are negative, and resp defaults to 0. 35838905Sborman * 35938905Sborman * When initiating a request to change state to new_state: 36038905Sborman * 36138905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 36238905Sborman * do nothing; 36338905Sborman * } else { 36438905Sborman * want_state = new_state; 36538905Sborman * send new_state; 36638905Sborman * want_resp++; 36738905Sborman * } 36838905Sborman * 36938905Sborman * When receiving new_state: 37038905Sborman * 37138905Sborman * if (want_resp) { 37238905Sborman * want_resp--; 37338905Sborman * if (want_resp && (new_state == my_state)) 37438905Sborman * want_resp--; 37538905Sborman * } 37638905Sborman * if ((want_resp == 0) && (new_state != want_state)) { 37738905Sborman * if (ok_to_switch_to new_state) 37838905Sborman * want_state = new_state; 37938905Sborman * else 38038905Sborman * want_resp++; 38138905Sborman * send want_state; 38238905Sborman * } 38338905Sborman * my_state = new_state; 38438905Sborman * 38538905Sborman * Note that new_state is implied in these functions by the function itself. 38638905Sborman * will and do imply positive new_state, wont and dont imply negative. 38738905Sborman * 38838905Sborman * Finally, there is one catch. If we send a negative response to a 38938905Sborman * positive request, my_state will be the positive while want_state will 39038905Sborman * remain negative. my_state will revert to negative when the negative 39138905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells 39238905Sborman * us not only the last negotiated state, but also tells us what the peer 39338905Sborman * wants to be doing as well. It is important to understand this difference 39438905Sborman * as we may wish to be processing data streams based on our desired state 39538905Sborman * (want_state) or based on what the peer thinks the state is (my_state). 39638905Sborman * 39738905Sborman * This all works fine because if the peer sends a positive request, the data 39838905Sborman * that we receive prior to negative acknowlegment will probably be affected 39938905Sborman * by the positive state, and we can process it as such (if we can; if we 40038905Sborman * can't then it really doesn't matter). If it is that important, then the 40138905Sborman * peer probably should be buffering until this option state negotiation 40238905Sborman * is complete. 40338905Sborman * 40438905Sborman */ 40546809Sdab void 40639503Sborman send_do(option, init) 40739503Sborman int option, init; 40838905Sborman { 40939503Sborman if (init) { 41044364Sborman if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || 41144364Sborman his_want_state_is_will(option)) 41239503Sborman return; 41339531Sborman /* 41439531Sborman * Special case for TELOPT_TM: We send a DO, but pretend 41539531Sborman * that we sent a DONT, so that we can send more DOs if 41639531Sborman * we want to. 41739531Sborman */ 41839531Sborman if (option == TELOPT_TM) 41944364Sborman set_his_want_state_wont(option); 42039531Sborman else 42144364Sborman set_his_want_state_will(option); 42239503Sborman do_dont_resp[option]++; 42339503Sborman } 42439503Sborman (void) sprintf(nfrontp, doopt, option); 42539503Sborman nfrontp += sizeof (dont) - 2; 42646809Sdab 42746809Sdab DIAG(TD_OPTIONS, printoption("td: send do", option)); 42839503Sborman } 42939503Sborman 43057212Sdab #ifdef AUTHENTICATION 43146809Sdab extern void auth_request(); 43246809Sdab #endif 43346809Sdab #ifdef LINEMODE 43446809Sdab extern void doclientstat(); 43546809Sdab #endif 43657212Sdab #ifdef ENCRYPTION 43746809Sdab extern void encrypt_send_support(); 43846809Sdab #endif 43946809Sdab 44046809Sdab void 44139503Sborman willoption(option) 44239503Sborman int option; 44339503Sborman { 44438905Sborman int changeok = 0; 44546809Sdab void (*func)() = 0; 44638905Sborman 44739503Sborman /* 44839503Sborman * process input from peer. 44939503Sborman */ 45039503Sborman 45146809Sdab DIAG(TD_OPTIONS, printoption("td: recv will", option)); 45244364Sborman 45339503Sborman if (do_dont_resp[option]) { 45439503Sborman do_dont_resp[option]--; 45544364Sborman if (do_dont_resp[option] && his_state_is_will(option)) 45639503Sborman do_dont_resp[option]--; 45739503Sborman } 45839531Sborman if (do_dont_resp[option] == 0) { 45944364Sborman if (his_want_state_is_wont(option)) { 46038905Sborman switch (option) { 46138905Sborman 46238905Sborman case TELOPT_BINARY: 46338905Sborman init_termbuf(); 46438905Sborman tty_binaryin(1); 46538905Sborman set_termbuf(); 46638905Sborman changeok++; 46738905Sborman break; 46838905Sborman 46938905Sborman case TELOPT_ECHO: 47038905Sborman /* 47144364Sborman * See comments below for more info. 47238905Sborman */ 47344364Sborman not42 = 0; /* looks like a 4.2 system */ 47438905Sborman break; 47538905Sborman 47638905Sborman case TELOPT_TM: 47738905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 47838905Sborman /* 47939503Sborman * This telnetd implementation does not really 48039503Sborman * support timing marks, it just uses them to 48139503Sborman * support the kludge linemode stuff. If we 48239503Sborman * receive a will or wont TM in response to our 48339503Sborman * do TM request that may have been sent to 48439503Sborman * determine kludge linemode support, process 48539503Sborman * it, otherwise TM should get a negative 48639503Sborman * response back. 48738905Sborman */ 48838905Sborman /* 48938905Sborman * Handle the linemode kludge stuff. 49038905Sborman * If we are not currently supporting any 49138905Sborman * linemode at all, then we assume that this 49238905Sborman * is the client telling us to use kludge 49338905Sborman * linemode in response to our query. Set the 49438905Sborman * linemode type that is to be supported, note 49538905Sborman * that the client wishes to use linemode, and 49638905Sborman * eat the will TM as though it never arrived. 49738905Sborman */ 49838905Sborman if (lmodetype < KLUDGE_LINEMODE) { 49938905Sborman lmodetype = KLUDGE_LINEMODE; 50038905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 50139503Sborman send_wont(TELOPT_SGA, 1); 50257212Sdab } else if (lmodetype == NO_AUTOKLUDGE) { 50357212Sdab lmodetype = KLUDGE_OK; 50438905Sborman } 50538905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 50638905Sborman /* 50739531Sborman * We never respond to a WILL TM, and 50844364Sborman * we leave the state WONT. 50938905Sborman */ 51038905Sborman return; 51138905Sborman 51238905Sborman case TELOPT_LFLOW: 51338905Sborman /* 51439503Sborman * If we are going to support flow control 51539503Sborman * option, then don't worry peer that we can't 51639503Sborman * change the flow control characters. 51738905Sborman */ 51838905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 51938905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 52038905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 52138905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 52238905Sborman case TELOPT_TTYPE: 52338905Sborman case TELOPT_SGA: 52438905Sborman case TELOPT_NAWS: 52538905Sborman case TELOPT_TSPEED: 52644364Sborman case TELOPT_XDISPLOC: 52744364Sborman case TELOPT_ENVIRON: 52839531Sborman changeok++; 52939531Sborman break; 53039531Sborman 53138905Sborman #ifdef LINEMODE 53238905Sborman case TELOPT_LINEMODE: 53339531Sborman # ifdef KLUDGELINEMODE 53439531Sborman /* 53539531Sborman * Note client's desire to use linemode. 53639531Sborman */ 53739531Sborman lmodetype = REAL_LINEMODE; 53839531Sborman # endif /* KLUDGELINEMODE */ 53946809Sdab func = doclientstat; 54046809Sdab changeok++; 54146809Sdab break; 54239531Sborman #endif /* LINEMODE */ 54338905Sborman 54457212Sdab #ifdef AUTHENTICATION 54546809Sdab case TELOPT_AUTHENTICATION: 54646809Sdab func = auth_request; 54746809Sdab changeok++; 54846809Sdab break; 54946809Sdab #endif 55046809Sdab 55157212Sdab #ifdef ENCRYPTION 55246809Sdab case TELOPT_ENCRYPT: 55346809Sdab func = encrypt_send_support; 55446809Sdab changeok++; 55546809Sdab break; 55646809Sdab #endif 55746809Sdab 55838905Sborman default: 55938905Sborman break; 56038905Sborman } 56139503Sborman if (changeok) { 56244364Sborman set_his_want_state_will(option); 56339503Sborman send_do(option, 0); 56439503Sborman } else { 56539503Sborman do_dont_resp[option]++; 56639503Sborman send_dont(option, 0); 56738905Sborman } 56844364Sborman } else { 56944364Sborman /* 57044364Sborman * Option processing that should happen when 57144364Sborman * we receive conformation of a change in 57244364Sborman * state that we had requested. 57344364Sborman */ 57444364Sborman switch (option) { 57544364Sborman case TELOPT_ECHO: 57644364Sborman not42 = 0; /* looks like a 4.2 system */ 57744364Sborman /* 57844364Sborman * Egads, he responded "WILL ECHO". Turn 57944364Sborman * it off right now! 58044364Sborman */ 58144364Sborman send_dont(option, 1); 58244364Sborman /* 58344364Sborman * "WILL ECHO". Kludge upon kludge! 58444364Sborman * A 4.2 client is now echoing user input at 58544364Sborman * the tty. This is probably undesireable and 58644364Sborman * it should be stopped. The client will 58744364Sborman * respond WONT TM to the DO TM that we send to 58844364Sborman * check for kludge linemode. When the WONT TM 58944364Sborman * arrives, linemode will be turned off and a 59044364Sborman * change propogated to the pty. This change 59144364Sborman * will cause us to process the new pty state 59244364Sborman * in localstat(), which will notice that 59344364Sborman * linemode is off and send a WILL ECHO 59444364Sborman * so that we are properly in character mode and 59544364Sborman * all is well. 59644364Sborman */ 59744364Sborman break; 59844364Sborman #ifdef LINEMODE 59944364Sborman case TELOPT_LINEMODE: 60044364Sborman # ifdef KLUDGELINEMODE 60144364Sborman /* 60244364Sborman * Note client's desire to use linemode. 60344364Sborman */ 60444364Sborman lmodetype = REAL_LINEMODE; 60544364Sborman # endif /* KLUDGELINEMODE */ 60646809Sdab func = doclientstat; 60746809Sdab break; 60844364Sborman #endif /* LINEMODE */ 60946809Sdab 61057212Sdab #ifdef AUTHENTICATION 61146809Sdab case TELOPT_AUTHENTICATION: 61246809Sdab func = auth_request; 61346809Sdab break; 61446809Sdab #endif 61546809Sdab 61657212Sdab #ifdef ENCRYPTION 61746809Sdab case TELOPT_ENCRYPT: 61846809Sdab func = encrypt_send_support; 61946809Sdab break; 62046809Sdab #endif 621*57597Sdab case TELOPT_LFLOW: 622*57597Sdab func = localstat; 623*57597Sdab break; 62444364Sborman } 62539531Sborman } 62638905Sborman } 62744364Sborman set_his_state_will(option); 62846809Sdab if (func) 62946809Sdab (*func)(); 63038905Sborman } /* end of willoption */ 63138905Sborman 63246809Sdab void 63339503Sborman send_dont(option, init) 63439503Sborman int option, init; 63538905Sborman { 63639503Sborman if (init) { 63744364Sborman if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || 63844364Sborman his_want_state_is_wont(option)) 63939503Sborman return; 64044364Sborman set_his_want_state_wont(option); 64139503Sborman do_dont_resp[option]++; 64239503Sborman } 64339503Sborman (void) sprintf(nfrontp, dont, option); 64439503Sborman nfrontp += sizeof (doopt) - 2; 64546809Sdab 64646809Sdab DIAG(TD_OPTIONS, printoption("td: send dont", option)); 64739503Sborman } 64839503Sborman 64946809Sdab void 65039503Sborman wontoption(option) 65139503Sborman int option; 65239503Sborman { 65338905Sborman /* 65438905Sborman * Process client input. 65538905Sborman */ 65639503Sborman 65746809Sdab DIAG(TD_OPTIONS, printoption("td: recv wont", option)); 65844364Sborman 65939503Sborman if (do_dont_resp[option]) { 66039503Sborman do_dont_resp[option]--; 66144364Sborman if (do_dont_resp[option] && his_state_is_wont(option)) 66239503Sborman do_dont_resp[option]--; 66339503Sborman } 66439531Sborman if (do_dont_resp[option] == 0) { 66544364Sborman if (his_want_state_is_will(option)) { 66639503Sborman /* it is always ok to change to negative state */ 66738905Sborman switch (option) { 66838905Sborman case TELOPT_ECHO: 66939503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 67038905Sborman break; 67138905Sborman 67238905Sborman case TELOPT_BINARY: 67338905Sborman init_termbuf(); 67438905Sborman tty_binaryin(0); 67538905Sborman set_termbuf(); 67638905Sborman break; 67738905Sborman 67838905Sborman #ifdef LINEMODE 67938905Sborman case TELOPT_LINEMODE: 68038905Sborman # ifdef KLUDGELINEMODE 68138905Sborman /* 68238905Sborman * If real linemode is supported, then client is 68338905Sborman * asking to turn linemode off. 68438905Sborman */ 68544364Sborman if (lmodetype != REAL_LINEMODE) 68644364Sborman break; 68744364Sborman lmodetype = KLUDGE_LINEMODE; 68838905Sborman # endif /* KLUDGELINEMODE */ 68944364Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 69038905Sborman break; 69146809Sdab #endif /* LINEMODE */ 69238905Sborman 69338905Sborman case TELOPT_TM: 69438905Sborman /* 69539503Sborman * If we get a WONT TM, and had sent a DO TM, 69639503Sborman * don't respond with a DONT TM, just leave it 69739503Sborman * as is. Short circut the state machine to 69839531Sborman * achive this. 69938905Sborman */ 70044364Sborman set_his_want_state_wont(TELOPT_TM); 70138905Sborman return; 70238905Sborman 70338905Sborman case TELOPT_LFLOW: 70438905Sborman /* 70539503Sborman * If we are not going to support flow control 70639503Sborman * option, then let peer know that we can't 70739503Sborman * change the flow control characters. 70838905Sborman */ 70938905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 71038905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 71138905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 71238905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 71338905Sborman break; 71438905Sborman 71557212Sdab #if defined(AUTHENTICATION) 71646809Sdab case TELOPT_AUTHENTICATION: 71746809Sdab auth_finished(0, AUTH_REJECT); 71846809Sdab break; 71946809Sdab #endif 72046809Sdab 72144364Sborman /* 72244364Sborman * For options that we might spin waiting for 72344364Sborman * sub-negotiation, if the client turns off the 72444364Sborman * option rather than responding to the request, 72544364Sborman * we have to treat it here as if we got a response 72644364Sborman * to the sub-negotiation, (by updating the timers) 72744364Sborman * so that we'll break out of the loop. 72844364Sborman */ 72944364Sborman case TELOPT_TTYPE: 73044364Sborman settimer(ttypesubopt); 73144364Sborman break; 73244364Sborman 73344364Sborman case TELOPT_TSPEED: 73444364Sborman settimer(tspeedsubopt); 73544364Sborman break; 73644364Sborman 73744364Sborman case TELOPT_XDISPLOC: 73844364Sborman settimer(xdisplocsubopt); 73944364Sborman break; 74044364Sborman 74144364Sborman case TELOPT_ENVIRON: 74244364Sborman settimer(environsubopt); 74344364Sborman break; 74444364Sborman 74538905Sborman default: 74638905Sborman break; 74738905Sborman } 74844364Sborman set_his_want_state_wont(option); 74944364Sborman if (his_state_is_will(option)) 75044364Sborman send_dont(option, 0); 75139531Sborman } else { 75239531Sborman switch (option) { 75339531Sborman case TELOPT_TM: 75439531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 75557212Sdab if (lmodetype < NO_AUTOKLUDGE) { 75639531Sborman lmodetype = NO_LINEMODE; 75739531Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 75839531Sborman send_will(TELOPT_SGA, 1); 75945234Sborman send_will(TELOPT_ECHO, 1); 76039531Sborman } 76139531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 76246809Sdab break; 76346809Sdab 76457212Sdab #if defined(AUTHENTICATION) 76546809Sdab case TELOPT_AUTHENTICATION: 76646809Sdab auth_finished(0, AUTH_REJECT); 76746809Sdab break; 76846809Sdab #endif 76939531Sborman default: 77039531Sborman break; 77139531Sborman } 77239531Sborman } 77338905Sborman } 77444364Sborman set_his_state_wont(option); 77538905Sborman 77639503Sborman } /* end of wontoption */ 77738905Sborman 77846809Sdab void 77939503Sborman send_will(option, init) 78039503Sborman int option, init; 78139503Sborman { 78239503Sborman if (init) { 78344364Sborman if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| 78444364Sborman my_want_state_is_will(option)) 78539503Sborman return; 78644364Sborman set_my_want_state_will(option); 78739503Sborman will_wont_resp[option]++; 78838905Sborman } 78939503Sborman (void) sprintf(nfrontp, will, option); 79039503Sborman nfrontp += sizeof (doopt) - 2; 79146809Sdab 79246809Sdab DIAG(TD_OPTIONS, printoption("td: send will", option)); 79339503Sborman } 79438905Sborman 79545234Sborman #if !defined(LINEMODE) || !defined(KLUDGELINEMODE) 79645234Sborman /* 79745234Sborman * When we get a DONT SGA, we will try once to turn it 79845234Sborman * back on. If the other side responds DONT SGA, we 79945234Sborman * leave it at that. This is so that when we talk to 80045234Sborman * clients that understand KLUDGELINEMODE but not LINEMODE, 80145234Sborman * we'll keep them in char-at-a-time mode. 80245234Sborman */ 80345234Sborman int turn_on_sga = 0; 80445234Sborman #endif 80545234Sborman 80646809Sdab void 80739503Sborman dooption(option) 80839503Sborman int option; 80938905Sborman { 81038905Sborman int changeok = 0; 81138905Sborman 81238905Sborman /* 81338905Sborman * Process client input. 81438905Sborman */ 81539503Sborman 81646809Sdab DIAG(TD_OPTIONS, printoption("td: recv do", option)); 81744364Sborman 81839503Sborman if (will_wont_resp[option]) { 81939503Sborman will_wont_resp[option]--; 82044364Sborman if (will_wont_resp[option] && my_state_is_will(option)) 82139503Sborman will_wont_resp[option]--; 82239503Sborman } 82344364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { 82438905Sborman switch (option) { 82538905Sborman case TELOPT_ECHO: 82638905Sborman #ifdef LINEMODE 82745234Sborman # ifdef KLUDGELINEMODE 82845234Sborman if (lmodetype == NO_LINEMODE) 82945234Sborman # else 83045234Sborman if (his_state_is_wont(TELOPT_LINEMODE)) 83145234Sborman # endif 83238905Sborman #endif 83345234Sborman { 83438905Sborman init_termbuf(); 83538905Sborman tty_setecho(1); 83638905Sborman set_termbuf(); 83738905Sborman } 83838905Sborman changeok++; 83938905Sborman break; 84038905Sborman 84138905Sborman case TELOPT_BINARY: 84238905Sborman init_termbuf(); 84338905Sborman tty_binaryout(1); 84438905Sborman set_termbuf(); 84538905Sborman changeok++; 84638905Sborman break; 84738905Sborman 84838905Sborman case TELOPT_SGA: 84938905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 85038905Sborman /* 85139503Sborman * If kludge linemode is in use, then we must 85239503Sborman * process an incoming do SGA for linemode 85339503Sborman * purposes. 85438905Sborman */ 85538905Sborman if (lmodetype == KLUDGE_LINEMODE) { 85638905Sborman /* 85739503Sborman * Receipt of "do SGA" in kludge 85839503Sborman * linemode is the peer asking us to 85939503Sborman * turn off linemode. Make note of 86039503Sborman * the request. 86138905Sborman */ 86238905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 86338905Sborman /* 86439503Sborman * If linemode did not get turned off 86539503Sborman * then don't tell peer that we did. 86639503Sborman * Breaking here forces a wont SGA to 86739503Sborman * be returned. 86838905Sborman */ 86938905Sborman if (linemode) 87038905Sborman break; 87138905Sborman } 87245234Sborman #else 87345234Sborman turn_on_sga = 0; 87438905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 87538905Sborman changeok++; 87638905Sborman break; 87738905Sborman 87838905Sborman case TELOPT_STATUS: 87938905Sborman changeok++; 88038905Sborman break; 88138905Sborman 88238905Sborman case TELOPT_TM: 88339503Sborman /* 88439503Sborman * Special case for TM. We send a WILL, but 88539503Sborman * pretend we sent a WONT. 88639503Sborman */ 88739503Sborman send_will(option, 0); 88844364Sborman set_my_want_state_wont(option); 88944364Sborman set_my_state_wont(option); 89039503Sborman return; 89139503Sborman 89246809Sdab case TELOPT_LOGOUT: 89346809Sdab /* 89446809Sdab * When we get a LOGOUT option, respond 89546809Sdab * with a WILL LOGOUT, make sure that 89646809Sdab * it gets written out to the network, 89746809Sdab * and then just go away... 89846809Sdab */ 89946809Sdab set_my_want_state_will(TELOPT_LOGOUT); 90046809Sdab send_will(TELOPT_LOGOUT, 0); 90146809Sdab set_my_state_will(TELOPT_LOGOUT); 90246809Sdab (void)netflush(); 90346809Sdab cleanup(0); 90446809Sdab /* NOT REACHED */ 90546809Sdab break; 90646809Sdab 90757212Sdab #if defined(ENCRYPTION) 90846809Sdab case TELOPT_ENCRYPT: 90946809Sdab changeok++; 91046809Sdab break; 91146809Sdab #endif 91238905Sborman case TELOPT_LINEMODE: 91338905Sborman case TELOPT_TTYPE: 91438905Sborman case TELOPT_NAWS: 91538905Sborman case TELOPT_TSPEED: 91638905Sborman case TELOPT_LFLOW: 91744364Sborman case TELOPT_XDISPLOC: 91844364Sborman case TELOPT_ENVIRON: 91938905Sborman default: 92038905Sborman break; 92138905Sborman } 92239503Sborman if (changeok) { 92344364Sborman set_my_want_state_will(option); 92439503Sborman send_will(option, 0); 92539503Sborman } else { 92639503Sborman will_wont_resp[option]++; 92739503Sborman send_wont(option, 0); 92838905Sborman } 92938905Sborman } 93044364Sborman set_my_state_will(option); 93138905Sborman 93238905Sborman } /* end of dooption */ 93338905Sborman 93446809Sdab void 93539503Sborman send_wont(option, init) 93639503Sborman int option, init; 93739503Sborman { 93839503Sborman if (init) { 93944364Sborman if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || 94044364Sborman my_want_state_is_wont(option)) 94139503Sborman return; 94244364Sborman set_my_want_state_wont(option); 94339503Sborman will_wont_resp[option]++; 94439503Sborman } 94539503Sborman (void) sprintf(nfrontp, wont, option); 94639503Sborman nfrontp += sizeof (wont) - 2; 94746809Sdab 94846809Sdab DIAG(TD_OPTIONS, printoption("td: send wont", option)); 94939503Sborman } 95038905Sborman 95146809Sdab void 95239503Sborman dontoption(option) 95339503Sborman int option; 95438905Sborman { 95538905Sborman /* 95638905Sborman * Process client input. 95738905Sborman */ 95840242Sborman 95946809Sdab 96046809Sdab DIAG(TD_OPTIONS, printoption("td: recv dont", option)); 96146809Sdab 96239503Sborman if (will_wont_resp[option]) { 96339503Sborman will_wont_resp[option]--; 96444364Sborman if (will_wont_resp[option] && my_state_is_wont(option)) 96539503Sborman will_wont_resp[option]--; 96639503Sborman } 96744364Sborman if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { 96838905Sborman switch (option) { 96938905Sborman case TELOPT_BINARY: 97038905Sborman init_termbuf(); 97138905Sborman tty_binaryout(0); 97238905Sborman set_termbuf(); 97338905Sborman break; 97438905Sborman 97539503Sborman case TELOPT_ECHO: /* we should stop echoing */ 97638905Sborman #ifdef LINEMODE 97745234Sborman # ifdef KLUDGELINEMODE 97857212Sdab if ((lmodetype != REAL_LINEMODE) && 97957212Sdab (lmodetype != KLUDGE_LINEMODE)) 98045234Sborman # else 98145234Sborman if (his_state_is_wont(TELOPT_LINEMODE)) 98245234Sborman # endif 98338905Sborman #endif 98445234Sborman { 98538905Sborman init_termbuf(); 98638905Sborman tty_setecho(0); 98738905Sborman set_termbuf(); 98838905Sborman } 98938905Sborman break; 99038905Sborman 99138905Sborman case TELOPT_SGA: 99238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 99338905Sborman /* 99439503Sborman * If kludge linemode is in use, then we 99539503Sborman * must process an incoming do SGA for 99639503Sborman * linemode purposes. 99738905Sborman */ 99857212Sdab if ((lmodetype == KLUDGE_LINEMODE) || 99957212Sdab (lmodetype == KLUDGE_OK)) { 100038905Sborman /* 100139503Sborman * The client is asking us to turn 100239503Sborman * linemode on. 100338905Sborman */ 100457212Sdab lmodetype = KLUDGE_LINEMODE; 100538905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 100638905Sborman /* 100739503Sborman * If we did not turn line mode on, 100839503Sborman * then what do we say? Will SGA? 100939503Sborman * This violates design of telnet. 101039503Sborman * Gross. Very Gross. 101138905Sborman */ 101238905Sborman } 101345234Sborman break; 101445234Sborman #else 101545234Sborman set_my_want_state_wont(option); 101645234Sborman if (my_state_is_will(option)) 101745234Sborman send_wont(option, 0); 101845234Sborman set_my_state_wont(option); 101945234Sborman if (turn_on_sga ^= 1) 102057212Sdab send_will(option, 1); 102145234Sborman return; 102238905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 102338905Sborman 102438905Sborman default: 102538905Sborman break; 102638905Sborman } 102738905Sborman 102844364Sborman set_my_want_state_wont(option); 102944364Sborman if (my_state_is_will(option)) 103044364Sborman send_wont(option, 0); 103138905Sborman } 103244364Sborman set_my_state_wont(option); 103338905Sborman 103438905Sborman } /* end of dontoption */ 103538905Sborman 103638905Sborman /* 103738905Sborman * suboption() 103838905Sborman * 103938905Sborman * Look at the sub-option buffer, and try to be helpful to the other 104038905Sborman * side. 104138905Sborman * 104238905Sborman * Currently we recognize: 104338905Sborman * 104438905Sborman * Terminal type is 104538905Sborman * Linemode 104638905Sborman * Window size 104738905Sborman * Terminal speed 104838905Sborman */ 104946809Sdab void 105038905Sborman suboption() 105138905Sborman { 105238905Sborman register int subchar; 105338905Sborman 105446809Sdab DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); 105546809Sdab 105638905Sborman subchar = SB_GET(); 105738905Sborman switch (subchar) { 105838905Sborman case TELOPT_TSPEED: { 105938905Sborman register int xspeed, rspeed; 106038905Sborman 106144364Sborman if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ 106238905Sborman break; 106338905Sborman 106438905Sborman settimer(tspeedsubopt); 106538905Sborman 106638905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 106738905Sborman return; 106838905Sborman 106946809Sdab xspeed = atoi((char *)subpointer); 107038905Sborman 107138905Sborman while (SB_GET() != ',' && !SB_EOF()); 107238905Sborman if (SB_EOF()) 107338905Sborman return; 107438905Sborman 107546809Sdab rspeed = atoi((char *)subpointer); 107638905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 107738905Sborman 107838905Sborman break; 107938905Sborman 108038905Sborman } /* end of case TELOPT_TSPEED */ 108138905Sborman 108238905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 108344364Sborman static char terminalname[41]; 108438905Sborman 108544364Sborman if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ 108638905Sborman break; 108738905Sborman settimer(ttypesubopt); 108838905Sborman 108946809Sdab if (SB_EOF() || SB_GET() != TELQUAL_IS) { 109038905Sborman return; /* ??? XXX but, this is the most robust */ 109138905Sborman } 109238905Sborman 109344364Sborman terminaltype = terminalname; 109438905Sborman 109538905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 109638905Sborman !SB_EOF()) { 109738905Sborman register int c; 109838905Sborman 109938905Sborman c = SB_GET(); 110038905Sborman if (isupper(c)) { 110138905Sborman c = tolower(c); 110238905Sborman } 110338905Sborman *terminaltype++ = c; /* accumulate name */ 110438905Sborman } 110538905Sborman *terminaltype = 0; 110638905Sborman terminaltype = terminalname; 110738905Sborman break; 110838905Sborman } /* end of case TELOPT_TTYPE */ 110938905Sborman 111038905Sborman case TELOPT_NAWS: { 111138905Sborman register int xwinsize, ywinsize; 111238905Sborman 111344364Sborman if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ 111438905Sborman break; 111538905Sborman 111638905Sborman if (SB_EOF()) 111738905Sborman return; 111838905Sborman xwinsize = SB_GET() << 8; 111938905Sborman if (SB_EOF()) 112038905Sborman return; 112138905Sborman xwinsize |= SB_GET(); 112238905Sborman if (SB_EOF()) 112338905Sborman return; 112438905Sborman ywinsize = SB_GET() << 8; 112538905Sborman if (SB_EOF()) 112638905Sborman return; 112738905Sborman ywinsize |= SB_GET(); 112838905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 112938905Sborman 113038905Sborman break; 113138905Sborman 113238905Sborman } /* end of case TELOPT_NAWS */ 113338905Sborman 113438905Sborman #ifdef LINEMODE 113538905Sborman case TELOPT_LINEMODE: { 113638905Sborman register int request; 113738905Sborman 113844364Sborman if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ 113938905Sborman break; 114038905Sborman /* 114138905Sborman * Process linemode suboptions. 114238905Sborman */ 114346809Sdab if (SB_EOF()) 114446809Sdab break; /* garbage was sent */ 114546809Sdab request = SB_GET(); /* get will/wont */ 114638905Sborman 114746809Sdab if (SB_EOF()) 114846809Sdab break; /* another garbage check */ 114946809Sdab 115038905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 115138905Sborman /* 115238905Sborman * Process suboption buffer of slc's 115338905Sborman */ 115438905Sborman start_slc(1); 115538905Sborman do_opt_slc(subpointer, subend - subpointer); 115646809Sdab (void) end_slc(0); 115746809Sdab break; 115838905Sborman } else if (request == LM_MODE) { 115946809Sdab if (SB_EOF()) 116046809Sdab return; 116138905Sborman useeditmode = SB_GET(); /* get mode flag */ 116238905Sborman clientstat(LM_MODE, 0, 0); 116346809Sdab break; 116438905Sborman } 116538905Sborman 116646809Sdab if (SB_EOF()) 116746809Sdab break; 116838905Sborman switch (SB_GET()) { /* what suboption? */ 116938905Sborman case LM_FORWARDMASK: 117038905Sborman /* 117138905Sborman * According to spec, only server can send request for 117238905Sborman * forwardmask, and client can only return a positive response. 117338905Sborman * So don't worry about it. 117438905Sborman */ 117538905Sborman 117638905Sborman default: 117738905Sborman break; 117838905Sborman } 117940242Sborman break; 118038905Sborman } /* end of case TELOPT_LINEMODE */ 118138905Sborman #endif 118238905Sborman case TELOPT_STATUS: { 118338905Sborman int mode; 118438905Sborman 118546809Sdab if (SB_EOF()) 118646809Sdab break; 118738905Sborman mode = SB_GET(); 118838905Sborman switch (mode) { 118938905Sborman case TELQUAL_SEND: 119044364Sborman if (my_state_is_will(TELOPT_STATUS)) 119138905Sborman send_status(); 119238905Sborman break; 119338905Sborman 119438905Sborman case TELQUAL_IS: 119538905Sborman break; 119638905Sborman 119738905Sborman default: 119838905Sborman break; 119938905Sborman } 120040242Sborman break; 120140242Sborman } /* end of case TELOPT_STATUS */ 120238905Sborman 120344364Sborman case TELOPT_XDISPLOC: { 120444364Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 120544364Sborman return; 120644364Sborman settimer(xdisplocsubopt); 120744364Sborman subpointer[SB_LEN()] = '\0'; 120846809Sdab (void)setenv("DISPLAY", (char *)subpointer, 1); 120944364Sborman break; 121044364Sborman } /* end of case TELOPT_XDISPLOC */ 121144364Sborman 121244364Sborman case TELOPT_ENVIRON: { 121344364Sborman register int c; 121444364Sborman register char *cp, *varp, *valp; 121544364Sborman 121644364Sborman if (SB_EOF()) 121744364Sborman return; 121844364Sborman c = SB_GET(); 121944364Sborman if (c == TELQUAL_IS) 122044364Sborman settimer(environsubopt); 122144364Sborman else if (c != TELQUAL_INFO) 122244364Sborman return; 122344364Sborman 122457212Sdab while (!SB_EOF()) { 122557212Sdab c = SB_GET(); 122657212Sdab if ((c == ENV_VAR) || (c == ENV_USERVAR)) 122757212Sdab break; 122857212Sdab } 122944364Sborman 123044364Sborman if (SB_EOF()) 123144364Sborman return; 123244364Sborman 123346809Sdab cp = varp = (char *)subpointer; 123444364Sborman valp = 0; 123544364Sborman 123644364Sborman while (!SB_EOF()) { 123744364Sborman switch (c = SB_GET()) { 123844364Sborman case ENV_VALUE: 123944364Sborman *cp = '\0'; 124046809Sdab cp = valp = (char *)subpointer; 124144364Sborman break; 124244364Sborman 124344364Sborman case ENV_VAR: 124457212Sdab case ENV_USERVAR: 124544364Sborman *cp = '\0'; 124644364Sborman if (valp) 124746809Sdab (void)setenv(varp, valp, 1); 124844364Sborman else 124944364Sborman unsetenv(varp); 125046809Sdab cp = varp = (char *)subpointer; 125144364Sborman valp = 0; 125244364Sborman break; 125344364Sborman 125444364Sborman case ENV_ESC: 125544364Sborman if (SB_EOF()) 125644364Sborman break; 125744364Sborman c = SB_GET(); 125844364Sborman /* FALL THROUGH */ 125944364Sborman default: 126044364Sborman *cp++ = c; 126144364Sborman break; 126244364Sborman } 126344364Sborman } 126444364Sborman *cp = '\0'; 126544364Sborman if (valp) 126646809Sdab (void)setenv(varp, valp, 1); 126744364Sborman else 126844364Sborman unsetenv(varp); 126944364Sborman break; 127044364Sborman } /* end of case TELOPT_ENVIRON */ 127157212Sdab #if defined(AUTHENTICATION) 127246809Sdab case TELOPT_AUTHENTICATION: 127346809Sdab if (SB_EOF()) 127446809Sdab break; 127546809Sdab switch(SB_GET()) { 127646809Sdab case TELQUAL_SEND: 127746809Sdab case TELQUAL_REPLY: 127846809Sdab /* 127946809Sdab * These are sent by us and cannot be sent by 128046809Sdab * the client. 128146809Sdab */ 128246809Sdab break; 128346809Sdab case TELQUAL_IS: 128446809Sdab auth_is(subpointer, SB_LEN()); 128546809Sdab break; 128647611Sdab case TELQUAL_NAME: 128747611Sdab auth_name(subpointer, SB_LEN()); 128847611Sdab break; 128946809Sdab } 129046809Sdab break; 129146809Sdab #endif 129257212Sdab #if defined(ENCRYPTION) 129346809Sdab case TELOPT_ENCRYPT: 129446809Sdab if (SB_EOF()) 129546809Sdab break; 129646809Sdab switch(SB_GET()) { 129746809Sdab case ENCRYPT_SUPPORT: 129846809Sdab encrypt_support(subpointer, SB_LEN()); 129946809Sdab break; 130046809Sdab case ENCRYPT_IS: 130146809Sdab encrypt_is(subpointer, SB_LEN()); 130246809Sdab break; 130346809Sdab case ENCRYPT_REPLY: 130446809Sdab encrypt_reply(subpointer, SB_LEN()); 130546809Sdab break; 130646809Sdab case ENCRYPT_START: 130747611Sdab encrypt_start(subpointer, SB_LEN()); 130846809Sdab break; 130946809Sdab case ENCRYPT_END: 131046809Sdab encrypt_end(); 131146809Sdab break; 131246809Sdab case ENCRYPT_REQSTART: 131347611Sdab encrypt_request_start(subpointer, SB_LEN()); 131446809Sdab break; 131546809Sdab case ENCRYPT_REQEND: 131646809Sdab /* 131746809Sdab * We can always send an REQEND so that we cannot 131846809Sdab * get stuck encrypting. We should only get this 131946809Sdab * if we have been able to get in the correct mode 132046809Sdab * anyhow. 132146809Sdab */ 132246809Sdab encrypt_request_end(); 132346809Sdab break; 132447611Sdab case ENCRYPT_ENC_KEYID: 132547611Sdab encrypt_enc_keyid(subpointer, SB_LEN()); 132647611Sdab break; 132747611Sdab case ENCRYPT_DEC_KEYID: 132847611Sdab encrypt_dec_keyid(subpointer, SB_LEN()); 132947611Sdab break; 133046809Sdab default: 133146809Sdab break; 133246809Sdab } 133346809Sdab break; 133446809Sdab #endif 133544364Sborman 133638905Sborman default: 133738905Sborman break; 133838905Sborman } /* end of switch */ 133938905Sborman 134038905Sborman } /* end of suboption */ 134138905Sborman 134246809Sdab void 134346809Sdab doclientstat() 134446809Sdab { 134546809Sdab clientstat(TELOPT_LINEMODE, WILL, 0); 134646809Sdab } 134746809Sdab 134838905Sborman #define ADD(c) *ncp++ = c; 134938905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 135046809Sdab void 135138905Sborman send_status() 135238905Sborman { 135344364Sborman unsigned char statusbuf[256]; 135444364Sborman register unsigned char *ncp; 135544364Sborman register unsigned char i; 135638905Sborman 135738905Sborman ncp = statusbuf; 135838905Sborman 135938905Sborman netflush(); /* get rid of anything waiting to go out */ 136038905Sborman 136138905Sborman ADD(IAC); 136238905Sborman ADD(SB); 136338905Sborman ADD(TELOPT_STATUS); 136438905Sborman ADD(TELQUAL_IS); 136538905Sborman 136646809Sdab /* 136746809Sdab * We check the want_state rather than the current state, 136846809Sdab * because if we received a DO/WILL for an option that we 136946809Sdab * don't support, and the other side didn't send a DONT/WONT 137046809Sdab * in response to our WONT/DONT, then the "state" will be 137146809Sdab * WILL/DO, and the "want_state" will be WONT/DONT. We 137246809Sdab * need to go by the latter. 137346809Sdab */ 137438905Sborman for (i = 0; i < NTELOPTS; i++) { 137546809Sdab if (my_want_state_is_will(i)) { 137638905Sborman ADD(WILL); 137738905Sborman ADD_DATA(i); 137838905Sborman if (i == IAC) 137938905Sborman ADD(IAC); 138038905Sborman } 138146809Sdab if (his_want_state_is_will(i)) { 138238905Sborman ADD(DO); 138338905Sborman ADD_DATA(i); 138438905Sborman if (i == IAC) 138538905Sborman ADD(IAC); 138638905Sborman } 138738905Sborman } 138838905Sborman 138946809Sdab if (his_want_state_is_will(TELOPT_LFLOW)) { 139046809Sdab ADD(SB); 139146809Sdab ADD(TELOPT_LFLOW); 139257212Sdab if (flowmode) { 139357212Sdab ADD(LFLOW_ON); 139457212Sdab } else { 139557212Sdab ADD(LFLOW_OFF); 139657212Sdab } 139746809Sdab ADD(SE); 139857212Sdab 139957212Sdab if (restartany >= 0) { 140057212Sdab ADD(SB) 140157212Sdab ADD(TELOPT_LFLOW); 140257212Sdab if (restartany) { 140357212Sdab ADD(LFLOW_RESTART_ANY); 140457212Sdab } else { 140557212Sdab ADD(LFLOW_RESTART_XON); 140657212Sdab } 140757212Sdab ADD(SE) 140857212Sdab ADD(SB); 140957212Sdab } 141046809Sdab } 141146809Sdab 141238905Sborman #ifdef LINEMODE 141346809Sdab if (his_want_state_is_will(TELOPT_LINEMODE)) { 141444364Sborman unsigned char *cp, *cpe; 141538905Sborman int len; 141638905Sborman 141738905Sborman ADD(SB); 141838905Sborman ADD(TELOPT_LINEMODE); 141938905Sborman ADD(LM_MODE); 142038905Sborman ADD_DATA(editmode); 142138905Sborman if (editmode == IAC) 142238905Sborman ADD(IAC); 142338905Sborman ADD(SE); 142438905Sborman 142538905Sborman ADD(SB); 142638905Sborman ADD(TELOPT_LINEMODE); 142738905Sborman ADD(LM_SLC); 142838905Sborman start_slc(0); 142938905Sborman send_slc(); 143038905Sborman len = end_slc(&cp); 143138905Sborman for (cpe = cp + len; cp < cpe; cp++) 143238905Sborman ADD_DATA(*cp); 143338905Sborman ADD(SE); 143438905Sborman } 143538905Sborman #endif /* LINEMODE */ 143638905Sborman 143738905Sborman ADD(IAC); 143838905Sborman ADD(SE); 143938905Sborman 144038905Sborman writenet(statusbuf, ncp - statusbuf); 144138905Sborman netflush(); /* Send it on its way */ 144246809Sdab 144346809Sdab DIAG(TD_OPTIONS, 144446809Sdab {printsub('>', statusbuf, ncp - statusbuf); netflush();}); 144538905Sborman } 1446