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*40242Sborman static char sccsid[] = "@(#)state.c 5.5 (Berkeley) 02/28/90"; 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; 63*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 6438905Sborman char *opfrontp = pfrontp; 6538905Sborman #endif 6638905Sborman 6738905Sborman while (ncc > 0) { 6838905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6938905Sborman break; 7038905Sborman c = *netip++ & 0377, ncc--; 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 && 145*40242Sborman *slctab[SLC_AO].sptr != (cc_t)-1) { 146*40242Sborman *pfrontp++ = 147*40242Sborman (unsigned char)*slctab[SLC_AO].sptr; 14838905Sborman } 14938905Sborman 15038905Sborman netclear(); /* clear buffer back */ 15138905Sborman *nfrontp++ = IAC; 15238905Sborman *nfrontp++ = DM; 15338905Sborman neturg = nfrontp-1; /* off by one XXX */ 15438905Sborman break; 15538905Sborman } 15638905Sborman 15738905Sborman /* 15838905Sborman * Erase Character and 15938905Sborman * Erase Line 16038905Sborman */ 16138905Sborman case EC: 16238905Sborman case EL: 16338905Sborman { 164*40242Sborman cc_t ch; 16538905Sborman 16638905Sborman ptyflush(); /* half-hearted */ 16738905Sborman init_termbuf(); 16838905Sborman ch = (c == EC) ? *slctab[SLC_EC].sptr : 16938905Sborman *slctab[SLC_EL].sptr; 170*40242Sborman if (ch != (cc_t)-1) 171*40242Sborman *pfrontp++ = (unsigned char)ch; 17238905Sborman break; 17338905Sborman } 17438905Sborman 17538905Sborman /* 17638905Sborman * Check for urgent data... 17738905Sborman */ 17838905Sborman case DM: 17938905Sborman SYNCHing = stilloob(net); 18038905Sborman settimer(gotDM); 18138905Sborman break; 18238905Sborman 18338905Sborman 18438905Sborman /* 18538905Sborman * Begin option subnegotiation... 18638905Sborman */ 18738905Sborman case SB: 18838905Sborman state = TS_SB; 18938905Sborman SB_CLEAR(); 19038905Sborman continue; 19138905Sborman 19238905Sborman case WILL: 19338905Sborman state = TS_WILL; 19438905Sborman continue; 19538905Sborman 19638905Sborman case WONT: 19738905Sborman state = TS_WONT; 19838905Sborman continue; 19938905Sborman 20038905Sborman case DO: 20138905Sborman state = TS_DO; 20238905Sborman continue; 20338905Sborman 20438905Sborman case DONT: 20538905Sborman state = TS_DONT; 20638905Sborman continue; 20738905Sborman case EOR: 20838905Sborman if (hisopts[TELOPT_EOR]) 20938905Sborman doeof(); 21038905Sborman break; 21138905Sborman 21238905Sborman /* 21338905Sborman * Handle RFC 10xx Telnet linemode option additions 21438905Sborman * to command stream (EOF, SUSP, ABORT). 21538905Sborman */ 21638905Sborman case xEOF: 21738905Sborman doeof(); 21838905Sborman break; 21938905Sborman 22038905Sborman case SUSP: 22138905Sborman sendsusp(); 22238905Sborman break; 22338905Sborman 22438905Sborman case ABORT: 22538905Sborman sendbrk(); 22638905Sborman break; 22738905Sborman 22838905Sborman case IAC: 22938905Sborman *pfrontp++ = c; 23038905Sborman break; 23138905Sborman } 23238905Sborman state = TS_DATA; 23338905Sborman break; 23438905Sborman 23538905Sborman case TS_SB: 23638905Sborman if (c == IAC) { 23738905Sborman state = TS_SE; 23838905Sborman } else { 23938905Sborman SB_ACCUM(c); 24038905Sborman } 24138905Sborman break; 24238905Sborman 24338905Sborman case TS_SE: 24438905Sborman if (c != SE) { 24538905Sborman if (c != IAC) { 24638905Sborman /* 24738905Sborman * bad form of suboption negotiation. 24838905Sborman * handle it in such a way as to avoid 24938905Sborman * damage to local state. Parse 25038905Sborman * suboption buffer found so far, 25138905Sborman * then treat remaining stream as 25238905Sborman * another command sequence. 25338905Sborman */ 25438905Sborman SB_TERM(); 25538905Sborman suboption(); 25638905Sborman state = TS_IAC; 25738905Sborman goto gotiac; 25838905Sborman } 25938905Sborman SB_ACCUM(c); 26038905Sborman state = TS_SB; 26138905Sborman } else { 26238905Sborman SB_TERM(); 26338905Sborman suboption(); /* handle sub-option */ 26438905Sborman state = TS_DATA; 26538905Sborman } 26638905Sborman break; 26738905Sborman 26838905Sborman case TS_WILL: 26939503Sborman willoption(c); 27038905Sborman state = TS_DATA; 27138905Sborman continue; 27238905Sborman 27338905Sborman case TS_WONT: 27439503Sborman wontoption(c); 27538905Sborman state = TS_DATA; 27638905Sborman continue; 27738905Sborman 27838905Sborman case TS_DO: 27939503Sborman dooption(c); 28038905Sborman state = TS_DATA; 28138905Sborman continue; 28238905Sborman 28338905Sborman case TS_DONT: 28439503Sborman dontoption(c); 28538905Sborman state = TS_DATA; 28638905Sborman continue; 28738905Sborman 28838905Sborman default: 28938905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 29038905Sborman printf("telnetd: panic state=%d\n", state); 29138905Sborman exit(1); 29238905Sborman } 29338905Sborman } 294*40242Sborman #if defined(CRAY2) && defined(UNICOS5) 29538905Sborman if (!linemode) { 29638905Sborman char xptyobuf[BUFSIZ+NETSLOP]; 29738905Sborman char xbuf2[BUFSIZ]; 29838905Sborman register char *cp; 29938905Sborman int n = pfrontp - opfrontp, oc; 30038905Sborman bcopy(opfrontp, xptyobuf, n); 30138905Sborman pfrontp = opfrontp; 30238905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, 30338905Sborman xbuf2, &oc, BUFSIZ); 30438905Sborman for (cp = xbuf2; oc > 0; --oc) 30538905Sborman if ((*nfrontp++ = *cp++) == IAC) 30638905Sborman *nfrontp++ = IAC; 30738905Sborman } 308*40242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 30938905Sborman } /* end of telrcv */ 31038905Sborman 31138905Sborman /* 31238905Sborman * The will/wont/do/dont state machines are based on Dave Borman's 31338905Sborman * Telnet option processing state machine. We keep track of the full 31438905Sborman * state of the option negotiation with the following state variables 31538905Sborman * myopts, hisopts - The last fully negotiated state for each 31638905Sborman * side of the connection. 31738905Sborman * mywants, hiswants - The state we wish to be in after a completed 31838905Sborman * negotiation. (hiswants is slightly misleading, 31938905Sborman * this is more precisely the state I want him to 32038905Sborman * be in. 32138905Sborman * resp - We count the number of requests we have sent out. 32238905Sborman * 32338905Sborman * These correspond to the following states: 32438905Sborman * my_state = the last negotiated state 32538905Sborman * want_state = what I want the state to go to 32638905Sborman * want_resp = how many requests I have sent 32738905Sborman * All state defaults are negative, and resp defaults to 0. 32838905Sborman * 32938905Sborman * When initiating a request to change state to new_state: 33038905Sborman * 33138905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 33238905Sborman * do nothing; 33338905Sborman * } else { 33438905Sborman * want_state = new_state; 33538905Sborman * send new_state; 33638905Sborman * want_resp++; 33738905Sborman * } 33838905Sborman * 33938905Sborman * When receiving new_state: 34038905Sborman * 34138905Sborman * if (want_resp) { 34238905Sborman * want_resp--; 34338905Sborman * if (want_resp && (new_state == my_state)) 34438905Sborman * want_resp--; 34538905Sborman * } 34638905Sborman * if ((want_resp == 0) && (new_state != want_state)) { 34738905Sborman * if (ok_to_switch_to new_state) 34838905Sborman * want_state = new_state; 34938905Sborman * else 35038905Sborman * want_resp++; 35138905Sborman * send want_state; 35238905Sborman * } 35338905Sborman * my_state = new_state; 35438905Sborman * 35538905Sborman * Note that new_state is implied in these functions by the function itself. 35638905Sborman * will and do imply positive new_state, wont and dont imply negative. 35738905Sborman * 35838905Sborman * Finally, there is one catch. If we send a negative response to a 35938905Sborman * positive request, my_state will be the positive while want_state will 36038905Sborman * remain negative. my_state will revert to negative when the negative 36138905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells 36238905Sborman * us not only the last negotiated state, but also tells us what the peer 36338905Sborman * wants to be doing as well. It is important to understand this difference 36438905Sborman * as we may wish to be processing data streams based on our desired state 36538905Sborman * (want_state) or based on what the peer thinks the state is (my_state). 36638905Sborman * 36738905Sborman * This all works fine because if the peer sends a positive request, the data 36838905Sborman * that we receive prior to negative acknowlegment will probably be affected 36938905Sborman * by the positive state, and we can process it as such (if we can; if we 37038905Sborman * can't then it really doesn't matter). If it is that important, then the 37138905Sborman * peer probably should be buffering until this option state negotiation 37238905Sborman * is complete. 37338905Sborman * 37438905Sborman */ 37539503Sborman send_do(option, init) 37639503Sborman int option, init; 37738905Sborman { 37839503Sborman if (init) { 37939503Sborman if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) || 38039503Sborman hiswants[option] == OPT_YES) 38139503Sborman return; 38239531Sborman /* 38339531Sborman * Special case for TELOPT_TM: We send a DO, but pretend 38439531Sborman * that we sent a DONT, so that we can send more DOs if 38539531Sborman * we want to. 38639531Sborman */ 38739531Sborman if (option == TELOPT_TM) 38839531Sborman hiswants[option] = OPT_NO; 38939531Sborman else 39039531Sborman hiswants[option] = OPT_YES; 39139503Sborman do_dont_resp[option]++; 39239503Sborman } 39339503Sborman (void) sprintf(nfrontp, doopt, option); 39439503Sborman nfrontp += sizeof (dont) - 2; 39539503Sborman } 39639503Sborman 39739503Sborman willoption(option) 39839503Sborman int option; 39939503Sborman { 40038905Sborman int changeok = 0; 40138905Sborman 40239503Sborman /* 40339503Sborman * process input from peer. 40439503Sborman */ 40539503Sborman 40639503Sborman if (do_dont_resp[option]) { 40739503Sborman do_dont_resp[option]--; 40839503Sborman if (do_dont_resp[option] && hisopts[option] == OPT_YES) 40939503Sborman do_dont_resp[option]--; 41039503Sborman } 41139531Sborman if (do_dont_resp[option] == 0) { 41239531Sborman if (hiswants[option] != OPT_YES) { 41338905Sborman switch (option) { 41438905Sborman 41538905Sborman case TELOPT_BINARY: 41638905Sborman init_termbuf(); 41738905Sborman tty_binaryin(1); 41838905Sborman set_termbuf(); 41938905Sborman changeok++; 42038905Sborman break; 42138905Sborman 42238905Sborman case TELOPT_ECHO: 42339503Sborman not42 = 0; /* looks like a 4.2 system */ 42439503Sborman #ifdef notdef 42538905Sborman /* 42639503Sborman * Now, in a 4.2 system, to break them out of 42739503Sborman * ECHOing (to the terminal) mode, we need to 42839503Sborman * send a WILL ECHO. 42938905Sborman */ 43038905Sborman if (myopts[TELOPT_ECHO] == OPT_YES) { 43139503Sborman send_will(TELOPT_ECHO, 1); 43238905Sborman } 43339503Sborman #else 43438905Sborman /* 43539503Sborman * "WILL ECHO". Kludge upon kludge! 43639503Sborman * A 4.2 client is now echoing user input at 43739503Sborman * the tty. This is probably undesireable and 43839503Sborman * it should be stopped. The client will 43939503Sborman * respond WONT TM to the DO TM that we send to 44039503Sborman * check for kludge linemode. When the WONT TM 44139503Sborman * arrives, linemode will be turned off and a 44239503Sborman * change propogated to the pty. This change 44339503Sborman * will cause us to process the new pty state 44439503Sborman * in localstat(), which will notice that 44539503Sborman * linemode is off and send a WILL ECHO 44639503Sborman * so that we are properly in character mode and 44739503Sborman * all is well. 44839503Sborman */ 44939503Sborman #endif 45039503Sborman /* 45138905Sborman * Fool the state machine into sending a don't. 45239503Sborman * This also allows the initial echo sending 45339503Sborman * code to break out of the loop that it is 45438905Sborman * in. (Look in telnet()) 45538905Sborman */ 45638905Sborman hiswants[TELOPT_ECHO] = OPT_NO; 45738905Sborman break; 45838905Sborman 45938905Sborman case TELOPT_TM: 46038905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 46138905Sborman /* 46239503Sborman * This telnetd implementation does not really 46339503Sborman * support timing marks, it just uses them to 46439503Sborman * support the kludge linemode stuff. If we 46539503Sborman * receive a will or wont TM in response to our 46639503Sborman * do TM request that may have been sent to 46739503Sborman * determine kludge linemode support, process 46839503Sborman * it, otherwise TM should get a negative 46939503Sborman * response back. 47038905Sborman */ 47138905Sborman /* 47238905Sborman * Handle the linemode kludge stuff. 47338905Sborman * If we are not currently supporting any 47438905Sborman * linemode at all, then we assume that this 47538905Sborman * is the client telling us to use kludge 47638905Sborman * linemode in response to our query. Set the 47738905Sborman * linemode type that is to be supported, note 47838905Sborman * that the client wishes to use linemode, and 47938905Sborman * eat the will TM as though it never arrived. 48038905Sborman */ 48138905Sborman if (lmodetype < KLUDGE_LINEMODE) { 48238905Sborman lmodetype = KLUDGE_LINEMODE; 48338905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 48439503Sborman send_wont(TELOPT_SGA, 1); 48538905Sborman } 48638905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 48738905Sborman /* 48839531Sborman * We never respond to a WILL TM, and 48939531Sborman * we leave the state OPT_NO. 49038905Sborman */ 49138905Sborman return; 49238905Sborman 49338905Sborman case TELOPT_LFLOW: 49438905Sborman /* 49539503Sborman * If we are going to support flow control 49639503Sborman * option, then don't worry peer that we can't 49739503Sborman * change the flow control characters. 49838905Sborman */ 49938905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 50038905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 50138905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 50238905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 50338905Sborman case TELOPT_TTYPE: 50438905Sborman case TELOPT_SGA: 50538905Sborman case TELOPT_NAWS: 50638905Sborman case TELOPT_TSPEED: 50739531Sborman changeok++; 50839531Sborman break; 50939531Sborman 51038905Sborman #ifdef LINEMODE 51138905Sborman case TELOPT_LINEMODE: 51239531Sborman # ifdef KLUDGELINEMODE 51339531Sborman /* 51439531Sborman * Note client's desire to use linemode. 51539531Sborman */ 51639531Sborman lmodetype = REAL_LINEMODE; 51739531Sborman # endif /* KLUDGELINEMODE */ 51839531Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 51938905Sborman changeok++; 52038905Sborman break; 52139531Sborman #endif /* LINEMODE */ 52238905Sborman 52338905Sborman default: 52438905Sborman break; 52538905Sborman } 52639503Sborman if (changeok) { 52738905Sborman hiswants[option] = OPT_YES; 52839503Sborman send_do(option, 0); 52939503Sborman } else { 53039503Sborman do_dont_resp[option]++; 53139503Sborman send_dont(option, 0); 53238905Sborman } 53339531Sborman } 53438905Sborman } 53539503Sborman hisopts[option] = OPT_YES; 53638905Sborman } /* end of willoption */ 53738905Sborman 53839503Sborman send_dont(option, init) 53939503Sborman int option, init; 54038905Sborman { 54139503Sborman if (init) { 54239503Sborman if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) || 54339503Sborman hiswants[option] == OPT_NO) 54439503Sborman return; 54539503Sborman hiswants[option] = OPT_NO; 54639503Sborman do_dont_resp[option]++; 54739503Sborman } 54839503Sborman (void) sprintf(nfrontp, dont, option); 54939503Sborman nfrontp += sizeof (doopt) - 2; 55039503Sborman } 55139503Sborman 55239503Sborman wontoption(option) 55339503Sborman int option; 55439503Sborman { 55538905Sborman char *fmt = (char *)0; 55638905Sborman 55738905Sborman /* 55838905Sborman * Process client input. 55938905Sborman */ 56039503Sborman 56139503Sborman if (do_dont_resp[option]) { 56239503Sborman do_dont_resp[option]--; 56339503Sborman if (do_dont_resp[option] && hisopts[option] == OPT_NO) 56439503Sborman do_dont_resp[option]--; 56539503Sborman } 56639531Sborman if (do_dont_resp[option] == 0) { 56739531Sborman if (hiswants[option] != OPT_NO) { 56839503Sborman /* it is always ok to change to negative state */ 56938905Sborman switch (option) { 57038905Sborman case TELOPT_ECHO: 57139503Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 57238905Sborman break; 57338905Sborman 57438905Sborman case TELOPT_BINARY: 57538905Sborman init_termbuf(); 57638905Sborman tty_binaryin(0); 57738905Sborman set_termbuf(); 57838905Sborman break; 57938905Sborman 58038905Sborman #ifdef LINEMODE 58138905Sborman case TELOPT_LINEMODE: 58238905Sborman # ifdef KLUDGELINEMODE 58338905Sborman /* 58438905Sborman * If real linemode is supported, then client is 58538905Sborman * asking to turn linemode off. 58638905Sborman */ 58738905Sborman if (lmodetype == REAL_LINEMODE) 58838905Sborman # endif /* KLUDGELINEMODE */ 58938905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 59038905Sborman break; 59138905Sborman #endif LINEMODE 59238905Sborman 59338905Sborman case TELOPT_TM: 59438905Sborman /* 59539503Sborman * If we get a WONT TM, and had sent a DO TM, 59639503Sborman * don't respond with a DONT TM, just leave it 59739503Sborman * as is. Short circut the state machine to 59839531Sborman * achive this. 59938905Sborman */ 60038905Sborman hiswants[TELOPT_TM] = OPT_NO; 60138905Sborman return; 60238905Sborman 60338905Sborman case TELOPT_LFLOW: 60438905Sborman /* 60539503Sborman * If we are not going to support flow control 60639503Sborman * option, then let peer know that we can't 60739503Sborman * change the flow control characters. 60838905Sborman */ 60938905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 61038905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 61138905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 61238905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 61338905Sborman break; 61438905Sborman 61538905Sborman default: 61638905Sborman break; 61738905Sborman } 61839503Sborman hiswants[option] = OPT_NO; 61939503Sborman fmt = dont; 62039503Sborman send_dont(option, 0); 62139531Sborman } else { 62239531Sborman switch (option) { 62339531Sborman case TELOPT_TM: 62439531Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 62539531Sborman if (lmodetype < REAL_LINEMODE) { 62639531Sborman lmodetype = NO_LINEMODE; 62739531Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 62839531Sborman send_will(TELOPT_SGA, 1); 62939531Sborman /*@*/ send_will(TELOPT_ECHO, 1); 63039531Sborman } 63139531Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 63239531Sborman default: 63339531Sborman break; 63439531Sborman } 63539531Sborman } 63638905Sborman } 63739503Sborman hisopts[option] = OPT_NO; 63838905Sborman 63939503Sborman } /* end of wontoption */ 64038905Sborman 64139503Sborman send_will(option, init) 64239503Sborman int option, init; 64339503Sborman { 64439503Sborman if (init) { 64539503Sborman if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)|| 64639503Sborman mywants[option] == OPT_YES) 64739503Sborman return; 64839503Sborman mywants[option] = OPT_YES; 64939503Sborman will_wont_resp[option]++; 65038905Sborman } 65139503Sborman (void) sprintf(nfrontp, will, option); 65239503Sborman nfrontp += sizeof (doopt) - 2; 65339503Sborman } 65438905Sborman 65539503Sborman dooption(option) 65639503Sborman int option; 65738905Sborman { 65838905Sborman int changeok = 0; 65938905Sborman 66038905Sborman /* 66138905Sborman * Process client input. 66238905Sborman */ 66339503Sborman 66439503Sborman if (will_wont_resp[option]) { 66539503Sborman will_wont_resp[option]--; 66639503Sborman if (will_wont_resp[option] && myopts[option] == OPT_YES) 66739503Sborman will_wont_resp[option]--; 66839503Sborman } 66939503Sborman if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) { 67038905Sborman switch (option) { 67138905Sborman case TELOPT_ECHO: 67238905Sborman #ifdef LINEMODE 67339503Sborman if (lmodetype == NO_LINEMODE) { 67438905Sborman #endif 67538905Sborman init_termbuf(); 67638905Sborman tty_setecho(1); 67738905Sborman set_termbuf(); 67838905Sborman #ifdef LINEMODE 67938905Sborman } 68038905Sborman #endif 68138905Sborman changeok++; 68238905Sborman break; 68338905Sborman 68438905Sborman case TELOPT_BINARY: 68538905Sborman init_termbuf(); 68638905Sborman tty_binaryout(1); 68738905Sborman set_termbuf(); 68838905Sborman changeok++; 68938905Sborman break; 69038905Sborman 69138905Sborman case TELOPT_SGA: 69238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 69338905Sborman /* 69439503Sborman * If kludge linemode is in use, then we must 69539503Sborman * process an incoming do SGA for linemode 69639503Sborman * purposes. 69738905Sborman */ 69838905Sborman if (lmodetype == KLUDGE_LINEMODE) { 69938905Sborman /* 70039503Sborman * Receipt of "do SGA" in kludge 70139503Sborman * linemode is the peer asking us to 70239503Sborman * turn off linemode. Make note of 70339503Sborman * the request. 70438905Sborman */ 70538905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 70638905Sborman /* 70739503Sborman * If linemode did not get turned off 70839503Sborman * then don't tell peer that we did. 70939503Sborman * Breaking here forces a wont SGA to 71039503Sborman * be returned. 71138905Sborman */ 71238905Sborman if (linemode) 71338905Sborman break; 71438905Sborman } 71538905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 71638905Sborman changeok++; 71738905Sborman break; 71838905Sborman 71938905Sborman case TELOPT_STATUS: 72038905Sborman changeok++; 72138905Sborman break; 72238905Sborman 72338905Sborman case TELOPT_TM: 72439503Sborman /* 72539503Sborman * Special case for TM. We send a WILL, but 72639503Sborman * pretend we sent a WONT. 72739503Sborman */ 72839503Sborman send_will(option, 0); 72939503Sborman mywants[option] = OPT_NO; 73039503Sborman myopts[option] = OPT_NO; 73139503Sborman return; 73239503Sborman 73338905Sborman case TELOPT_LINEMODE: 73438905Sborman case TELOPT_TTYPE: 73538905Sborman case TELOPT_NAWS: 73638905Sborman case TELOPT_TSPEED: 73738905Sborman case TELOPT_LFLOW: 73838905Sborman default: 73938905Sborman break; 74038905Sborman } 74139503Sborman if (changeok) { 74238905Sborman mywants[option] = OPT_YES; 74339503Sborman send_will(option, 0); 74439503Sborman } else { 74539503Sborman will_wont_resp[option]++; 74639503Sborman send_wont(option, 0); 74738905Sborman } 74838905Sborman } 74939503Sborman myopts[option] = OPT_YES; 75038905Sborman 75138905Sborman } /* end of dooption */ 75238905Sborman 75339503Sborman send_wont(option, init) 75439503Sborman int option, init; 75539503Sborman { 75639503Sborman if (init) { 75739503Sborman if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) || 75839503Sborman mywants[option] == OPT_NO) 75939503Sborman return; 76039503Sborman mywants[option] = OPT_NO; 76139503Sborman will_wont_resp[option]++; 76239503Sborman } 76339503Sborman (void) sprintf(nfrontp, wont, option); 76439503Sborman nfrontp += sizeof (wont) - 2; 76539503Sborman } 76638905Sborman 76739503Sborman dontoption(option) 76839503Sborman int option; 76938905Sborman { 77038905Sborman /* 77138905Sborman * Process client input. 77238905Sborman */ 773*40242Sborman 77439503Sborman if (will_wont_resp[option]) { 77539503Sborman will_wont_resp[option]--; 77639503Sborman if (will_wont_resp[option] && myopts[option] == OPT_NO) 77739503Sborman will_wont_resp[option]--; 77839503Sborman } 77939503Sborman if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) { 78038905Sborman switch (option) { 78138905Sborman case TELOPT_BINARY: 78238905Sborman init_termbuf(); 78338905Sborman tty_binaryout(0); 78438905Sborman set_termbuf(); 78538905Sborman break; 78638905Sborman 78739503Sborman case TELOPT_ECHO: /* we should stop echoing */ 78838905Sborman #ifdef LINEMODE 78939503Sborman if (lmodetype == NO_LINEMODE) { 79038905Sborman #endif 79138905Sborman init_termbuf(); 79238905Sborman tty_setecho(0); 79338905Sborman set_termbuf(); 79438905Sborman #ifdef LINEMODE 79538905Sborman } 79638905Sborman #endif 79738905Sborman break; 79838905Sborman 79938905Sborman case TELOPT_SGA: 80038905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 80138905Sborman /* 80239503Sborman * If kludge linemode is in use, then we 80339503Sborman * must process an incoming do SGA for 80439503Sborman * linemode purposes. 80538905Sborman */ 80638905Sborman if (lmodetype == KLUDGE_LINEMODE) { 80738905Sborman /* 80839503Sborman * The client is asking us to turn 80939503Sborman * linemode on. 81038905Sborman */ 81138905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 81238905Sborman /* 81339503Sborman * If we did not turn line mode on, 81439503Sborman * then what do we say? Will SGA? 81539503Sborman * This violates design of telnet. 81639503Sborman * Gross. Very Gross. 81738905Sborman */ 81838905Sborman } 81938905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 82038905Sborman 82138905Sborman default: 82238905Sborman break; 82338905Sborman } 82438905Sborman 82539503Sborman mywants[option] = OPT_NO; 82639503Sborman send_wont(option, 0); 82738905Sborman } 82839503Sborman myopts[option] = OPT_NO; 82938905Sborman 83038905Sborman } /* end of dontoption */ 83138905Sborman 83238905Sborman /* 83338905Sborman * suboption() 83438905Sborman * 83538905Sborman * Look at the sub-option buffer, and try to be helpful to the other 83638905Sborman * side. 83738905Sborman * 83838905Sborman * Currently we recognize: 83938905Sborman * 84038905Sborman * Terminal type is 84138905Sborman * Linemode 84238905Sborman * Window size 84338905Sborman * Terminal speed 84438905Sborman */ 84538905Sborman suboption() 84638905Sborman { 84738905Sborman register int subchar; 84838905Sborman 84938905Sborman subchar = SB_GET(); 85038905Sborman switch (subchar) { 85138905Sborman case TELOPT_TSPEED: { 85238905Sborman register int xspeed, rspeed; 85338905Sborman 85438905Sborman if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ 85538905Sborman break; 85638905Sborman 85738905Sborman settimer(tspeedsubopt); 85838905Sborman 85938905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 86038905Sborman return; 86138905Sborman 86238905Sborman xspeed = atoi(subpointer); 86338905Sborman 86438905Sborman while (SB_GET() != ',' && !SB_EOF()); 86538905Sborman if (SB_EOF()) 86638905Sborman return; 86738905Sborman 86838905Sborman rspeed = atoi(subpointer); 86938905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 87038905Sborman 87138905Sborman break; 87238905Sborman 87338905Sborman } /* end of case TELOPT_TSPEED */ 87438905Sborman 87538905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 87638905Sborman static char terminalname[5+41] = "TERM="; 87738905Sborman 87838997Sborman if (hisopts[TELOPT_TTYPE] == OPT_NO) /* Ignore if option disabled */ 87938905Sborman break; 88038905Sborman settimer(ttypesubopt); 88138905Sborman 88238905Sborman if (SB_GET() != TELQUAL_IS) { 88338905Sborman return; /* ??? XXX but, this is the most robust */ 88438905Sborman } 88538905Sborman 88638905Sborman terminaltype = terminalname+sizeof("TERM=")-1; 88738905Sborman 88838905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 88938905Sborman !SB_EOF()) { 89038905Sborman register int c; 89138905Sborman 89238905Sborman c = SB_GET(); 89338905Sborman if (isupper(c)) { 89438905Sborman c = tolower(c); 89538905Sborman } 89638905Sborman *terminaltype++ = c; /* accumulate name */ 89738905Sborman } 89838905Sborman *terminaltype = 0; 89938905Sborman terminaltype = terminalname; 90038905Sborman break; 90138905Sborman } /* end of case TELOPT_TTYPE */ 90238905Sborman 90338905Sborman case TELOPT_NAWS: { 90438905Sborman register int xwinsize, ywinsize; 90538905Sborman 90638905Sborman if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ 90738905Sborman break; 90838905Sborman 90938905Sborman if (SB_EOF()) 91038905Sborman return; 91138905Sborman xwinsize = SB_GET() << 8; 91238905Sborman if (SB_EOF()) 91338905Sborman return; 91438905Sborman xwinsize |= SB_GET(); 91538905Sborman if (SB_EOF()) 91638905Sborman return; 91738905Sborman ywinsize = SB_GET() << 8; 91838905Sborman if (SB_EOF()) 91938905Sborman return; 92038905Sborman ywinsize |= SB_GET(); 92138905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 92238905Sborman 92338905Sborman break; 92438905Sborman 92538905Sborman } /* end of case TELOPT_NAWS */ 92638905Sborman 92738905Sborman #ifdef LINEMODE 92838905Sborman case TELOPT_LINEMODE: { 92938905Sborman register int request; 93038905Sborman 93138905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ 93238905Sborman break; 93338905Sborman /* 93438905Sborman * Process linemode suboptions. 93538905Sborman */ 93638905Sborman if (SB_EOF()) break; /* garbage was sent */ 93738905Sborman request = SB_GET(); /* get will/wont */ 93838905Sborman if (SB_EOF()) break; /* another garbage check */ 93938905Sborman 94038905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 94138905Sborman /* 94238905Sborman * Process suboption buffer of slc's 94338905Sborman */ 94438905Sborman start_slc(1); 94538905Sborman do_opt_slc(subpointer, subend - subpointer); 94638905Sborman end_slc(0); 94738905Sborman 94838905Sborman } else if (request == LM_MODE) { 94938905Sborman useeditmode = SB_GET(); /* get mode flag */ 95038905Sborman clientstat(LM_MODE, 0, 0); 95138905Sborman } 95238905Sborman 95338905Sborman switch (SB_GET()) { /* what suboption? */ 95438905Sborman case LM_FORWARDMASK: 95538905Sborman /* 95638905Sborman * According to spec, only server can send request for 95738905Sborman * forwardmask, and client can only return a positive response. 95838905Sborman * So don't worry about it. 95938905Sborman */ 96038905Sborman 96138905Sborman default: 96238905Sborman break; 96338905Sborman } 964*40242Sborman break; 96538905Sborman } /* end of case TELOPT_LINEMODE */ 96638905Sborman #endif 96738905Sborman case TELOPT_STATUS: { 96838905Sborman int mode; 96938905Sborman 97038905Sborman mode = SB_GET(); 97138905Sborman switch (mode) { 97238905Sborman case TELQUAL_SEND: 97338905Sborman if (myopts[TELOPT_STATUS] == OPT_YES) 97438905Sborman send_status(); 97538905Sborman break; 97638905Sborman 97738905Sborman case TELQUAL_IS: 97838905Sborman break; 97938905Sborman 98038905Sborman default: 98138905Sborman break; 98238905Sborman } 983*40242Sborman break; 984*40242Sborman } /* end of case TELOPT_STATUS */ 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