138905Sborman /* 238905Sborman * Copyright (c) 1989 Regents of the University of California. 338905Sborman * All rights reserved. 438905Sborman * 5*42673Sbostic * %sccs.include.redist.c% 638905Sborman */ 738905Sborman 838905Sborman #ifndef lint 9*42673Sbostic static char sccsid[] = "@(#)state.c 5.6 (Berkeley) 06/01/90"; 1038905Sborman #endif /* not lint */ 1138905Sborman 1238905Sborman #include "telnetd.h" 1338905Sborman 1438905Sborman char doopt[] = { IAC, DO, '%', 'c', 0 }; 1538905Sborman char dont[] = { IAC, DONT, '%', 'c', 0 }; 1638905Sborman char will[] = { IAC, WILL, '%', 'c', 0 }; 1738905Sborman char wont[] = { IAC, WONT, '%', 'c', 0 }; 1838905Sborman int not42 = 1; 1938905Sborman 2038905Sborman /* 2138905Sborman * Buffer for sub-options, and macros 2238905Sborman * for suboptions buffer manipulations 2338905Sborman */ 2438905Sborman char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 2538905Sborman 2638905Sborman #define SB_CLEAR() subpointer = subbuffer; 2738905Sborman #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 2838905Sborman #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 2938905Sborman *subpointer++ = (c); \ 3038905Sborman } 3138905Sborman #define SB_GET() ((*subpointer++)&0xff) 3238905Sborman #define SB_EOF() (subpointer >= subend) 3338905Sborman 3438905Sborman 3538905Sborman 3638905Sborman /* 3738905Sborman * State for recv fsm 3838905Sborman */ 3938905Sborman #define TS_DATA 0 /* base state */ 4038905Sborman #define TS_IAC 1 /* look for double IAC's */ 4138905Sborman #define TS_CR 2 /* CR-LF ->'s CR */ 4238905Sborman #define TS_SB 3 /* throw away begin's... */ 4338905Sborman #define TS_SE 4 /* ...end's (suboption negotiation) */ 4438905Sborman #define TS_WILL 5 /* will option negotiation */ 4538905Sborman #define TS_WONT 6 /* wont " */ 4638905Sborman #define TS_DO 7 /* do " */ 4738905Sborman #define TS_DONT 8 /* dont " */ 4838905Sborman 4938905Sborman telrcv() 5038905Sborman { 5138905Sborman register int c; 5238905Sborman static int state = TS_DATA; 5340242Sborman #if defined(CRAY2) && defined(UNICOS5) 5438905Sborman char *opfrontp = pfrontp; 5538905Sborman #endif 5638905Sborman 5738905Sborman while (ncc > 0) { 5838905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 5938905Sborman break; 6038905Sborman c = *netip++ & 0377, ncc--; 6138905Sborman switch (state) { 6238905Sborman 6338905Sborman case TS_CR: 6438905Sborman state = TS_DATA; 6538905Sborman /* Strip off \n or \0 after a \r */ 6638905Sborman if ((c == 0) || (c == '\n')) { 6738905Sborman break; 6838905Sborman } 6938905Sborman /* FALL THROUGH */ 7038905Sborman 7138905Sborman case TS_DATA: 7238905Sborman if (c == IAC) { 7338905Sborman state = TS_IAC; 7438905Sborman break; 7538905Sborman } 7638905Sborman /* 7738905Sborman * We now map \r\n ==> \r for pragmatic reasons. 7838905Sborman * Many client implementations send \r\n when 7938905Sborman * the user hits the CarriageReturn key. 8038905Sborman * 8138905Sborman * We USED to map \r\n ==> \n, since \r\n says 8238905Sborman * that we want to be in column 1 of the next 8338905Sborman * printable line, and \n is the standard 8438905Sborman * unix way of saying that (\r is only good 8538905Sborman * if CRMOD is set, which it normally is). 8638905Sborman */ 8738905Sborman if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 8838905Sborman /* 8938905Sborman * If we are operating in linemode, 9038905Sborman * convert to local end-of-line. 9138905Sborman */ 9238905Sborman if ((linemode) && (ncc > 0)&&('\n' == *netip)) { 9338905Sborman netip++; ncc--; 9438905Sborman c = '\n'; 9538905Sborman } else { 9638905Sborman state = TS_CR; 9738905Sborman } 9838905Sborman } 9938905Sborman *pfrontp++ = c; 10038905Sborman break; 10138905Sborman 10238905Sborman case TS_IAC: 10338905Sborman gotiac: switch (c) { 10438905Sborman 10538905Sborman /* 10638905Sborman * Send the process on the pty side an 10738905Sborman * interrupt. Do this with a NULL or 10838905Sborman * interrupt char; depending on the tty mode. 10938905Sborman */ 11038905Sborman case IP: 11138905Sborman interrupt(); 11238905Sborman break; 11338905Sborman 11438905Sborman case BREAK: 11538905Sborman sendbrk(); 11638905Sborman break; 11738905Sborman 11838905Sborman /* 11938905Sborman * Are You There? 12038905Sborman */ 12138905Sborman case AYT: 12238905Sborman (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); 12338905Sborman nfrontp += 9; 12438905Sborman break; 12538905Sborman 12638905Sborman /* 12738905Sborman * Abort Output 12838905Sborman */ 12938905Sborman case AO: 13038905Sborman { 13138905Sborman ptyflush(); /* half-hearted */ 13238905Sborman init_termbuf(); 13338905Sborman 13438905Sborman if (slctab[SLC_AO].sptr && 13540242Sborman *slctab[SLC_AO].sptr != (cc_t)-1) { 13640242Sborman *pfrontp++ = 13740242Sborman (unsigned char)*slctab[SLC_AO].sptr; 13838905Sborman } 13938905Sborman 14038905Sborman netclear(); /* clear buffer back */ 14138905Sborman *nfrontp++ = IAC; 14238905Sborman *nfrontp++ = DM; 14338905Sborman neturg = nfrontp-1; /* off by one XXX */ 14438905Sborman break; 14538905Sborman } 14638905Sborman 14738905Sborman /* 14838905Sborman * Erase Character and 14938905Sborman * Erase Line 15038905Sborman */ 15138905Sborman case EC: 15238905Sborman case EL: 15338905Sborman { 15440242Sborman cc_t ch; 15538905Sborman 15638905Sborman ptyflush(); /* half-hearted */ 15738905Sborman init_termbuf(); 15838905Sborman ch = (c == EC) ? *slctab[SLC_EC].sptr : 15938905Sborman *slctab[SLC_EL].sptr; 16040242Sborman if (ch != (cc_t)-1) 16140242Sborman *pfrontp++ = (unsigned char)ch; 16238905Sborman break; 16338905Sborman } 16438905Sborman 16538905Sborman /* 16638905Sborman * Check for urgent data... 16738905Sborman */ 16838905Sborman case DM: 16938905Sborman SYNCHing = stilloob(net); 17038905Sborman settimer(gotDM); 17138905Sborman break; 17238905Sborman 17338905Sborman 17438905Sborman /* 17538905Sborman * Begin option subnegotiation... 17638905Sborman */ 17738905Sborman case SB: 17838905Sborman state = TS_SB; 17938905Sborman SB_CLEAR(); 18038905Sborman continue; 18138905Sborman 18238905Sborman case WILL: 18338905Sborman state = TS_WILL; 18438905Sborman continue; 18538905Sborman 18638905Sborman case WONT: 18738905Sborman state = TS_WONT; 18838905Sborman continue; 18938905Sborman 19038905Sborman case DO: 19138905Sborman state = TS_DO; 19238905Sborman continue; 19338905Sborman 19438905Sborman case DONT: 19538905Sborman state = TS_DONT; 19638905Sborman continue; 19738905Sborman case EOR: 19838905Sborman if (hisopts[TELOPT_EOR]) 19938905Sborman doeof(); 20038905Sborman break; 20138905Sborman 20238905Sborman /* 20338905Sborman * Handle RFC 10xx Telnet linemode option additions 20438905Sborman * to command stream (EOF, SUSP, ABORT). 20538905Sborman */ 20638905Sborman case xEOF: 20738905Sborman doeof(); 20838905Sborman break; 20938905Sborman 21038905Sborman case SUSP: 21138905Sborman sendsusp(); 21238905Sborman break; 21338905Sborman 21438905Sborman case ABORT: 21538905Sborman sendbrk(); 21638905Sborman break; 21738905Sborman 21838905Sborman case IAC: 21938905Sborman *pfrontp++ = c; 22038905Sborman break; 22138905Sborman } 22238905Sborman state = TS_DATA; 22338905Sborman break; 22438905Sborman 22538905Sborman case TS_SB: 22638905Sborman if (c == IAC) { 22738905Sborman state = TS_SE; 22838905Sborman } else { 22938905Sborman SB_ACCUM(c); 23038905Sborman } 23138905Sborman break; 23238905Sborman 23338905Sborman case TS_SE: 23438905Sborman if (c != SE) { 23538905Sborman if (c != IAC) { 23638905Sborman /* 23738905Sborman * bad form of suboption negotiation. 23838905Sborman * handle it in such a way as to avoid 23938905Sborman * damage to local state. Parse 24038905Sborman * suboption buffer found so far, 24138905Sborman * then treat remaining stream as 24238905Sborman * another command sequence. 24338905Sborman */ 24438905Sborman SB_TERM(); 24538905Sborman suboption(); 24638905Sborman state = TS_IAC; 24738905Sborman goto gotiac; 24838905Sborman } 24938905Sborman SB_ACCUM(c); 25038905Sborman state = TS_SB; 25138905Sborman } else { 25238905Sborman SB_TERM(); 25338905Sborman suboption(); /* handle sub-option */ 25438905Sborman state = TS_DATA; 25538905Sborman } 25638905Sborman break; 25738905Sborman 25838905Sborman case TS_WILL: 25939503Sborman willoption(c); 26038905Sborman state = TS_DATA; 26138905Sborman continue; 26238905Sborman 26338905Sborman case TS_WONT: 26439503Sborman wontoption(c); 26538905Sborman state = TS_DATA; 26638905Sborman continue; 26738905Sborman 26838905Sborman case TS_DO: 26939503Sborman dooption(c); 27038905Sborman state = TS_DATA; 27138905Sborman continue; 27238905Sborman 27338905Sborman case TS_DONT: 27439503Sborman dontoption(c); 27538905Sborman state = TS_DATA; 27638905Sborman continue; 27738905Sborman 27838905Sborman default: 27938905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 28038905Sborman printf("telnetd: panic state=%d\n", state); 28138905Sborman exit(1); 28238905Sborman } 28338905Sborman } 28440242Sborman #if defined(CRAY2) && defined(UNICOS5) 28538905Sborman if (!linemode) { 28638905Sborman char xptyobuf[BUFSIZ+NETSLOP]; 28738905Sborman char xbuf2[BUFSIZ]; 28838905Sborman register char *cp; 28938905Sborman int n = pfrontp - opfrontp, oc; 29038905Sborman bcopy(opfrontp, xptyobuf, n); 29138905Sborman pfrontp = opfrontp; 29238905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, 29338905Sborman xbuf2, &oc, BUFSIZ); 29438905Sborman for (cp = xbuf2; oc > 0; --oc) 29538905Sborman if ((*nfrontp++ = *cp++) == IAC) 29638905Sborman *nfrontp++ = IAC; 29738905Sborman } 29840242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 29938905Sborman } /* end of telrcv */ 30038905Sborman 30138905Sborman /* 30238905Sborman * The will/wont/do/dont state machines are based on Dave Borman's 30338905Sborman * Telnet option processing state machine. We keep track of the full 30438905Sborman * state of the option negotiation with the following state variables 30538905Sborman * myopts, hisopts - The last fully negotiated state for each 30638905Sborman * side of the connection. 30738905Sborman * mywants, hiswants - The state we wish to be in after a completed 30838905Sborman * negotiation. (hiswants is slightly misleading, 30938905Sborman * this is more precisely the state I want him to 31038905Sborman * be in. 31138905Sborman * resp - We count the number of requests we have sent out. 31238905Sborman * 31338905Sborman * These correspond to the following states: 31438905Sborman * my_state = the last negotiated state 31538905Sborman * want_state = what I want the state to go to 31638905Sborman * want_resp = how many requests I have sent 31738905Sborman * All state defaults are negative, and resp defaults to 0. 31838905Sborman * 31938905Sborman * When initiating a request to change state to new_state: 32038905Sborman * 32138905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 32238905Sborman * do nothing; 32338905Sborman * } else { 32438905Sborman * want_state = new_state; 32538905Sborman * send new_state; 32638905Sborman * want_resp++; 32738905Sborman * } 32838905Sborman * 32938905Sborman * When receiving new_state: 33038905Sborman * 33138905Sborman * if (want_resp) { 33238905Sborman * want_resp--; 33338905Sborman * if (want_resp && (new_state == my_state)) 33438905Sborman * want_resp--; 33538905Sborman * } 33638905Sborman * if ((want_resp == 0) && (new_state != want_state)) { 33738905Sborman * if (ok_to_switch_to new_state) 33838905Sborman * want_state = new_state; 33938905Sborman * else 34038905Sborman * want_resp++; 34138905Sborman * send want_state; 34238905Sborman * } 34338905Sborman * my_state = new_state; 34438905Sborman * 34538905Sborman * Note that new_state is implied in these functions by the function itself. 34638905Sborman * will and do imply positive new_state, wont and dont imply negative. 34738905Sborman * 34838905Sborman * Finally, there is one catch. If we send a negative response to a 34938905Sborman * positive request, my_state will be the positive while want_state will 35038905Sborman * remain negative. my_state will revert to negative when the negative 35138905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells 35238905Sborman * us not only the last negotiated state, but also tells us what the peer 35338905Sborman * wants to be doing as well. It is important to understand this difference 35438905Sborman * as we may wish to be processing data streams based on our desired state 35538905Sborman * (want_state) or based on what the peer thinks the state is (my_state). 35638905Sborman * 35738905Sborman * This all works fine because if the peer sends a positive request, the data 35838905Sborman * that we receive prior to negative acknowlegment will probably be affected 35938905Sborman * by the positive state, and we can process it as such (if we can; if we 36038905Sborman * can't then it really doesn't matter). If it is that important, then the 36138905Sborman * peer probably should be buffering until this option state negotiation 36238905Sborman * is complete. 36338905Sborman * 36438905Sborman */ 36539503Sborman send_do(option, init) 36639503Sborman int option, init; 36738905Sborman { 36839503Sborman if (init) { 36939503Sborman if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) || 37039503Sborman hiswants[option] == OPT_YES) 37139503Sborman return; 37239531Sborman /* 37339531Sborman * Special case for TELOPT_TM: We send a DO, but pretend 37439531Sborman * that we sent a DONT, so that we can send more DOs if 37539531Sborman * we want to. 37639531Sborman */ 37739531Sborman if (option == TELOPT_TM) 37839531Sborman hiswants[option] = OPT_NO; 37939531Sborman else 38039531Sborman hiswants[option] = OPT_YES; 38139503Sborman do_dont_resp[option]++; 38239503Sborman } 38339503Sborman (void) sprintf(nfrontp, doopt, option); 38439503Sborman nfrontp += sizeof (dont) - 2; 38539503Sborman } 38639503Sborman 38739503Sborman willoption(option) 38839503Sborman int option; 38939503Sborman { 39038905Sborman int changeok = 0; 39138905Sborman 39239503Sborman /* 39339503Sborman * process input from peer. 39439503Sborman */ 39539503Sborman 39639503Sborman if (do_dont_resp[option]) { 39739503Sborman do_dont_resp[option]--; 39839503Sborman if (do_dont_resp[option] && hisopts[option] == OPT_YES) 39939503Sborman do_dont_resp[option]--; 40039503Sborman } 40139531Sborman if (do_dont_resp[option] == 0) { 40239531Sborman if (hiswants[option] != OPT_YES) { 40338905Sborman switch (option) { 40438905Sborman 40538905Sborman case TELOPT_BINARY: 40638905Sborman init_termbuf(); 40738905Sborman tty_binaryin(1); 40838905Sborman set_termbuf(); 40938905Sborman changeok++; 41038905Sborman break; 41138905Sborman 41238905Sborman case TELOPT_ECHO: 41339503Sborman not42 = 0; /* looks like a 4.2 system */ 41439503Sborman #ifdef notdef 41538905Sborman /* 41639503Sborman * Now, in a 4.2 system, to break them out of 41739503Sborman * ECHOing (to the terminal) mode, we need to 41839503Sborman * send a WILL ECHO. 41938905Sborman */ 42038905Sborman if (myopts[TELOPT_ECHO] == OPT_YES) { 42139503Sborman send_will(TELOPT_ECHO, 1); 42238905Sborman } 42339503Sborman #else 42438905Sborman /* 42539503Sborman * "WILL ECHO". Kludge upon kludge! 42639503Sborman * A 4.2 client is now echoing user input at 42739503Sborman * the tty. This is probably undesireable and 42839503Sborman * it should be stopped. The client will 42939503Sborman * respond WONT TM to the DO TM that we send to 43039503Sborman * check for kludge linemode. When the WONT TM 43139503Sborman * arrives, linemode will be turned off and a 43239503Sborman * change propogated to the pty. This change 43339503Sborman * will cause us to process the new pty state 43439503Sborman * in localstat(), which will notice that 43539503Sborman * linemode is off and send a WILL ECHO 43639503Sborman * so that we are properly in character mode and 43739503Sborman * all is well. 43839503Sborman */ 43939503Sborman #endif 44039503Sborman /* 44138905Sborman * Fool the state machine into sending a don't. 44239503Sborman * This also allows the initial echo sending 44339503Sborman * code to break out of the loop that it is 44438905Sborman * in. (Look in telnet()) 44538905Sborman */ 44638905Sborman hiswants[TELOPT_ECHO] = OPT_NO; 44738905Sborman break; 44838905Sborman 44938905Sborman case TELOPT_TM: 45038905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 45138905Sborman /* 45239503Sborman * This telnetd implementation does not really 45339503Sborman * support timing marks, it just uses them to 45439503Sborman * support the kludge linemode stuff. If we 45539503Sborman * receive a will or wont TM in response to our 45639503Sborman * do TM request that may have been sent to 45739503Sborman * determine kludge linemode support, process 45839503Sborman * it, otherwise TM should get a negative 45939503Sborman * response back. 46038905Sborman */ 46138905Sborman /* 46238905Sborman * Handle the linemode kludge stuff. 46338905Sborman * If we are not currently supporting any 46438905Sborman * linemode at all, then we assume that this 46538905Sborman * is the client telling us to use kludge 46638905Sborman * linemode in response to our query. Set the 46738905Sborman * linemode type that is to be supported, note 46838905Sborman * that the client wishes to use linemode, and 46938905Sborman * eat the will TM as though it never arrived. 47038905Sborman */ 47138905Sborman if (lmodetype < KLUDGE_LINEMODE) { 47238905Sborman lmodetype = KLUDGE_LINEMODE; 47338905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 47439503Sborman send_wont(TELOPT_SGA, 1); 47538905Sborman } 47638905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 47738905Sborman /* 47839531Sborman * We never respond to a WILL TM, and 47939531Sborman * we leave the state OPT_NO. 48038905Sborman */ 48138905Sborman return; 48238905Sborman 48338905Sborman case TELOPT_LFLOW: 48438905Sborman /* 48539503Sborman * If we are going to support flow control 48639503Sborman * option, then don't worry peer that we can't 48739503Sborman * change the flow control characters. 48838905Sborman */ 48938905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 49038905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 49138905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 49238905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 49338905Sborman case TELOPT_TTYPE: 49438905Sborman case TELOPT_SGA: 49538905Sborman case TELOPT_NAWS: 49638905Sborman case TELOPT_TSPEED: 49739531Sborman changeok++; 49839531Sborman break; 49939531Sborman 50038905Sborman #ifdef LINEMODE 50138905Sborman case TELOPT_LINEMODE: 50239531Sborman # ifdef KLUDGELINEMODE 50339531Sborman /* 50439531Sborman * Note client's desire to use linemode. 50539531Sborman */ 50639531Sborman lmodetype = REAL_LINEMODE; 50739531Sborman # endif /* KLUDGELINEMODE */ 50839531Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 50938905Sborman changeok++; 51038905Sborman break; 51139531Sborman #endif /* LINEMODE */ 51238905Sborman 51338905Sborman default: 51438905Sborman break; 51538905Sborman } 51639503Sborman if (changeok) { 51738905Sborman hiswants[option] = OPT_YES; 51839503Sborman send_do(option, 0); 51939503Sborman } else { 52039503Sborman do_dont_resp[option]++; 52139503Sborman send_dont(option, 0); 52238905Sborman } 52339531Sborman } 52438905Sborman } 52539503Sborman hisopts[option] = OPT_YES; 52638905Sborman } /* end of willoption */ 52738905Sborman 52839503Sborman send_dont(option, init) 52939503Sborman int option, init; 53038905Sborman { 53139503Sborman if (init) { 53239503Sborman if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) || 53339503Sborman hiswants[option] == OPT_NO) 53439503Sborman return; 53539503Sborman hiswants[option] = OPT_NO; 53639503Sborman do_dont_resp[option]++; 53739503Sborman } 53839503Sborman (void) sprintf(nfrontp, dont, option); 53939503Sborman nfrontp += sizeof (doopt) - 2; 54039503Sborman } 54139503Sborman 54239503Sborman wontoption(option) 54339503Sborman int option; 54439503Sborman { 54538905Sborman char *fmt = (char *)0; 54638905Sborman 54738905Sborman /* 54838905Sborman * Process client input. 54938905Sborman */ 55039503Sborman 55139503Sborman if (do_dont_resp[option]) { 55239503Sborman do_dont_resp[option]--; 55339503Sborman if (do_dont_resp[option] && hisopts[option] == OPT_NO) 55439503Sborman do_dont_resp[option]--; 55539503Sborman } 55639531Sborman if (do_dont_resp[option] == 0) { 55739531Sborman if (hiswants[option] != OPT_NO) { 55839503Sborman /* it is always ok to change to negative state */ 55938905Sborman switch (option) { 56038905Sborman case TELOPT_ECHO: 56139503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 56238905Sborman break; 56338905Sborman 56438905Sborman case TELOPT_BINARY: 56538905Sborman init_termbuf(); 56638905Sborman tty_binaryin(0); 56738905Sborman set_termbuf(); 56838905Sborman break; 56938905Sborman 57038905Sborman #ifdef LINEMODE 57138905Sborman case TELOPT_LINEMODE: 57238905Sborman # ifdef KLUDGELINEMODE 57338905Sborman /* 57438905Sborman * If real linemode is supported, then client is 57538905Sborman * asking to turn linemode off. 57638905Sborman */ 57738905Sborman if (lmodetype == REAL_LINEMODE) 57838905Sborman # endif /* KLUDGELINEMODE */ 57938905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 58038905Sborman break; 58138905Sborman #endif LINEMODE 58238905Sborman 58338905Sborman case TELOPT_TM: 58438905Sborman /* 58539503Sborman * If we get a WONT TM, and had sent a DO TM, 58639503Sborman * don't respond with a DONT TM, just leave it 58739503Sborman * as is. Short circut the state machine to 58839531Sborman * achive this. 58938905Sborman */ 59038905Sborman hiswants[TELOPT_TM] = OPT_NO; 59138905Sborman return; 59238905Sborman 59338905Sborman case TELOPT_LFLOW: 59438905Sborman /* 59539503Sborman * If we are not going to support flow control 59639503Sborman * option, then let peer know that we can't 59739503Sborman * change the flow control characters. 59838905Sborman */ 59938905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 60038905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 60138905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 60238905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 60338905Sborman break; 60438905Sborman 60538905Sborman default: 60638905Sborman break; 60738905Sborman } 60839503Sborman hiswants[option] = OPT_NO; 60939503Sborman fmt = dont; 61039503Sborman send_dont(option, 0); 61139531Sborman } else { 61239531Sborman switch (option) { 61339531Sborman case TELOPT_TM: 61439531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 61539531Sborman if (lmodetype < REAL_LINEMODE) { 61639531Sborman lmodetype = NO_LINEMODE; 61739531Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 61839531Sborman send_will(TELOPT_SGA, 1); 61939531Sborman /*@*/ send_will(TELOPT_ECHO, 1); 62039531Sborman } 62139531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 62239531Sborman default: 62339531Sborman break; 62439531Sborman } 62539531Sborman } 62638905Sborman } 62739503Sborman hisopts[option] = OPT_NO; 62838905Sborman 62939503Sborman } /* end of wontoption */ 63038905Sborman 63139503Sborman send_will(option, init) 63239503Sborman int option, init; 63339503Sborman { 63439503Sborman if (init) { 63539503Sborman if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)|| 63639503Sborman mywants[option] == OPT_YES) 63739503Sborman return; 63839503Sborman mywants[option] = OPT_YES; 63939503Sborman will_wont_resp[option]++; 64038905Sborman } 64139503Sborman (void) sprintf(nfrontp, will, option); 64239503Sborman nfrontp += sizeof (doopt) - 2; 64339503Sborman } 64438905Sborman 64539503Sborman dooption(option) 64639503Sborman int option; 64738905Sborman { 64838905Sborman int changeok = 0; 64938905Sborman 65038905Sborman /* 65138905Sborman * Process client input. 65238905Sborman */ 65339503Sborman 65439503Sborman if (will_wont_resp[option]) { 65539503Sborman will_wont_resp[option]--; 65639503Sborman if (will_wont_resp[option] && myopts[option] == OPT_YES) 65739503Sborman will_wont_resp[option]--; 65839503Sborman } 65939503Sborman if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) { 66038905Sborman switch (option) { 66138905Sborman case TELOPT_ECHO: 66238905Sborman #ifdef LINEMODE 66339503Sborman if (lmodetype == NO_LINEMODE) { 66438905Sborman #endif 66538905Sborman init_termbuf(); 66638905Sborman tty_setecho(1); 66738905Sborman set_termbuf(); 66838905Sborman #ifdef LINEMODE 66938905Sborman } 67038905Sborman #endif 67138905Sborman changeok++; 67238905Sborman break; 67338905Sborman 67438905Sborman case TELOPT_BINARY: 67538905Sborman init_termbuf(); 67638905Sborman tty_binaryout(1); 67738905Sborman set_termbuf(); 67838905Sborman changeok++; 67938905Sborman break; 68038905Sborman 68138905Sborman case TELOPT_SGA: 68238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 68338905Sborman /* 68439503Sborman * If kludge linemode is in use, then we must 68539503Sborman * process an incoming do SGA for linemode 68639503Sborman * purposes. 68738905Sborman */ 68838905Sborman if (lmodetype == KLUDGE_LINEMODE) { 68938905Sborman /* 69039503Sborman * Receipt of "do SGA" in kludge 69139503Sborman * linemode is the peer asking us to 69239503Sborman * turn off linemode. Make note of 69339503Sborman * the request. 69438905Sborman */ 69538905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 69638905Sborman /* 69739503Sborman * If linemode did not get turned off 69839503Sborman * then don't tell peer that we did. 69939503Sborman * Breaking here forces a wont SGA to 70039503Sborman * be returned. 70138905Sborman */ 70238905Sborman if (linemode) 70338905Sborman break; 70438905Sborman } 70538905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 70638905Sborman changeok++; 70738905Sborman break; 70838905Sborman 70938905Sborman case TELOPT_STATUS: 71038905Sborman changeok++; 71138905Sborman break; 71238905Sborman 71338905Sborman case TELOPT_TM: 71439503Sborman /* 71539503Sborman * Special case for TM. We send a WILL, but 71639503Sborman * pretend we sent a WONT. 71739503Sborman */ 71839503Sborman send_will(option, 0); 71939503Sborman mywants[option] = OPT_NO; 72039503Sborman myopts[option] = OPT_NO; 72139503Sborman return; 72239503Sborman 72338905Sborman case TELOPT_LINEMODE: 72438905Sborman case TELOPT_TTYPE: 72538905Sborman case TELOPT_NAWS: 72638905Sborman case TELOPT_TSPEED: 72738905Sborman case TELOPT_LFLOW: 72838905Sborman default: 72938905Sborman break; 73038905Sborman } 73139503Sborman if (changeok) { 73238905Sborman mywants[option] = OPT_YES; 73339503Sborman send_will(option, 0); 73439503Sborman } else { 73539503Sborman will_wont_resp[option]++; 73639503Sborman send_wont(option, 0); 73738905Sborman } 73838905Sborman } 73939503Sborman myopts[option] = OPT_YES; 74038905Sborman 74138905Sborman } /* end of dooption */ 74238905Sborman 74339503Sborman send_wont(option, init) 74439503Sborman int option, init; 74539503Sborman { 74639503Sborman if (init) { 74739503Sborman if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) || 74839503Sborman mywants[option] == OPT_NO) 74939503Sborman return; 75039503Sborman mywants[option] = OPT_NO; 75139503Sborman will_wont_resp[option]++; 75239503Sborman } 75339503Sborman (void) sprintf(nfrontp, wont, option); 75439503Sborman nfrontp += sizeof (wont) - 2; 75539503Sborman } 75638905Sborman 75739503Sborman dontoption(option) 75839503Sborman int option; 75938905Sborman { 76038905Sborman /* 76138905Sborman * Process client input. 76238905Sborman */ 76340242Sborman 76439503Sborman if (will_wont_resp[option]) { 76539503Sborman will_wont_resp[option]--; 76639503Sborman if (will_wont_resp[option] && myopts[option] == OPT_NO) 76739503Sborman will_wont_resp[option]--; 76839503Sborman } 76939503Sborman if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) { 77038905Sborman switch (option) { 77138905Sborman case TELOPT_BINARY: 77238905Sborman init_termbuf(); 77338905Sborman tty_binaryout(0); 77438905Sborman set_termbuf(); 77538905Sborman break; 77638905Sborman 77739503Sborman case TELOPT_ECHO: /* we should stop echoing */ 77838905Sborman #ifdef LINEMODE 77939503Sborman if (lmodetype == NO_LINEMODE) { 78038905Sborman #endif 78138905Sborman init_termbuf(); 78238905Sborman tty_setecho(0); 78338905Sborman set_termbuf(); 78438905Sborman #ifdef LINEMODE 78538905Sborman } 78638905Sborman #endif 78738905Sborman break; 78838905Sborman 78938905Sborman case TELOPT_SGA: 79038905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 79138905Sborman /* 79239503Sborman * If kludge linemode is in use, then we 79339503Sborman * must process an incoming do SGA for 79439503Sborman * linemode purposes. 79538905Sborman */ 79638905Sborman if (lmodetype == KLUDGE_LINEMODE) { 79738905Sborman /* 79839503Sborman * The client is asking us to turn 79939503Sborman * linemode on. 80038905Sborman */ 80138905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 80238905Sborman /* 80339503Sborman * If we did not turn line mode on, 80439503Sborman * then what do we say? Will SGA? 80539503Sborman * This violates design of telnet. 80639503Sborman * Gross. Very Gross. 80738905Sborman */ 80838905Sborman } 80938905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 81038905Sborman 81138905Sborman default: 81238905Sborman break; 81338905Sborman } 81438905Sborman 81539503Sborman mywants[option] = OPT_NO; 81639503Sborman send_wont(option, 0); 81738905Sborman } 81839503Sborman myopts[option] = OPT_NO; 81938905Sborman 82038905Sborman } /* end of dontoption */ 82138905Sborman 82238905Sborman /* 82338905Sborman * suboption() 82438905Sborman * 82538905Sborman * Look at the sub-option buffer, and try to be helpful to the other 82638905Sborman * side. 82738905Sborman * 82838905Sborman * Currently we recognize: 82938905Sborman * 83038905Sborman * Terminal type is 83138905Sborman * Linemode 83238905Sborman * Window size 83338905Sborman * Terminal speed 83438905Sborman */ 83538905Sborman suboption() 83638905Sborman { 83738905Sborman register int subchar; 83838905Sborman 83938905Sborman subchar = SB_GET(); 84038905Sborman switch (subchar) { 84138905Sborman case TELOPT_TSPEED: { 84238905Sborman register int xspeed, rspeed; 84338905Sborman 84438905Sborman if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ 84538905Sborman break; 84638905Sborman 84738905Sborman settimer(tspeedsubopt); 84838905Sborman 84938905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 85038905Sborman return; 85138905Sborman 85238905Sborman xspeed = atoi(subpointer); 85338905Sborman 85438905Sborman while (SB_GET() != ',' && !SB_EOF()); 85538905Sborman if (SB_EOF()) 85638905Sborman return; 85738905Sborman 85838905Sborman rspeed = atoi(subpointer); 85938905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 86038905Sborman 86138905Sborman break; 86238905Sborman 86338905Sborman } /* end of case TELOPT_TSPEED */ 86438905Sborman 86538905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 86638905Sborman static char terminalname[5+41] = "TERM="; 86738905Sborman 86838997Sborman if (hisopts[TELOPT_TTYPE] == OPT_NO) /* Ignore if option disabled */ 86938905Sborman break; 87038905Sborman settimer(ttypesubopt); 87138905Sborman 87238905Sborman if (SB_GET() != TELQUAL_IS) { 87338905Sborman return; /* ??? XXX but, this is the most robust */ 87438905Sborman } 87538905Sborman 87638905Sborman terminaltype = terminalname+sizeof("TERM=")-1; 87738905Sborman 87838905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 87938905Sborman !SB_EOF()) { 88038905Sborman register int c; 88138905Sborman 88238905Sborman c = SB_GET(); 88338905Sborman if (isupper(c)) { 88438905Sborman c = tolower(c); 88538905Sborman } 88638905Sborman *terminaltype++ = c; /* accumulate name */ 88738905Sborman } 88838905Sborman *terminaltype = 0; 88938905Sborman terminaltype = terminalname; 89038905Sborman break; 89138905Sborman } /* end of case TELOPT_TTYPE */ 89238905Sborman 89338905Sborman case TELOPT_NAWS: { 89438905Sborman register int xwinsize, ywinsize; 89538905Sborman 89638905Sborman if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ 89738905Sborman break; 89838905Sborman 89938905Sborman if (SB_EOF()) 90038905Sborman return; 90138905Sborman xwinsize = SB_GET() << 8; 90238905Sborman if (SB_EOF()) 90338905Sborman return; 90438905Sborman xwinsize |= SB_GET(); 90538905Sborman if (SB_EOF()) 90638905Sborman return; 90738905Sborman ywinsize = SB_GET() << 8; 90838905Sborman if (SB_EOF()) 90938905Sborman return; 91038905Sborman ywinsize |= SB_GET(); 91138905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 91238905Sborman 91338905Sborman break; 91438905Sborman 91538905Sborman } /* end of case TELOPT_NAWS */ 91638905Sborman 91738905Sborman #ifdef LINEMODE 91838905Sborman case TELOPT_LINEMODE: { 91938905Sborman register int request; 92038905Sborman 92138905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ 92238905Sborman break; 92338905Sborman /* 92438905Sborman * Process linemode suboptions. 92538905Sborman */ 92638905Sborman if (SB_EOF()) break; /* garbage was sent */ 92738905Sborman request = SB_GET(); /* get will/wont */ 92838905Sborman if (SB_EOF()) break; /* another garbage check */ 92938905Sborman 93038905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 93138905Sborman /* 93238905Sborman * Process suboption buffer of slc's 93338905Sborman */ 93438905Sborman start_slc(1); 93538905Sborman do_opt_slc(subpointer, subend - subpointer); 93638905Sborman end_slc(0); 93738905Sborman 93838905Sborman } else if (request == LM_MODE) { 93938905Sborman useeditmode = SB_GET(); /* get mode flag */ 94038905Sborman clientstat(LM_MODE, 0, 0); 94138905Sborman } 94238905Sborman 94338905Sborman switch (SB_GET()) { /* what suboption? */ 94438905Sborman case LM_FORWARDMASK: 94538905Sborman /* 94638905Sborman * According to spec, only server can send request for 94738905Sborman * forwardmask, and client can only return a positive response. 94838905Sborman * So don't worry about it. 94938905Sborman */ 95038905Sborman 95138905Sborman default: 95238905Sborman break; 95338905Sborman } 95440242Sborman break; 95538905Sborman } /* end of case TELOPT_LINEMODE */ 95638905Sborman #endif 95738905Sborman case TELOPT_STATUS: { 95838905Sborman int mode; 95938905Sborman 96038905Sborman mode = SB_GET(); 96138905Sborman switch (mode) { 96238905Sborman case TELQUAL_SEND: 96338905Sborman if (myopts[TELOPT_STATUS] == OPT_YES) 96438905Sborman send_status(); 96538905Sborman break; 96638905Sborman 96738905Sborman case TELQUAL_IS: 96838905Sborman break; 96938905Sborman 97038905Sborman default: 97138905Sborman break; 97238905Sborman } 97340242Sborman break; 97440242Sborman } /* end of case TELOPT_STATUS */ 97538905Sborman 97638905Sborman default: 97738905Sborman break; 97838905Sborman } /* end of switch */ 97938905Sborman 98038905Sborman } /* end of suboption */ 98138905Sborman 98238905Sborman #define ADD(c) *ncp++ = c; 98338905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 98438905Sborman send_status() 98538905Sborman { 98638905Sborman char statusbuf[256]; 98738905Sborman register char *ncp; 98838905Sborman register int i; 98938905Sborman 99038905Sborman ncp = statusbuf; 99138905Sborman 99238905Sborman netflush(); /* get rid of anything waiting to go out */ 99338905Sborman 99438905Sborman ADD(IAC); 99538905Sborman ADD(SB); 99638905Sborman ADD(TELOPT_STATUS); 99738905Sborman ADD(TELQUAL_IS); 99838905Sborman 99938905Sborman for (i = 0; i < NTELOPTS; i++) { 100038905Sborman if (myopts[i] == OPT_YES) { 100138905Sborman ADD(WILL); 100238905Sborman ADD_DATA(i); 100338905Sborman if (i == IAC) 100438905Sborman ADD(IAC); 100538905Sborman } 100638905Sborman if (hisopts[i] == OPT_YES) { 100738905Sborman ADD(DO); 100838905Sborman ADD_DATA(i); 100938905Sborman if (i == IAC) 101038905Sborman ADD(IAC); 101138905Sborman } 101238905Sborman } 101338905Sborman 101438905Sborman #ifdef LINEMODE 101538905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_YES) { 101638905Sborman char *cp, *cpe; 101738905Sborman int len; 101838905Sborman 101938905Sborman ADD(SB); 102038905Sborman ADD(TELOPT_LINEMODE); 102138905Sborman ADD(LM_MODE); 102238905Sborman ADD_DATA(editmode); 102338905Sborman if (editmode == IAC) 102438905Sborman ADD(IAC); 102538905Sborman ADD(SE); 102638905Sborman 102738905Sborman ADD(SB); 102838905Sborman ADD(TELOPT_LINEMODE); 102938905Sborman ADD(LM_SLC); 103038905Sborman start_slc(0); 103138905Sborman send_slc(); 103238905Sborman len = end_slc(&cp); 103338905Sborman for (cpe = cp + len; cp < cpe; cp++) 103438905Sborman ADD_DATA(*cp); 103538905Sborman ADD(SE); 103638905Sborman } 103738905Sborman #endif /* LINEMODE */ 103838905Sborman 103938905Sborman ADD(IAC); 104038905Sborman ADD(SE); 104138905Sborman 104238905Sborman writenet(statusbuf, ncp - statusbuf); 104338905Sborman netflush(); /* Send it on its way */ 104438905Sborman } 1045