138905Sborman /* 238905Sborman * Copyright (c) 1989 Regents of the University of California. 338905Sborman * All rights reserved. 438905Sborman * 538905Sborman * Redistribution and use in source and binary forms are permitted 638905Sborman * provided that the above copyright notice and this paragraph are 738905Sborman * duplicated in all such forms and that any documentation, 838905Sborman * advertising materials, and other materials related to such 938905Sborman * distribution and use acknowledge that the software was developed 1038905Sborman * by the University of California, Berkeley. The name of the 1138905Sborman * University may not be used to endorse or promote products derived 1238905Sborman * from this software without specific prior written permission. 1338905Sborman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1438905Sborman * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1538905Sborman * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1638905Sborman */ 1738905Sborman 1838905Sborman #ifndef lint 19*39531Sborman static char sccsid[] = "@(#)state.c 5.4 (Berkeley) 11/14/89"; 2038905Sborman #endif /* not lint */ 2138905Sborman 2238905Sborman #include "telnetd.h" 2338905Sborman 2438905Sborman char doopt[] = { IAC, DO, '%', 'c', 0 }; 2538905Sborman char dont[] = { IAC, DONT, '%', 'c', 0 }; 2638905Sborman char will[] = { IAC, WILL, '%', 'c', 0 }; 2738905Sborman char wont[] = { IAC, WONT, '%', 'c', 0 }; 2838905Sborman int not42 = 1; 2938905Sborman 3038905Sborman /* 3138905Sborman * Buffer for sub-options, and macros 3238905Sborman * for suboptions buffer manipulations 3338905Sborman */ 3438905Sborman char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 3538905Sborman 3638905Sborman #define SB_CLEAR() subpointer = subbuffer; 3738905Sborman #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 3838905Sborman #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 3938905Sborman *subpointer++ = (c); \ 4038905Sborman } 4138905Sborman #define SB_GET() ((*subpointer++)&0xff) 4238905Sborman #define SB_EOF() (subpointer >= subend) 4338905Sborman 4438905Sborman 4538905Sborman 4638905Sborman /* 4738905Sborman * State for recv fsm 4838905Sborman */ 4938905Sborman #define TS_DATA 0 /* base state */ 5038905Sborman #define TS_IAC 1 /* look for double IAC's */ 5138905Sborman #define TS_CR 2 /* CR-LF ->'s CR */ 5238905Sborman #define TS_SB 3 /* throw away begin's... */ 5338905Sborman #define TS_SE 4 /* ...end's (suboption negotiation) */ 5438905Sborman #define TS_WILL 5 /* will option negotiation */ 5538905Sborman #define TS_WONT 6 /* wont " */ 5638905Sborman #define TS_DO 7 /* do " */ 5738905Sborman #define TS_DONT 8 /* dont " */ 5838905Sborman 5938905Sborman telrcv() 6038905Sborman { 6138905Sborman register int c; 6238905Sborman static int state = TS_DATA; 6338905Sborman #ifdef CRAY2 6438905Sborman char *opfrontp = pfrontp; 6538905Sborman #endif 6638905Sborman 6738905Sborman while (ncc > 0) { 6838905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6938905Sborman break; 7038905Sborman c = *netip++ & 0377, ncc--; 7138905Sborman switch (state) { 7238905Sborman 7338905Sborman case TS_CR: 7438905Sborman state = TS_DATA; 7538905Sborman /* Strip off \n or \0 after a \r */ 7638905Sborman if ((c == 0) || (c == '\n')) { 7738905Sborman break; 7838905Sborman } 7938905Sborman /* FALL THROUGH */ 8038905Sborman 8138905Sborman case TS_DATA: 8238905Sborman if (c == IAC) { 8338905Sborman state = TS_IAC; 8438905Sborman break; 8538905Sborman } 8638905Sborman /* 8738905Sborman * We now map \r\n ==> \r for pragmatic reasons. 8838905Sborman * Many client implementations send \r\n when 8938905Sborman * the user hits the CarriageReturn key. 9038905Sborman * 9138905Sborman * We USED to map \r\n ==> \n, since \r\n says 9238905Sborman * that we want to be in column 1 of the next 9338905Sborman * printable line, and \n is the standard 9438905Sborman * unix way of saying that (\r is only good 9538905Sborman * if CRMOD is set, which it normally is). 9638905Sborman */ 9738905Sborman if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 9838905Sborman /* 9938905Sborman * If we are operating in linemode, 10038905Sborman * convert to local end-of-line. 10138905Sborman */ 10238905Sborman if ((linemode) && (ncc > 0)&&('\n' == *netip)) { 10338905Sborman netip++; ncc--; 10438905Sborman c = '\n'; 10538905Sborman } else { 10638905Sborman state = TS_CR; 10738905Sborman } 10838905Sborman } 10938905Sborman *pfrontp++ = c; 11038905Sborman break; 11138905Sborman 11238905Sborman case TS_IAC: 11338905Sborman gotiac: switch (c) { 11438905Sborman 11538905Sborman /* 11638905Sborman * Send the process on the pty side an 11738905Sborman * interrupt. Do this with a NULL or 11838905Sborman * interrupt char; depending on the tty mode. 11938905Sborman */ 12038905Sborman case IP: 12138905Sborman interrupt(); 12238905Sborman break; 12338905Sborman 12438905Sborman case BREAK: 12538905Sborman sendbrk(); 12638905Sborman break; 12738905Sborman 12838905Sborman /* 12938905Sborman * Are You There? 13038905Sborman */ 13138905Sborman case AYT: 13238905Sborman (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); 13338905Sborman nfrontp += 9; 13438905Sborman break; 13538905Sborman 13638905Sborman /* 13738905Sborman * Abort Output 13838905Sborman */ 13938905Sborman case AO: 14038905Sborman { 14138905Sborman ptyflush(); /* half-hearted */ 14238905Sborman init_termbuf(); 14338905Sborman 14438905Sborman if (slctab[SLC_AO].sptr && 14538905Sborman *slctab[SLC_AO].sptr != '\377') { 14638905Sborman *pfrontp++ = *slctab[SLC_AO].sptr; 14738905Sborman } 14838905Sborman 14938905Sborman netclear(); /* clear buffer back */ 15038905Sborman *nfrontp++ = IAC; 15138905Sborman *nfrontp++ = DM; 15238905Sborman neturg = nfrontp-1; /* off by one XXX */ 15338905Sborman break; 15438905Sborman } 15538905Sborman 15638905Sborman /* 15738905Sborman * Erase Character and 15838905Sborman * Erase Line 15938905Sborman */ 16038905Sborman case EC: 16138905Sborman case EL: 16238905Sborman { 16338905Sborman unsigned char ch; 16438905Sborman 16538905Sborman ptyflush(); /* half-hearted */ 16638905Sborman init_termbuf(); 16738905Sborman ch = (c == EC) ? *slctab[SLC_EC].sptr : 16838905Sborman *slctab[SLC_EL].sptr; 16938905Sborman if (ch != '\377') 17038905Sborman *pfrontp++ = ch; 17138905Sborman break; 17238905Sborman } 17338905Sborman 17438905Sborman /* 17538905Sborman * Check for urgent data... 17638905Sborman */ 17738905Sborman case DM: 17838905Sborman SYNCHing = stilloob(net); 17938905Sborman settimer(gotDM); 18038905Sborman break; 18138905Sborman 18238905Sborman 18338905Sborman /* 18438905Sborman * Begin option subnegotiation... 18538905Sborman */ 18638905Sborman case SB: 18738905Sborman state = TS_SB; 18838905Sborman SB_CLEAR(); 18938905Sborman continue; 19038905Sborman 19138905Sborman case WILL: 19238905Sborman state = TS_WILL; 19338905Sborman continue; 19438905Sborman 19538905Sborman case WONT: 19638905Sborman state = TS_WONT; 19738905Sborman continue; 19838905Sborman 19938905Sborman case DO: 20038905Sborman state = TS_DO; 20138905Sborman continue; 20238905Sborman 20338905Sborman case DONT: 20438905Sborman state = TS_DONT; 20538905Sborman continue; 20638905Sborman case EOR: 20738905Sborman if (hisopts[TELOPT_EOR]) 20838905Sborman doeof(); 20938905Sborman break; 21038905Sborman 21138905Sborman /* 21238905Sborman * Handle RFC 10xx Telnet linemode option additions 21338905Sborman * to command stream (EOF, SUSP, ABORT). 21438905Sborman */ 21538905Sborman case xEOF: 21638905Sborman doeof(); 21738905Sborman break; 21838905Sborman 21938905Sborman case SUSP: 22038905Sborman sendsusp(); 22138905Sborman break; 22238905Sborman 22338905Sborman case ABORT: 22438905Sborman sendbrk(); 22538905Sborman break; 22638905Sborman 22738905Sborman case IAC: 22838905Sborman *pfrontp++ = c; 22938905Sborman break; 23038905Sborman } 23138905Sborman state = TS_DATA; 23238905Sborman break; 23338905Sborman 23438905Sborman case TS_SB: 23538905Sborman if (c == IAC) { 23638905Sborman state = TS_SE; 23738905Sborman } else { 23838905Sborman SB_ACCUM(c); 23938905Sborman } 24038905Sborman break; 24138905Sborman 24238905Sborman case TS_SE: 24338905Sborman if (c != SE) { 24438905Sborman if (c != IAC) { 24538905Sborman /* 24638905Sborman * bad form of suboption negotiation. 24738905Sborman * handle it in such a way as to avoid 24838905Sborman * damage to local state. Parse 24938905Sborman * suboption buffer found so far, 25038905Sborman * then treat remaining stream as 25138905Sborman * another command sequence. 25238905Sborman */ 25338905Sborman SB_TERM(); 25438905Sborman suboption(); 25538905Sborman state = TS_IAC; 25638905Sborman goto gotiac; 25738905Sborman } 25838905Sborman SB_ACCUM(c); 25938905Sborman state = TS_SB; 26038905Sborman } else { 26138905Sborman SB_TERM(); 26238905Sborman suboption(); /* handle sub-option */ 26338905Sborman state = TS_DATA; 26438905Sborman } 26538905Sborman break; 26638905Sborman 26738905Sborman case TS_WILL: 26839503Sborman willoption(c); 26938905Sborman state = TS_DATA; 27038905Sborman continue; 27138905Sborman 27238905Sborman case TS_WONT: 27339503Sborman wontoption(c); 27438905Sborman state = TS_DATA; 27538905Sborman continue; 27638905Sborman 27738905Sborman case TS_DO: 27839503Sborman dooption(c); 27938905Sborman state = TS_DATA; 28038905Sborman continue; 28138905Sborman 28238905Sborman case TS_DONT: 28339503Sborman dontoption(c); 28438905Sborman state = TS_DATA; 28538905Sborman continue; 28638905Sborman 28738905Sborman default: 28838905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 28938905Sborman printf("telnetd: panic state=%d\n", state); 29038905Sborman exit(1); 29138905Sborman } 29238905Sborman } 29338905Sborman #if CRAY2 29438905Sborman if (!linemode) { 29538905Sborman char xptyobuf[BUFSIZ+NETSLOP]; 29638905Sborman char xbuf2[BUFSIZ]; 29738905Sborman register char *cp; 29838905Sborman int n = pfrontp - opfrontp, oc; 29938905Sborman bcopy(opfrontp, xptyobuf, n); 30038905Sborman pfrontp = opfrontp; 30138905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, 30238905Sborman xbuf2, &oc, BUFSIZ); 30338905Sborman for (cp = xbuf2; oc > 0; --oc) 30438905Sborman if ((*nfrontp++ = *cp++) == IAC) 30538905Sborman *nfrontp++ = IAC; 30638905Sborman } 30738905Sborman #endif /* CRAY2 */ 30838905Sborman } /* end of telrcv */ 30938905Sborman 31038905Sborman /* 31138905Sborman * The will/wont/do/dont state machines are based on Dave Borman's 31238905Sborman * Telnet option processing state machine. We keep track of the full 31338905Sborman * state of the option negotiation with the following state variables 31438905Sborman * myopts, hisopts - The last fully negotiated state for each 31538905Sborman * side of the connection. 31638905Sborman * mywants, hiswants - The state we wish to be in after a completed 31738905Sborman * negotiation. (hiswants is slightly misleading, 31838905Sborman * this is more precisely the state I want him to 31938905Sborman * be in. 32038905Sborman * resp - We count the number of requests we have sent out. 32138905Sborman * 32238905Sborman * These correspond to the following states: 32338905Sborman * my_state = the last negotiated state 32438905Sborman * want_state = what I want the state to go to 32538905Sborman * want_resp = how many requests I have sent 32638905Sborman * All state defaults are negative, and resp defaults to 0. 32738905Sborman * 32838905Sborman * When initiating a request to change state to new_state: 32938905Sborman * 33038905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 33138905Sborman * do nothing; 33238905Sborman * } else { 33338905Sborman * want_state = new_state; 33438905Sborman * send new_state; 33538905Sborman * want_resp++; 33638905Sborman * } 33738905Sborman * 33838905Sborman * When receiving new_state: 33938905Sborman * 34038905Sborman * if (want_resp) { 34138905Sborman * want_resp--; 34238905Sborman * if (want_resp && (new_state == my_state)) 34338905Sborman * want_resp--; 34438905Sborman * } 34538905Sborman * if ((want_resp == 0) && (new_state != want_state)) { 34638905Sborman * if (ok_to_switch_to new_state) 34738905Sborman * want_state = new_state; 34838905Sborman * else 34938905Sborman * want_resp++; 35038905Sborman * send want_state; 35138905Sborman * } 35238905Sborman * my_state = new_state; 35338905Sborman * 35438905Sborman * Note that new_state is implied in these functions by the function itself. 35538905Sborman * will and do imply positive new_state, wont and dont imply negative. 35638905Sborman * 35738905Sborman * Finally, there is one catch. If we send a negative response to a 35838905Sborman * positive request, my_state will be the positive while want_state will 35938905Sborman * remain negative. my_state will revert to negative when the negative 36038905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells 36138905Sborman * us not only the last negotiated state, but also tells us what the peer 36238905Sborman * wants to be doing as well. It is important to understand this difference 36338905Sborman * as we may wish to be processing data streams based on our desired state 36438905Sborman * (want_state) or based on what the peer thinks the state is (my_state). 36538905Sborman * 36638905Sborman * This all works fine because if the peer sends a positive request, the data 36738905Sborman * that we receive prior to negative acknowlegment will probably be affected 36838905Sborman * by the positive state, and we can process it as such (if we can; if we 36938905Sborman * can't then it really doesn't matter). If it is that important, then the 37038905Sborman * peer probably should be buffering until this option state negotiation 37138905Sborman * is complete. 37238905Sborman * 37338905Sborman * In processing options, request signifies whether this is a request 37438905Sborman * to send or a response. request is true if this is a request to 37538905Sborman * send generated locally. 37638905Sborman */ 37739503Sborman send_do(option, init) 37839503Sborman int option, init; 37938905Sborman { 38039503Sborman if (init) { 38139503Sborman if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) || 38239503Sborman hiswants[option] == OPT_YES) 38339503Sborman return; 384*39531Sborman /* 385*39531Sborman * Special case for TELOPT_TM: We send a DO, but pretend 386*39531Sborman * that we sent a DONT, so that we can send more DOs if 387*39531Sborman * we want to. 388*39531Sborman */ 389*39531Sborman if (option == TELOPT_TM) 390*39531Sborman hiswants[option] = OPT_NO; 391*39531Sborman else 392*39531Sborman hiswants[option] = OPT_YES; 39339503Sborman do_dont_resp[option]++; 39439503Sborman } 39539503Sborman (void) sprintf(nfrontp, doopt, option); 39639503Sborman nfrontp += sizeof (dont) - 2; 39739503Sborman } 39839503Sborman 39939503Sborman willoption(option) 40039503Sborman int option; 40139503Sborman { 40238905Sborman int changeok = 0; 40338905Sborman 40439503Sborman /* 40539503Sborman * process input from peer. 40639503Sborman */ 40739503Sborman 40839503Sborman if (do_dont_resp[option]) { 40939503Sborman do_dont_resp[option]--; 41039503Sborman if (do_dont_resp[option] && hisopts[option] == OPT_YES) 41139503Sborman do_dont_resp[option]--; 41239503Sborman } 413*39531Sborman if (do_dont_resp[option] == 0) { 414*39531Sborman if (hiswants[option] != OPT_YES) { 41538905Sborman switch (option) { 41638905Sborman 41738905Sborman case TELOPT_BINARY: 41838905Sborman init_termbuf(); 41938905Sborman tty_binaryin(1); 42038905Sborman set_termbuf(); 42138905Sborman changeok++; 42238905Sborman break; 42338905Sborman 42438905Sborman case TELOPT_ECHO: 42539503Sborman not42 = 0; /* looks like a 4.2 system */ 42639503Sborman #ifdef notdef 42738905Sborman /* 42839503Sborman * Now, in a 4.2 system, to break them out of 42939503Sborman * ECHOing (to the terminal) mode, we need to 43039503Sborman * send a WILL ECHO. 43138905Sborman */ 43238905Sborman if (myopts[TELOPT_ECHO] == OPT_YES) { 43339503Sborman send_will(TELOPT_ECHO, 1); 43438905Sborman } 43539503Sborman #else 43638905Sborman /* 43739503Sborman * "WILL ECHO". Kludge upon kludge! 43839503Sborman * A 4.2 client is now echoing user input at 43939503Sborman * the tty. This is probably undesireable and 44039503Sborman * it should be stopped. The client will 44139503Sborman * respond WONT TM to the DO TM that we send to 44239503Sborman * check for kludge linemode. When the WONT TM 44339503Sborman * arrives, linemode will be turned off and a 44439503Sborman * change propogated to the pty. This change 44539503Sborman * will cause us to process the new pty state 44639503Sborman * in localstat(), which will notice that 44739503Sborman * linemode is off and send a WILL ECHO 44839503Sborman * so that we are properly in character mode and 44939503Sborman * all is well. 45039503Sborman */ 45139503Sborman #endif 45239503Sborman /* 45338905Sborman * Fool the state machine into sending a don't. 45439503Sborman * This also allows the initial echo sending 45539503Sborman * code to break out of the loop that it is 45638905Sborman * in. (Look in telnet()) 45738905Sborman */ 45838905Sborman hiswants[TELOPT_ECHO] = OPT_NO; 45938905Sborman break; 46038905Sborman 46138905Sborman case TELOPT_TM: 46238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 46338905Sborman /* 46439503Sborman * This telnetd implementation does not really 46539503Sborman * support timing marks, it just uses them to 46639503Sborman * support the kludge linemode stuff. If we 46739503Sborman * receive a will or wont TM in response to our 46839503Sborman * do TM request that may have been sent to 46939503Sborman * determine kludge linemode support, process 47039503Sborman * it, otherwise TM should get a negative 47139503Sborman * response back. 47238905Sborman */ 47338905Sborman /* 47438905Sborman * Handle the linemode kludge stuff. 47538905Sborman * If we are not currently supporting any 47638905Sborman * linemode at all, then we assume that this 47738905Sborman * is the client telling us to use kludge 47838905Sborman * linemode in response to our query. Set the 47938905Sborman * linemode type that is to be supported, note 48038905Sborman * that the client wishes to use linemode, and 48138905Sborman * eat the will TM as though it never arrived. 48238905Sborman */ 48338905Sborman if (lmodetype < KLUDGE_LINEMODE) { 48438905Sborman lmodetype = KLUDGE_LINEMODE; 48538905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 48639503Sborman send_wont(TELOPT_SGA, 1); 48738905Sborman } 48838905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 48938905Sborman /* 490*39531Sborman * We never respond to a WILL TM, and 491*39531Sborman * we leave the state OPT_NO. 49238905Sborman */ 49338905Sborman return; 49438905Sborman 49538905Sborman case TELOPT_LFLOW: 49638905Sborman /* 49739503Sborman * If we are going to support flow control 49839503Sborman * option, then don't worry peer that we can't 49939503Sborman * change the flow control characters. 50038905Sborman */ 50138905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 50238905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 50338905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 50438905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 50538905Sborman case TELOPT_TTYPE: 50638905Sborman case TELOPT_SGA: 50738905Sborman case TELOPT_NAWS: 50838905Sborman case TELOPT_TSPEED: 509*39531Sborman changeok++; 510*39531Sborman break; 511*39531Sborman 51238905Sborman #ifdef LINEMODE 51338905Sborman case TELOPT_LINEMODE: 514*39531Sborman # ifdef KLUDGELINEMODE 515*39531Sborman /* 516*39531Sborman * Note client's desire to use linemode. 517*39531Sborman */ 518*39531Sborman lmodetype = REAL_LINEMODE; 519*39531Sborman # endif /* KLUDGELINEMODE */ 520*39531Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 52138905Sborman changeok++; 52238905Sborman break; 523*39531Sborman #endif /* LINEMODE */ 52438905Sborman 52538905Sborman default: 52638905Sborman break; 52738905Sborman } 52839503Sborman if (changeok) { 52938905Sborman hiswants[option] = OPT_YES; 53039503Sborman send_do(option, 0); 53139503Sborman } else { 53239503Sborman do_dont_resp[option]++; 53339503Sborman send_dont(option, 0); 53438905Sborman } 535*39531Sborman } 53638905Sborman } 53739503Sborman hisopts[option] = OPT_YES; 53838905Sborman } /* end of willoption */ 53938905Sborman 54039503Sborman send_dont(option, init) 54139503Sborman int option, init; 54238905Sborman { 54339503Sborman if (init) { 54439503Sborman if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) || 54539503Sborman hiswants[option] == OPT_NO) 54639503Sborman return; 54739503Sborman hiswants[option] = OPT_NO; 54839503Sborman do_dont_resp[option]++; 54939503Sborman } 55039503Sborman (void) sprintf(nfrontp, dont, option); 55139503Sborman nfrontp += sizeof (doopt) - 2; 55239503Sborman } 55339503Sborman 55439503Sborman wontoption(option) 55539503Sborman int option; 55639503Sborman { 55738905Sborman char *fmt = (char *)0; 55838905Sborman 55938905Sborman /* 56038905Sborman * Process client input. 56138905Sborman */ 56239503Sborman 56339503Sborman if (do_dont_resp[option]) { 56439503Sborman do_dont_resp[option]--; 56539503Sborman if (do_dont_resp[option] && hisopts[option] == OPT_NO) 56639503Sborman do_dont_resp[option]--; 56739503Sborman } 568*39531Sborman if (do_dont_resp[option] == 0) { 569*39531Sborman if (hiswants[option] != OPT_NO) { 57039503Sborman /* it is always ok to change to negative state */ 57138905Sborman switch (option) { 57238905Sborman case TELOPT_ECHO: 57339503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 57438905Sborman break; 57538905Sborman 57638905Sborman case TELOPT_BINARY: 57738905Sborman init_termbuf(); 57838905Sborman tty_binaryin(0); 57938905Sborman set_termbuf(); 58038905Sborman break; 58138905Sborman 58238905Sborman #ifdef LINEMODE 58338905Sborman case TELOPT_LINEMODE: 58438905Sborman # ifdef KLUDGELINEMODE 58538905Sborman /* 58638905Sborman * If real linemode is supported, then client is 58738905Sborman * asking to turn linemode off. 58838905Sborman */ 58938905Sborman if (lmodetype == REAL_LINEMODE) 59038905Sborman # endif /* KLUDGELINEMODE */ 59138905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 59238905Sborman break; 59338905Sborman #endif LINEMODE 59438905Sborman 59538905Sborman case TELOPT_TM: 59638905Sborman /* 59739503Sborman * If we get a WONT TM, and had sent a DO TM, 59839503Sborman * don't respond with a DONT TM, just leave it 59939503Sborman * as is. Short circut the state machine to 600*39531Sborman * achive this. 60138905Sborman */ 60238905Sborman hiswants[TELOPT_TM] = OPT_NO; 60338905Sborman return; 60438905Sborman 60538905Sborman case TELOPT_LFLOW: 60638905Sborman /* 60739503Sborman * If we are not going to support flow control 60839503Sborman * option, then let peer know that we can't 60939503Sborman * change the flow control characters. 61038905Sborman */ 61138905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 61238905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 61338905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 61438905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 61538905Sborman break; 61638905Sborman 61738905Sborman default: 61838905Sborman break; 61938905Sborman } 62039503Sborman hiswants[option] = OPT_NO; 62139503Sborman fmt = dont; 62239503Sborman send_dont(option, 0); 623*39531Sborman } else { 624*39531Sborman switch (option) { 625*39531Sborman case TELOPT_TM: 626*39531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 627*39531Sborman if (lmodetype < REAL_LINEMODE) { 628*39531Sborman lmodetype = NO_LINEMODE; 629*39531Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 630*39531Sborman send_will(TELOPT_SGA, 1); 631*39531Sborman /*@*/ send_will(TELOPT_ECHO, 1); 632*39531Sborman } 633*39531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 634*39531Sborman default: 635*39531Sborman break; 636*39531Sborman } 637*39531Sborman } 63838905Sborman } 63939503Sborman hisopts[option] = OPT_NO; 64038905Sborman 64139503Sborman } /* end of wontoption */ 64238905Sborman 64339503Sborman send_will(option, init) 64439503Sborman int option, init; 64539503Sborman { 64639503Sborman if (init) { 64739503Sborman if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)|| 64839503Sborman mywants[option] == OPT_YES) 64939503Sborman return; 65039503Sborman mywants[option] = OPT_YES; 65139503Sborman will_wont_resp[option]++; 65238905Sborman } 65339503Sborman (void) sprintf(nfrontp, will, option); 65439503Sborman nfrontp += sizeof (doopt) - 2; 65539503Sborman } 65638905Sborman 65739503Sborman dooption(option) 65839503Sborman int option; 65938905Sborman { 66038905Sborman int changeok = 0; 66138905Sborman 66238905Sborman /* 66338905Sborman * Process client input. 66438905Sborman */ 66539503Sborman 66639503Sborman if (will_wont_resp[option]) { 66739503Sborman will_wont_resp[option]--; 66839503Sborman if (will_wont_resp[option] && myopts[option] == OPT_YES) 66939503Sborman will_wont_resp[option]--; 67039503Sborman } 67139503Sborman if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) { 67238905Sborman switch (option) { 67338905Sborman case TELOPT_ECHO: 67438905Sborman #ifdef LINEMODE 67539503Sborman if (lmodetype == NO_LINEMODE) { 67638905Sborman #endif 67738905Sborman init_termbuf(); 67838905Sborman tty_setecho(1); 67938905Sborman set_termbuf(); 68038905Sborman #ifdef LINEMODE 68138905Sborman } 68238905Sborman #endif 68338905Sborman changeok++; 68438905Sborman break; 68538905Sborman 68638905Sborman case TELOPT_BINARY: 68738905Sborman init_termbuf(); 68838905Sborman tty_binaryout(1); 68938905Sborman set_termbuf(); 69038905Sborman changeok++; 69138905Sborman break; 69238905Sborman 69338905Sborman case TELOPT_SGA: 69438905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 69538905Sborman /* 69639503Sborman * If kludge linemode is in use, then we must 69739503Sborman * process an incoming do SGA for linemode 69839503Sborman * purposes. 69938905Sborman */ 70038905Sborman if (lmodetype == KLUDGE_LINEMODE) { 70138905Sborman /* 70239503Sborman * Receipt of "do SGA" in kludge 70339503Sborman * linemode is the peer asking us to 70439503Sborman * turn off linemode. Make note of 70539503Sborman * the request. 70638905Sborman */ 70738905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 70838905Sborman /* 70939503Sborman * If linemode did not get turned off 71039503Sborman * then don't tell peer that we did. 71139503Sborman * Breaking here forces a wont SGA to 71239503Sborman * be returned. 71338905Sborman */ 71438905Sborman if (linemode) 71538905Sborman break; 71638905Sborman } 71738905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 71838905Sborman changeok++; 71938905Sborman break; 72038905Sborman 72138905Sborman case TELOPT_STATUS: 72238905Sborman changeok++; 72338905Sborman break; 72438905Sborman 72538905Sborman case TELOPT_TM: 72639503Sborman /* 72739503Sborman * Special case for TM. We send a WILL, but 72839503Sborman * pretend we sent a WONT. 72939503Sborman */ 73039503Sborman send_will(option, 0); 73139503Sborman mywants[option] = OPT_NO; 73239503Sborman myopts[option] = OPT_NO; 73339503Sborman return; 73439503Sborman 73538905Sborman case TELOPT_LINEMODE: 73638905Sborman case TELOPT_TTYPE: 73738905Sborman case TELOPT_NAWS: 73838905Sborman case TELOPT_TSPEED: 73938905Sborman case TELOPT_LFLOW: 74038905Sborman default: 74138905Sborman break; 74238905Sborman } 74339503Sborman if (changeok) { 74438905Sborman mywants[option] = OPT_YES; 74539503Sborman send_will(option, 0); 74639503Sborman } else { 74739503Sborman will_wont_resp[option]++; 74839503Sborman send_wont(option, 0); 74938905Sborman } 75038905Sborman } 75139503Sborman myopts[option] = OPT_YES; 75238905Sborman 75338905Sborman } /* end of dooption */ 75438905Sborman 75539503Sborman send_wont(option, init) 75639503Sborman int option, init; 75739503Sborman { 75839503Sborman if (init) { 75939503Sborman if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) || 76039503Sborman mywants[option] == OPT_NO) 76139503Sborman return; 76239503Sborman mywants[option] = OPT_NO; 76339503Sborman will_wont_resp[option]++; 76439503Sborman } 76539503Sborman (void) sprintf(nfrontp, wont, option); 76639503Sborman nfrontp += sizeof (wont) - 2; 76739503Sborman } 76838905Sborman 76939503Sborman dontoption(option) 77039503Sborman int option; 77138905Sborman { 77238905Sborman /* 77338905Sborman * Process client input. 77438905Sborman */ 77539503Sborman if (will_wont_resp[option]) { 77639503Sborman will_wont_resp[option]--; 77739503Sborman if (will_wont_resp[option] && myopts[option] == OPT_NO) 77839503Sborman will_wont_resp[option]--; 77939503Sborman } 78039503Sborman if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) { 78138905Sborman switch (option) { 78238905Sborman case TELOPT_BINARY: 78338905Sborman init_termbuf(); 78438905Sborman tty_binaryout(0); 78538905Sborman set_termbuf(); 78638905Sborman break; 78738905Sborman 78839503Sborman case TELOPT_ECHO: /* we should stop echoing */ 78938905Sborman #ifdef LINEMODE 79039503Sborman if (lmodetype == NO_LINEMODE) { 79138905Sborman #endif 79238905Sborman init_termbuf(); 79338905Sborman tty_setecho(0); 79438905Sborman set_termbuf(); 79538905Sborman #ifdef LINEMODE 79638905Sborman } 79738905Sborman #endif 79838905Sborman break; 79938905Sborman 80038905Sborman case TELOPT_SGA: 80138905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 80238905Sborman /* 80339503Sborman * If kludge linemode is in use, then we 80439503Sborman * must process an incoming do SGA for 80539503Sborman * linemode purposes. 80638905Sborman */ 80738905Sborman if (lmodetype == KLUDGE_LINEMODE) { 80838905Sborman /* 80939503Sborman * The client is asking us to turn 81039503Sborman * linemode on. 81138905Sborman */ 81238905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 81338905Sborman /* 81439503Sborman * If we did not turn line mode on, 81539503Sborman * then what do we say? Will SGA? 81639503Sborman * This violates design of telnet. 81739503Sborman * Gross. Very Gross. 81838905Sborman */ 81938905Sborman } 82038905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 82138905Sborman 82238905Sborman default: 82338905Sborman break; 82438905Sborman } 82538905Sborman 82639503Sborman mywants[option] = OPT_NO; 82739503Sborman send_wont(option, 0); 82838905Sborman } 82939503Sborman myopts[option] = OPT_NO; 83038905Sborman 83138905Sborman } /* end of dontoption */ 83238905Sborman 83338905Sborman /* 83438905Sborman * suboption() 83538905Sborman * 83638905Sborman * Look at the sub-option buffer, and try to be helpful to the other 83738905Sborman * side. 83838905Sborman * 83938905Sborman * Currently we recognize: 84038905Sborman * 84138905Sborman * Terminal type is 84238905Sborman * Linemode 84338905Sborman * Window size 84438905Sborman * Terminal speed 84538905Sborman */ 84638905Sborman suboption() 84738905Sborman { 84838905Sborman register int subchar; 84938905Sborman 85038905Sborman subchar = SB_GET(); 85138905Sborman switch (subchar) { 85238905Sborman case TELOPT_TSPEED: { 85338905Sborman register int xspeed, rspeed; 85438905Sborman 85538905Sborman if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ 85638905Sborman break; 85738905Sborman 85838905Sborman settimer(tspeedsubopt); 85938905Sborman 86038905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 86138905Sborman return; 86238905Sborman 86338905Sborman xspeed = atoi(subpointer); 86438905Sborman 86538905Sborman while (SB_GET() != ',' && !SB_EOF()); 86638905Sborman if (SB_EOF()) 86738905Sborman return; 86838905Sborman 86938905Sborman rspeed = atoi(subpointer); 87038905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 87138905Sborman 87238905Sborman break; 87338905Sborman 87438905Sborman } /* end of case TELOPT_TSPEED */ 87538905Sborman 87638905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 87738905Sborman static char terminalname[5+41] = "TERM="; 87838905Sborman 87938997Sborman if (hisopts[TELOPT_TTYPE] == OPT_NO) /* Ignore if option disabled */ 88038905Sborman break; 88138905Sborman settimer(ttypesubopt); 88238905Sborman 88338905Sborman if (SB_GET() != TELQUAL_IS) { 88438905Sborman return; /* ??? XXX but, this is the most robust */ 88538905Sborman } 88638905Sborman 88738905Sborman terminaltype = terminalname+sizeof("TERM=")-1; 88838905Sborman 88938905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 89038905Sborman !SB_EOF()) { 89138905Sborman register int c; 89238905Sborman 89338905Sborman c = SB_GET(); 89438905Sborman if (isupper(c)) { 89538905Sborman c = tolower(c); 89638905Sborman } 89738905Sborman *terminaltype++ = c; /* accumulate name */ 89838905Sborman } 89938905Sborman *terminaltype = 0; 90038905Sborman terminaltype = terminalname; 90138905Sborman break; 90238905Sborman } /* end of case TELOPT_TTYPE */ 90338905Sborman 90438905Sborman case TELOPT_NAWS: { 90538905Sborman register int xwinsize, ywinsize; 90638905Sborman 90738905Sborman if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ 90838905Sborman break; 90938905Sborman 91038905Sborman if (SB_EOF()) 91138905Sborman return; 91238905Sborman xwinsize = SB_GET() << 8; 91338905Sborman if (SB_EOF()) 91438905Sborman return; 91538905Sborman xwinsize |= SB_GET(); 91638905Sborman if (SB_EOF()) 91738905Sborman return; 91838905Sborman ywinsize = SB_GET() << 8; 91938905Sborman if (SB_EOF()) 92038905Sborman return; 92138905Sborman ywinsize |= SB_GET(); 92238905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 92338905Sborman 92438905Sborman break; 92538905Sborman 92638905Sborman } /* end of case TELOPT_NAWS */ 92738905Sborman 92838905Sborman #ifdef LINEMODE 92938905Sborman case TELOPT_LINEMODE: { 93038905Sborman register int request; 93138905Sborman 93238905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ 93338905Sborman break; 93438905Sborman /* 93538905Sborman * Process linemode suboptions. 93638905Sborman */ 93738905Sborman if (SB_EOF()) break; /* garbage was sent */ 93838905Sborman request = SB_GET(); /* get will/wont */ 93938905Sborman if (SB_EOF()) break; /* another garbage check */ 94038905Sborman 94138905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 94238905Sborman /* 94338905Sborman * Process suboption buffer of slc's 94438905Sborman */ 94538905Sborman start_slc(1); 94638905Sborman do_opt_slc(subpointer, subend - subpointer); 94738905Sborman end_slc(0); 94838905Sborman 94938905Sborman } else if (request == LM_MODE) { 95038905Sborman useeditmode = SB_GET(); /* get mode flag */ 95138905Sborman clientstat(LM_MODE, 0, 0); 95238905Sborman } 95338905Sborman 95438905Sborman switch (SB_GET()) { /* what suboption? */ 95538905Sborman case LM_FORWARDMASK: 95638905Sborman /* 95738905Sborman * According to spec, only server can send request for 95838905Sborman * forwardmask, and client can only return a positive response. 95938905Sborman * So don't worry about it. 96038905Sborman */ 96138905Sborman 96238905Sborman default: 96338905Sborman break; 96438905Sborman } 96538905Sborman 96638905Sborman } /* end of case TELOPT_LINEMODE */ 96738905Sborman #endif 96838905Sborman case TELOPT_STATUS: { 96938905Sborman int mode; 97038905Sborman 97138905Sborman mode = SB_GET(); 97238905Sborman switch (mode) { 97338905Sborman case TELQUAL_SEND: 97438905Sborman if (myopts[TELOPT_STATUS] == OPT_YES) 97538905Sborman send_status(); 97638905Sborman break; 97738905Sborman 97838905Sborman case TELQUAL_IS: 97938905Sborman break; 98038905Sborman 98138905Sborman default: 98238905Sborman break; 98338905Sborman } 98438905Sborman } 98538905Sborman 98638905Sborman default: 98738905Sborman break; 98838905Sborman } /* end of switch */ 98938905Sborman 99038905Sborman } /* end of suboption */ 99138905Sborman 99238905Sborman #define ADD(c) *ncp++ = c; 99338905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 99438905Sborman send_status() 99538905Sborman { 99638905Sborman char statusbuf[256]; 99738905Sborman register char *ncp; 99838905Sborman register int i; 99938905Sborman 100038905Sborman ncp = statusbuf; 100138905Sborman 100238905Sborman netflush(); /* get rid of anything waiting to go out */ 100338905Sborman 100438905Sborman ADD(IAC); 100538905Sborman ADD(SB); 100638905Sborman ADD(TELOPT_STATUS); 100738905Sborman ADD(TELQUAL_IS); 100838905Sborman 100938905Sborman for (i = 0; i < NTELOPTS; i++) { 101038905Sborman if (myopts[i] == OPT_YES) { 101138905Sborman ADD(WILL); 101238905Sborman ADD_DATA(i); 101338905Sborman if (i == IAC) 101438905Sborman ADD(IAC); 101538905Sborman } 101638905Sborman if (hisopts[i] == OPT_YES) { 101738905Sborman ADD(DO); 101838905Sborman ADD_DATA(i); 101938905Sborman if (i == IAC) 102038905Sborman ADD(IAC); 102138905Sborman } 102238905Sborman } 102338905Sborman 102438905Sborman #ifdef LINEMODE 102538905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_YES) { 102638905Sborman char *cp, *cpe; 102738905Sborman int len; 102838905Sborman 102938905Sborman ADD(SB); 103038905Sborman ADD(TELOPT_LINEMODE); 103138905Sborman ADD(LM_MODE); 103238905Sborman ADD_DATA(editmode); 103338905Sborman if (editmode == IAC) 103438905Sborman ADD(IAC); 103538905Sborman ADD(SE); 103638905Sborman 103738905Sborman ADD(SB); 103838905Sborman ADD(TELOPT_LINEMODE); 103938905Sborman ADD(LM_SLC); 104038905Sborman start_slc(0); 104138905Sborman send_slc(); 104238905Sborman len = end_slc(&cp); 104338905Sborman for (cpe = cp + len; cp < cpe; cp++) 104438905Sborman ADD_DATA(*cp); 104538905Sborman ADD(SE); 104638905Sborman } 104738905Sborman #endif /* LINEMODE */ 104838905Sborman 104938905Sborman ADD(IAC); 105038905Sborman ADD(SE); 105138905Sborman 105238905Sborman writenet(statusbuf, ncp - statusbuf); 105338905Sborman netflush(); /* Send it on its way */ 105438905Sborman } 1055