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*38997Sborman static char sccsid[] = "@(#)state.c 5.2 (Berkeley) 09/05/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: 26838905Sborman willoption(c, 0); 26938905Sborman state = TS_DATA; 27038905Sborman continue; 27138905Sborman 27238905Sborman case TS_WONT: 27338905Sborman wontoption(c, 0); 27438905Sborman state = TS_DATA; 27538905Sborman continue; 27638905Sborman 27738905Sborman case TS_DO: 27838905Sborman dooption(c, 0); 27938905Sborman state = TS_DATA; 28038905Sborman continue; 28138905Sborman 28238905Sborman case TS_DONT: 28338905Sborman dontoption(c, 0); 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 */ 37738905Sborman willoption(option, request) 37838905Sborman int option, request; 37938905Sborman { 38038905Sborman int changeok = 0; 38138905Sborman char *fmt = (char *)0; 38238905Sborman 38338905Sborman /* 38438905Sborman * process input from peer. 38538905Sborman */ 38638905Sborman if (request == 0) { 38738905Sborman switch (option) { 38838905Sborman 38938905Sborman case TELOPT_BINARY: 39038905Sborman init_termbuf(); 39138905Sborman tty_binaryin(1); 39238905Sborman set_termbuf(); 39338905Sborman changeok++; 39438905Sborman break; 39538905Sborman 39638905Sborman case TELOPT_ECHO: 39738905Sborman not42 = 0; /* looks like a 4.2 system */ 39838905Sborman /* 39938905Sborman * Now, in a 4.2 system, to break them out of ECHOing 40038905Sborman * (to the terminal) mode, we need to send a 40138905Sborman * "WILL ECHO". Kludge upon kludge! 40238905Sborman */ 40338905Sborman if (myopts[TELOPT_ECHO] == OPT_YES) { 40438905Sborman dooption(TELOPT_ECHO, 1); 40538905Sborman } 40638905Sborman /* 40738905Sborman * Fool the state machine into sending a don't. 40838905Sborman * This also allows the initial echo sending code to 40938905Sborman * break out of the loop that it is 41038905Sborman * in. (Look in telnet()) 41138905Sborman */ 41238905Sborman hiswants[TELOPT_ECHO] = OPT_NO; 41338905Sborman break; 41438905Sborman 41538905Sborman case TELOPT_TM: 41638905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 41738905Sborman /* 41838905Sborman * This telnetd implementation does not really support 41938905Sborman * timing marks, it just uses them to support the kludge 42038905Sborman * linemode stuff. If we receive a will or wont TM in 42138905Sborman * response to our do TM request that may have been sent 42238905Sborman * to determine kludge linemode support, process it, 42338905Sborman * otherwise TM should get a negative response back. 42438905Sborman */ 42538905Sborman /* 42638905Sborman * Handle the linemode kludge stuff. 42738905Sborman * If we are not currently supporting any 42838905Sborman * linemode at all, then we assume that this 42938905Sborman * is the client telling us to use kludge 43038905Sborman * linemode in response to our query. Set the 43138905Sborman * linemode type that is to be supported, note 43238905Sborman * that the client wishes to use linemode, and 43338905Sborman * eat the will TM as though it never arrived. 43438905Sborman */ 43538905Sborman if (lmodetype < KLUDGE_LINEMODE) { 43638905Sborman lmodetype = KLUDGE_LINEMODE; 43738905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 43838905Sborman dontoption(TELOPT_SGA, 0); 43938905Sborman } 44038905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 44138905Sborman /* 44238905Sborman * cheat the state machine so that it 44338905Sborman * looks like we never sent the TM at 44438905Sborman * all. The bad part of this is that 44538905Sborman * if the client sends a will TM on his 44638905Sborman * own to turn on linemode, then he 44738905Sborman * won't get a response. 44838905Sborman */ 44938905Sborman hiswants[TELOPT_TM] = OPT_NO; 45038905Sborman resp[TELOPT_TM]--; 45138905Sborman return; 45238905Sborman 45338905Sborman case TELOPT_LFLOW: 45438905Sborman /* 45538905Sborman * If we are going to support flow control option, 45638905Sborman * then don't worry peer that we can't change the 45738905Sborman * flow control characters. 45838905Sborman */ 45938905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 46038905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 46138905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 46238905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 46338905Sborman case TELOPT_TTYPE: 46438905Sborman case TELOPT_SGA: 46538905Sborman case TELOPT_NAWS: 46638905Sborman case TELOPT_TSPEED: 46738905Sborman #ifdef LINEMODE 46838905Sborman case TELOPT_LINEMODE: 46938905Sborman #endif LINEMODE 47038905Sborman changeok++; 47138905Sborman break; 47238905Sborman 47338905Sborman default: 47438905Sborman break; 47538905Sborman } 47638905Sborman 47738905Sborman } 47838905Sborman 47938905Sborman if (request) { 48038905Sborman if (!((resp[option] == 0 && hisopts[option] == OPT_YES) || 48138905Sborman hiswants[option] == OPT_YES)) { 48238905Sborman hiswants[option] = OPT_YES; 48338905Sborman fmt = doopt; 48438905Sborman resp[option]++; 48538905Sborman } 48638905Sborman } else { 48738905Sborman if (resp[option]) { 48838905Sborman resp[option]--; 48938905Sborman if (resp[option] && hisopts[option] == OPT_YES) 49038905Sborman resp[option]--; 49138905Sborman } 49238905Sborman if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) { 49338905Sborman if (changeok) 49438905Sborman hiswants[option] = OPT_YES; 49538905Sborman else 49638905Sborman resp[option]++; 49738905Sborman fmt = (hiswants[option] ? doopt : dont); 49838905Sborman } 49938905Sborman hisopts[option] = OPT_YES; 50038905Sborman } 50138905Sborman 50238905Sborman if (fmt) { 50338905Sborman (void) sprintf(nfrontp, fmt, option); 50438905Sborman nfrontp += sizeof (dont) - 2; 50538905Sborman } 50638905Sborman 50738905Sborman /* 50838905Sborman * Handle other processing that should occur after we have 50938905Sborman * responded to client input. 51038905Sborman */ 51138905Sborman if (!request) { 51238905Sborman switch (option) { 51338905Sborman #ifdef LINEMODE 51438905Sborman case TELOPT_LINEMODE: 51538905Sborman # ifdef KLUDGELINEMODE 51638905Sborman /* 51738905Sborman * Note client's desire to use linemode. 51838905Sborman */ 51938905Sborman lmodetype = REAL_LINEMODE; 52038905Sborman # endif /* KLUDGELINEMODE */ 52138905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 52238905Sborman break; 52338905Sborman #endif LINEMODE 52438905Sborman 52538905Sborman default: 52638905Sborman break; 52738905Sborman } 52838905Sborman } 52938905Sborman 53038905Sborman } /* end of willoption */ 53138905Sborman 53238905Sborman wontoption(option, request) 53338905Sborman int option, request; 53438905Sborman { 53538905Sborman char *fmt = (char *)0; 53638905Sborman 53738905Sborman /* 53838905Sborman * Process client input. 53938905Sborman */ 54038905Sborman if (!request) { 54138905Sborman switch (option) { 54238905Sborman case TELOPT_ECHO: 54338905Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 54438905Sborman break; 54538905Sborman 54638905Sborman case TELOPT_BINARY: 54738905Sborman init_termbuf(); 54838905Sborman tty_binaryin(0); 54938905Sborman set_termbuf(); 55038905Sborman break; 55138905Sborman 55238905Sborman #ifdef LINEMODE 55338905Sborman case TELOPT_LINEMODE: 55438905Sborman # ifdef KLUDGELINEMODE 55538905Sborman /* 55638905Sborman * If real linemode is supported, then client is 55738905Sborman * asking to turn linemode off. 55838905Sborman */ 55938905Sborman if (lmodetype == REAL_LINEMODE) 56038905Sborman # endif /* KLUDGELINEMODE */ 56138905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 56238905Sborman break; 56338905Sborman #endif LINEMODE 56438905Sborman 56538905Sborman case TELOPT_TM: 56638905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 56738905Sborman if (lmodetype < REAL_LINEMODE) { 56838905Sborman lmodetype = NO_LINEMODE; 56938905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 57038905Sborman dooption(TELOPT_SGA, 0); 57138905Sborman } 57238905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 57338905Sborman /* 57438905Sborman * If we get a WONT TM, and had sent a DO TM, don't 57538905Sborman * respond with a DONT TM, just leave it as is. 57638905Sborman * Short circut the state machine to achive this. 57738905Sborman * The bad part of this is that if the client sends 57838905Sborman * a WONT TM on his own to turn off linemode, then he 57938905Sborman * won't get a response. 58038905Sborman */ 58138905Sborman hiswants[TELOPT_TM] = OPT_NO; 58238905Sborman resp[TELOPT_TM]--; 58338905Sborman return; 58438905Sborman 58538905Sborman case TELOPT_LFLOW: 58638905Sborman /* 58738905Sborman * If we are not going to support flow control option, 58838905Sborman * then let peer know that we can't change the 58938905Sborman * flow control characters. 59038905Sborman */ 59138905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 59238905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 59338905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 59438905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 59538905Sborman break; 59638905Sborman 59738905Sborman default: 59838905Sborman break; 59938905Sborman } 60038905Sborman } 60138905Sborman 60238905Sborman 60338905Sborman if (request) { 60438905Sborman if (!((resp[option] == 0 && hisopts[option] == OPT_NO) || 60538905Sborman hiswants[option] == OPT_NO)) { 60638905Sborman hiswants[option] = OPT_NO; 60738905Sborman fmt = dont; 60838905Sborman resp[option]++; 60938905Sborman } 61038905Sborman } else { 61138905Sborman if (resp[option]) { 61238905Sborman resp[option]--; 61338905Sborman if (resp[option] && hisopts[option] == OPT_NO) 61438905Sborman resp[option]--; 61538905Sborman } 61638905Sborman if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) { 61738905Sborman /* it is always ok to change to negative state */ 61838905Sborman hiswants[option] = OPT_NO; 61938905Sborman fmt = dont; 62038905Sborman } 62138905Sborman hisopts[option] = OPT_NO; 62238905Sborman } 62338905Sborman 62438905Sborman if (fmt) { 62538905Sborman (void) sprintf(nfrontp, fmt, option); 62638905Sborman nfrontp += sizeof (doopt) - 2; 62738905Sborman } 62838905Sborman 62938905Sborman } /* end of wontoption */ 63038905Sborman 63138905Sborman dooption(option, request) 63238905Sborman int option, request; 63338905Sborman { 63438905Sborman int changeok = 0; 63538905Sborman char *fmt = (char *)0; 63638905Sborman 63738905Sborman /* 63838905Sborman * Process client input. 63938905Sborman */ 64038905Sborman if (!request) { 64138905Sborman switch (option) { 64238905Sborman case TELOPT_ECHO: 64338905Sborman #ifdef LINEMODE 64438905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 64538905Sborman #endif 64638905Sborman init_termbuf(); 64738905Sborman tty_setecho(1); 64838905Sborman set_termbuf(); 64938905Sborman #ifdef LINEMODE 65038905Sborman } 65138905Sborman #endif 65238905Sborman changeok++; 65338905Sborman break; 65438905Sborman 65538905Sborman case TELOPT_BINARY: 65638905Sborman init_termbuf(); 65738905Sborman tty_binaryout(1); 65838905Sborman set_termbuf(); 65938905Sborman changeok++; 66038905Sborman break; 66138905Sborman 66238905Sborman case TELOPT_SGA: 66338905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 66438905Sborman /* 66538905Sborman * If kludge linemode is in use, then we must process 66638905Sborman * an incoming do SGA for linemode purposes. 66738905Sborman */ 66838905Sborman if (lmodetype == KLUDGE_LINEMODE) { 66938905Sborman /* 67038905Sborman * Receipt of "do SGA" in kludge linemode 67138905Sborman * is the peer asking us to turn off linemode. 67238905Sborman * Make note of the request. 67338905Sborman */ 67438905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 67538905Sborman /* 67638905Sborman * If linemode did not get turned off then 67738905Sborman * don't tell peer that we did. Breaking 67838905Sborman * here forces a wont SGA to be returned. 67938905Sborman */ 68038905Sborman if (linemode) 68138905Sborman break; 68238905Sborman } 68338905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 68438905Sborman changeok++; 68538905Sborman break; 68638905Sborman 68738905Sborman case TELOPT_STATUS: 68838905Sborman changeok++; 68938905Sborman break; 69038905Sborman 69138905Sborman case TELOPT_TM: 69238905Sborman case TELOPT_LINEMODE: 69338905Sborman case TELOPT_TTYPE: 69438905Sborman case TELOPT_NAWS: 69538905Sborman case TELOPT_TSPEED: 69638905Sborman case TELOPT_LFLOW: 69738905Sborman default: 69838905Sborman break; 69938905Sborman } 70038905Sborman } 70138905Sborman 70238905Sborman if (request) { 70338905Sborman if (!((resp[option] == 0 && myopts[option] == OPT_YES) || 70438905Sborman mywants[option] == OPT_YES)) { 70538905Sborman mywants[option] = OPT_YES; 70638905Sborman fmt = will; 70738905Sborman resp[option]++; 70838905Sborman } 70938905Sborman } else { 71038905Sborman if (resp[option]) { 71138905Sborman resp[option]--; 71238905Sborman if (resp[option] && myopts[option] == OPT_YES) 71338905Sborman resp[option]--; 71438905Sborman } 71538905Sborman if ((resp[option] == 0) && (mywants[option] != OPT_YES)) { 71638905Sborman if (changeok) 71738905Sborman mywants[option] = OPT_YES; 71838905Sborman else 71938905Sborman resp[option]++; 72038905Sborman fmt = (mywants[option] ? will : wont); 72138905Sborman } 72238905Sborman myopts[option] = OPT_YES; 72338905Sborman } 72438905Sborman 72538905Sborman if (fmt) { 72638905Sborman (void) sprintf(nfrontp, fmt, option); 72738905Sborman nfrontp += sizeof (doopt) - 2; 72838905Sborman } 72938905Sborman 73038905Sborman } /* end of dooption */ 73138905Sborman 73238905Sborman 73338905Sborman dontoption(option, request) 73438905Sborman int option, request; 73538905Sborman { 73638905Sborman char *fmt = (char *)0; 73738905Sborman 73838905Sborman /* 73938905Sborman * Process client input. 74038905Sborman */ 74138905Sborman if (!request) { 74238905Sborman switch (option) { 74338905Sborman case TELOPT_BINARY: 74438905Sborman init_termbuf(); 74538905Sborman tty_binaryout(0); 74638905Sborman set_termbuf(); 74738905Sborman break; 74838905Sborman 74938905Sborman case TELOPT_ECHO: /* we should stop echoing */ 75038905Sborman #ifdef LINEMODE 75138905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 75238905Sborman #endif 75338905Sborman init_termbuf(); 75438905Sborman tty_setecho(0); 75538905Sborman set_termbuf(); 75638905Sborman #ifdef LINEMODE 75738905Sborman } 75838905Sborman #endif 75938905Sborman break; 76038905Sborman 76138905Sborman case TELOPT_SGA: 76238905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 76338905Sborman /* 76438905Sborman * If kludge linemode is in use, then we must process an 76538905Sborman * incoming do SGA for linemode purposes. 76638905Sborman */ 76738905Sborman if (lmodetype == KLUDGE_LINEMODE) { 76838905Sborman /* 76938905Sborman * The client is asking us to turn linemode 77038905Sborman * on. 77138905Sborman */ 77238905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 77338905Sborman /* 77438905Sborman * If we did not turn line mode on, then what do 77538905Sborman * we say? Will SGA? This violates design of 77638905Sborman * telnet. Gross. Very Gross. 77738905Sborman */ 77838905Sborman } 77938905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 78038905Sborman 78138905Sborman default: 78238905Sborman break; 78338905Sborman } 78438905Sborman } 78538905Sborman 78638905Sborman if (request) { 78738905Sborman if (!((resp[option] == 0 && myopts[option] == OPT_NO) || 78838905Sborman mywants[option] == OPT_NO)) { 78938905Sborman mywants[option] = OPT_NO; 79038905Sborman fmt = wont; 79138905Sborman resp[option]++; 79238905Sborman } 79338905Sborman } else { 79438905Sborman if (resp[option]) { 79538905Sborman resp[option]--; 79638905Sborman if (resp[option] && myopts[option] == OPT_NO) 79738905Sborman resp[option]--; 79838905Sborman } 79938905Sborman if ((resp[option] == 0) && (mywants[option] != OPT_NO)) { 80038905Sborman mywants[option] = OPT_NO; 80138905Sborman fmt = wont; 80238905Sborman } 80338905Sborman myopts[option] = OPT_NO; 80438905Sborman } 80538905Sborman 80638905Sborman if (fmt) { 80738905Sborman (void) sprintf(nfrontp, fmt, option); 80838905Sborman nfrontp += sizeof (wont) - 2; 80938905Sborman } 81038905Sborman 81138905Sborman } /* end of dontoption */ 81238905Sborman 81338905Sborman /* 81438905Sborman * suboption() 81538905Sborman * 81638905Sborman * Look at the sub-option buffer, and try to be helpful to the other 81738905Sborman * side. 81838905Sborman * 81938905Sborman * Currently we recognize: 82038905Sborman * 82138905Sborman * Terminal type is 82238905Sborman * Linemode 82338905Sborman * Window size 82438905Sborman * Terminal speed 82538905Sborman */ 82638905Sborman suboption() 82738905Sborman { 82838905Sborman register int subchar; 82938905Sborman 83038905Sborman subchar = SB_GET(); 83138905Sborman switch (subchar) { 83238905Sborman case TELOPT_TSPEED: { 83338905Sborman register int xspeed, rspeed; 83438905Sborman 83538905Sborman if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ 83638905Sborman break; 83738905Sborman 83838905Sborman settimer(tspeedsubopt); 83938905Sborman 84038905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 84138905Sborman return; 84238905Sborman 84338905Sborman xspeed = atoi(subpointer); 84438905Sborman 84538905Sborman while (SB_GET() != ',' && !SB_EOF()); 84638905Sborman if (SB_EOF()) 84738905Sborman return; 84838905Sborman 84938905Sborman rspeed = atoi(subpointer); 85038905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 85138905Sborman 85238905Sborman break; 85338905Sborman 85438905Sborman } /* end of case TELOPT_TSPEED */ 85538905Sborman 85638905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 85738905Sborman static char terminalname[5+41] = "TERM="; 85838905Sborman 859*38997Sborman if (hisopts[TELOPT_TTYPE] == OPT_NO) /* Ignore if option disabled */ 86038905Sborman break; 86138905Sborman settimer(ttypesubopt); 86238905Sborman 86338905Sborman if (SB_GET() != TELQUAL_IS) { 86438905Sborman return; /* ??? XXX but, this is the most robust */ 86538905Sborman } 86638905Sborman 86738905Sborman terminaltype = terminalname+sizeof("TERM=")-1; 86838905Sborman 86938905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 87038905Sborman !SB_EOF()) { 87138905Sborman register int c; 87238905Sborman 87338905Sborman c = SB_GET(); 87438905Sborman if (isupper(c)) { 87538905Sborman c = tolower(c); 87638905Sborman } 87738905Sborman *terminaltype++ = c; /* accumulate name */ 87838905Sborman } 87938905Sborman *terminaltype = 0; 88038905Sborman terminaltype = terminalname; 88138905Sborman break; 88238905Sborman } /* end of case TELOPT_TTYPE */ 88338905Sborman 88438905Sborman case TELOPT_NAWS: { 88538905Sborman register int xwinsize, ywinsize; 88638905Sborman 88738905Sborman if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ 88838905Sborman break; 88938905Sborman 89038905Sborman if (SB_EOF()) 89138905Sborman return; 89238905Sborman xwinsize = SB_GET() << 8; 89338905Sborman if (SB_EOF()) 89438905Sborman return; 89538905Sborman xwinsize |= SB_GET(); 89638905Sborman if (SB_EOF()) 89738905Sborman return; 89838905Sborman ywinsize = SB_GET() << 8; 89938905Sborman if (SB_EOF()) 90038905Sborman return; 90138905Sborman ywinsize |= SB_GET(); 90238905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 90338905Sborman 90438905Sborman break; 90538905Sborman 90638905Sborman } /* end of case TELOPT_NAWS */ 90738905Sborman 90838905Sborman #ifdef LINEMODE 90938905Sborman case TELOPT_LINEMODE: { 91038905Sborman register int request; 91138905Sborman 91238905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ 91338905Sborman break; 91438905Sborman /* 91538905Sborman * Process linemode suboptions. 91638905Sborman */ 91738905Sborman if (SB_EOF()) break; /* garbage was sent */ 91838905Sborman request = SB_GET(); /* get will/wont */ 91938905Sborman if (SB_EOF()) break; /* another garbage check */ 92038905Sborman 92138905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 92238905Sborman /* 92338905Sborman * Process suboption buffer of slc's 92438905Sborman */ 92538905Sborman start_slc(1); 92638905Sborman do_opt_slc(subpointer, subend - subpointer); 92738905Sborman end_slc(0); 92838905Sborman 92938905Sborman } else if (request == LM_MODE) { 93038905Sborman useeditmode = SB_GET(); /* get mode flag */ 93138905Sborman clientstat(LM_MODE, 0, 0); 93238905Sborman } 93338905Sborman 93438905Sborman switch (SB_GET()) { /* what suboption? */ 93538905Sborman case LM_FORWARDMASK: 93638905Sborman /* 93738905Sborman * According to spec, only server can send request for 93838905Sborman * forwardmask, and client can only return a positive response. 93938905Sborman * So don't worry about it. 94038905Sborman */ 94138905Sborman 94238905Sborman default: 94338905Sborman break; 94438905Sborman } 94538905Sborman 94638905Sborman } /* end of case TELOPT_LINEMODE */ 94738905Sborman #endif 94838905Sborman case TELOPT_STATUS: { 94938905Sborman int mode; 95038905Sborman 95138905Sborman mode = SB_GET(); 95238905Sborman switch (mode) { 95338905Sborman case TELQUAL_SEND: 95438905Sborman if (myopts[TELOPT_STATUS] == OPT_YES) 95538905Sborman send_status(); 95638905Sborman break; 95738905Sborman 95838905Sborman case TELQUAL_IS: 95938905Sborman break; 96038905Sborman 96138905Sborman default: 96238905Sborman break; 96338905Sborman } 96438905Sborman } 96538905Sborman 96638905Sborman default: 96738905Sborman break; 96838905Sborman } /* end of switch */ 96938905Sborman 97038905Sborman } /* end of suboption */ 97138905Sborman 97238905Sborman #define ADD(c) *ncp++ = c; 97338905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 97438905Sborman send_status() 97538905Sborman { 97638905Sborman char statusbuf[256]; 97738905Sborman register char *ncp; 97838905Sborman register int i; 97938905Sborman 98038905Sborman ncp = statusbuf; 98138905Sborman 98238905Sborman netflush(); /* get rid of anything waiting to go out */ 98338905Sborman 98438905Sborman ADD(IAC); 98538905Sborman ADD(SB); 98638905Sborman ADD(TELOPT_STATUS); 98738905Sborman ADD(TELQUAL_IS); 98838905Sborman 98938905Sborman for (i = 0; i < NTELOPTS; i++) { 99038905Sborman if (myopts[i] == OPT_YES) { 99138905Sborman ADD(WILL); 99238905Sborman ADD_DATA(i); 99338905Sborman if (i == IAC) 99438905Sborman ADD(IAC); 99538905Sborman } 99638905Sborman if (hisopts[i] == OPT_YES) { 99738905Sborman ADD(DO); 99838905Sborman ADD_DATA(i); 99938905Sborman if (i == IAC) 100038905Sborman ADD(IAC); 100138905Sborman } 100238905Sborman } 100338905Sborman 100438905Sborman #ifdef LINEMODE 100538905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_YES) { 100638905Sborman char *cp, *cpe; 100738905Sborman int len; 100838905Sborman 100938905Sborman ADD(SB); 101038905Sborman ADD(TELOPT_LINEMODE); 101138905Sborman ADD(LM_MODE); 101238905Sborman ADD_DATA(editmode); 101338905Sborman if (editmode == IAC) 101438905Sborman ADD(IAC); 101538905Sborman ADD(SE); 101638905Sborman 101738905Sborman ADD(SB); 101838905Sborman ADD(TELOPT_LINEMODE); 101938905Sborman ADD(LM_SLC); 102038905Sborman start_slc(0); 102138905Sborman send_slc(); 102238905Sborman len = end_slc(&cp); 102338905Sborman for (cpe = cp + len; cp < cpe; cp++) 102438905Sborman ADD_DATA(*cp); 102538905Sborman ADD(SE); 102638905Sborman } 102738905Sborman #endif /* LINEMODE */ 102838905Sborman 102938905Sborman ADD(IAC); 103038905Sborman ADD(SE); 103138905Sborman 103238905Sborman writenet(statusbuf, ncp - statusbuf); 103338905Sborman netflush(); /* Send it on its way */ 103438905Sborman } 1035