1*38905Sborman /* 2*38905Sborman * Copyright (c) 1989 Regents of the University of California. 3*38905Sborman * All rights reserved. 4*38905Sborman * 5*38905Sborman * Redistribution and use in source and binary forms are permitted 6*38905Sborman * provided that the above copyright notice and this paragraph are 7*38905Sborman * duplicated in all such forms and that any documentation, 8*38905Sborman * advertising materials, and other materials related to such 9*38905Sborman * distribution and use acknowledge that the software was developed 10*38905Sborman * by the University of California, Berkeley. The name of the 11*38905Sborman * University may not be used to endorse or promote products derived 12*38905Sborman * from this software without specific prior written permission. 13*38905Sborman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*38905Sborman * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*38905Sborman * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16*38905Sborman */ 17*38905Sborman 18*38905Sborman #ifndef lint 19*38905Sborman static char sccsid[] = "@(#)state.c 5.1 (Berkeley) 09/01/89"; 20*38905Sborman #endif /* not lint */ 21*38905Sborman 22*38905Sborman #include "telnetd.h" 23*38905Sborman 24*38905Sborman char doopt[] = { IAC, DO, '%', 'c', 0 }; 25*38905Sborman char dont[] = { IAC, DONT, '%', 'c', 0 }; 26*38905Sborman char will[] = { IAC, WILL, '%', 'c', 0 }; 27*38905Sborman char wont[] = { IAC, WONT, '%', 'c', 0 }; 28*38905Sborman int not42 = 1; 29*38905Sborman 30*38905Sborman /* 31*38905Sborman * Buffer for sub-options, and macros 32*38905Sborman * for suboptions buffer manipulations 33*38905Sborman */ 34*38905Sborman char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 35*38905Sborman 36*38905Sborman #define SB_CLEAR() subpointer = subbuffer; 37*38905Sborman #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 38*38905Sborman #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 39*38905Sborman *subpointer++ = (c); \ 40*38905Sborman } 41*38905Sborman #define SB_GET() ((*subpointer++)&0xff) 42*38905Sborman #define SB_EOF() (subpointer >= subend) 43*38905Sborman 44*38905Sborman 45*38905Sborman 46*38905Sborman /* 47*38905Sborman * State for recv fsm 48*38905Sborman */ 49*38905Sborman #define TS_DATA 0 /* base state */ 50*38905Sborman #define TS_IAC 1 /* look for double IAC's */ 51*38905Sborman #define TS_CR 2 /* CR-LF ->'s CR */ 52*38905Sborman #define TS_SB 3 /* throw away begin's... */ 53*38905Sborman #define TS_SE 4 /* ...end's (suboption negotiation) */ 54*38905Sborman #define TS_WILL 5 /* will option negotiation */ 55*38905Sborman #define TS_WONT 6 /* wont " */ 56*38905Sborman #define TS_DO 7 /* do " */ 57*38905Sborman #define TS_DONT 8 /* dont " */ 58*38905Sborman 59*38905Sborman telrcv() 60*38905Sborman { 61*38905Sborman register int c; 62*38905Sborman static int state = TS_DATA; 63*38905Sborman #ifdef CRAY2 64*38905Sborman char *opfrontp = pfrontp; 65*38905Sborman #endif 66*38905Sborman 67*38905Sborman while (ncc > 0) { 68*38905Sborman if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 69*38905Sborman break; 70*38905Sborman c = *netip++ & 0377, ncc--; 71*38905Sborman switch (state) { 72*38905Sborman 73*38905Sborman case TS_CR: 74*38905Sborman state = TS_DATA; 75*38905Sborman /* Strip off \n or \0 after a \r */ 76*38905Sborman if ((c == 0) || (c == '\n')) { 77*38905Sborman break; 78*38905Sborman } 79*38905Sborman /* FALL THROUGH */ 80*38905Sborman 81*38905Sborman case TS_DATA: 82*38905Sborman if (c == IAC) { 83*38905Sborman state = TS_IAC; 84*38905Sborman break; 85*38905Sborman } 86*38905Sborman /* 87*38905Sborman * We now map \r\n ==> \r for pragmatic reasons. 88*38905Sborman * Many client implementations send \r\n when 89*38905Sborman * the user hits the CarriageReturn key. 90*38905Sborman * 91*38905Sborman * We USED to map \r\n ==> \n, since \r\n says 92*38905Sborman * that we want to be in column 1 of the next 93*38905Sborman * printable line, and \n is the standard 94*38905Sborman * unix way of saying that (\r is only good 95*38905Sborman * if CRMOD is set, which it normally is). 96*38905Sborman */ 97*38905Sborman if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 98*38905Sborman /* 99*38905Sborman * If we are operating in linemode, 100*38905Sborman * convert to local end-of-line. 101*38905Sborman */ 102*38905Sborman if ((linemode) && (ncc > 0)&&('\n' == *netip)) { 103*38905Sborman netip++; ncc--; 104*38905Sborman c = '\n'; 105*38905Sborman } else { 106*38905Sborman state = TS_CR; 107*38905Sborman } 108*38905Sborman } 109*38905Sborman *pfrontp++ = c; 110*38905Sborman break; 111*38905Sborman 112*38905Sborman case TS_IAC: 113*38905Sborman gotiac: switch (c) { 114*38905Sborman 115*38905Sborman /* 116*38905Sborman * Send the process on the pty side an 117*38905Sborman * interrupt. Do this with a NULL or 118*38905Sborman * interrupt char; depending on the tty mode. 119*38905Sborman */ 120*38905Sborman case IP: 121*38905Sborman interrupt(); 122*38905Sborman break; 123*38905Sborman 124*38905Sborman case BREAK: 125*38905Sborman sendbrk(); 126*38905Sborman break; 127*38905Sborman 128*38905Sborman /* 129*38905Sborman * Are You There? 130*38905Sborman */ 131*38905Sborman case AYT: 132*38905Sborman (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); 133*38905Sborman nfrontp += 9; 134*38905Sborman break; 135*38905Sborman 136*38905Sborman /* 137*38905Sborman * Abort Output 138*38905Sborman */ 139*38905Sborman case AO: 140*38905Sborman { 141*38905Sborman ptyflush(); /* half-hearted */ 142*38905Sborman init_termbuf(); 143*38905Sborman 144*38905Sborman if (slctab[SLC_AO].sptr && 145*38905Sborman *slctab[SLC_AO].sptr != '\377') { 146*38905Sborman *pfrontp++ = *slctab[SLC_AO].sptr; 147*38905Sborman } 148*38905Sborman 149*38905Sborman netclear(); /* clear buffer back */ 150*38905Sborman *nfrontp++ = IAC; 151*38905Sborman *nfrontp++ = DM; 152*38905Sborman neturg = nfrontp-1; /* off by one XXX */ 153*38905Sborman break; 154*38905Sborman } 155*38905Sborman 156*38905Sborman /* 157*38905Sborman * Erase Character and 158*38905Sborman * Erase Line 159*38905Sborman */ 160*38905Sborman case EC: 161*38905Sborman case EL: 162*38905Sborman { 163*38905Sborman unsigned char ch; 164*38905Sborman 165*38905Sborman ptyflush(); /* half-hearted */ 166*38905Sborman init_termbuf(); 167*38905Sborman ch = (c == EC) ? *slctab[SLC_EC].sptr : 168*38905Sborman *slctab[SLC_EL].sptr; 169*38905Sborman if (ch != '\377') 170*38905Sborman *pfrontp++ = ch; 171*38905Sborman break; 172*38905Sborman } 173*38905Sborman 174*38905Sborman /* 175*38905Sborman * Check for urgent data... 176*38905Sborman */ 177*38905Sborman case DM: 178*38905Sborman SYNCHing = stilloob(net); 179*38905Sborman settimer(gotDM); 180*38905Sborman break; 181*38905Sborman 182*38905Sborman 183*38905Sborman /* 184*38905Sborman * Begin option subnegotiation... 185*38905Sborman */ 186*38905Sborman case SB: 187*38905Sborman state = TS_SB; 188*38905Sborman SB_CLEAR(); 189*38905Sborman continue; 190*38905Sborman 191*38905Sborman case WILL: 192*38905Sborman state = TS_WILL; 193*38905Sborman continue; 194*38905Sborman 195*38905Sborman case WONT: 196*38905Sborman state = TS_WONT; 197*38905Sborman continue; 198*38905Sborman 199*38905Sborman case DO: 200*38905Sborman state = TS_DO; 201*38905Sborman continue; 202*38905Sborman 203*38905Sborman case DONT: 204*38905Sborman state = TS_DONT; 205*38905Sborman continue; 206*38905Sborman case EOR: 207*38905Sborman if (hisopts[TELOPT_EOR]) 208*38905Sborman doeof(); 209*38905Sborman break; 210*38905Sborman 211*38905Sborman /* 212*38905Sborman * Handle RFC 10xx Telnet linemode option additions 213*38905Sborman * to command stream (EOF, SUSP, ABORT). 214*38905Sborman */ 215*38905Sborman case xEOF: 216*38905Sborman doeof(); 217*38905Sborman break; 218*38905Sborman 219*38905Sborman case SUSP: 220*38905Sborman sendsusp(); 221*38905Sborman break; 222*38905Sborman 223*38905Sborman case ABORT: 224*38905Sborman sendbrk(); 225*38905Sborman break; 226*38905Sborman 227*38905Sborman case IAC: 228*38905Sborman *pfrontp++ = c; 229*38905Sborman break; 230*38905Sborman } 231*38905Sborman state = TS_DATA; 232*38905Sborman break; 233*38905Sborman 234*38905Sborman case TS_SB: 235*38905Sborman if (c == IAC) { 236*38905Sborman state = TS_SE; 237*38905Sborman } else { 238*38905Sborman SB_ACCUM(c); 239*38905Sborman } 240*38905Sborman break; 241*38905Sborman 242*38905Sborman case TS_SE: 243*38905Sborman if (c != SE) { 244*38905Sborman if (c != IAC) { 245*38905Sborman /* 246*38905Sborman * bad form of suboption negotiation. 247*38905Sborman * handle it in such a way as to avoid 248*38905Sborman * damage to local state. Parse 249*38905Sborman * suboption buffer found so far, 250*38905Sborman * then treat remaining stream as 251*38905Sborman * another command sequence. 252*38905Sborman */ 253*38905Sborman SB_TERM(); 254*38905Sborman suboption(); 255*38905Sborman state = TS_IAC; 256*38905Sborman goto gotiac; 257*38905Sborman } 258*38905Sborman SB_ACCUM(c); 259*38905Sborman state = TS_SB; 260*38905Sborman } else { 261*38905Sborman SB_TERM(); 262*38905Sborman suboption(); /* handle sub-option */ 263*38905Sborman state = TS_DATA; 264*38905Sborman } 265*38905Sborman break; 266*38905Sborman 267*38905Sborman case TS_WILL: 268*38905Sborman willoption(c, 0); 269*38905Sborman state = TS_DATA; 270*38905Sborman continue; 271*38905Sborman 272*38905Sborman case TS_WONT: 273*38905Sborman wontoption(c, 0); 274*38905Sborman state = TS_DATA; 275*38905Sborman continue; 276*38905Sborman 277*38905Sborman case TS_DO: 278*38905Sborman dooption(c, 0); 279*38905Sborman state = TS_DATA; 280*38905Sborman continue; 281*38905Sborman 282*38905Sborman case TS_DONT: 283*38905Sborman dontoption(c, 0); 284*38905Sborman state = TS_DATA; 285*38905Sborman continue; 286*38905Sborman 287*38905Sborman default: 288*38905Sborman syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 289*38905Sborman printf("telnetd: panic state=%d\n", state); 290*38905Sborman exit(1); 291*38905Sborman } 292*38905Sborman } 293*38905Sborman #if CRAY2 294*38905Sborman if (!linemode) { 295*38905Sborman char xptyobuf[BUFSIZ+NETSLOP]; 296*38905Sborman char xbuf2[BUFSIZ]; 297*38905Sborman register char *cp; 298*38905Sborman int n = pfrontp - opfrontp, oc; 299*38905Sborman bcopy(opfrontp, xptyobuf, n); 300*38905Sborman pfrontp = opfrontp; 301*38905Sborman pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, 302*38905Sborman xbuf2, &oc, BUFSIZ); 303*38905Sborman for (cp = xbuf2; oc > 0; --oc) 304*38905Sborman if ((*nfrontp++ = *cp++) == IAC) 305*38905Sborman *nfrontp++ = IAC; 306*38905Sborman } 307*38905Sborman #endif /* CRAY2 */ 308*38905Sborman } /* end of telrcv */ 309*38905Sborman 310*38905Sborman /* 311*38905Sborman * The will/wont/do/dont state machines are based on Dave Borman's 312*38905Sborman * Telnet option processing state machine. We keep track of the full 313*38905Sborman * state of the option negotiation with the following state variables 314*38905Sborman * myopts, hisopts - The last fully negotiated state for each 315*38905Sborman * side of the connection. 316*38905Sborman * mywants, hiswants - The state we wish to be in after a completed 317*38905Sborman * negotiation. (hiswants is slightly misleading, 318*38905Sborman * this is more precisely the state I want him to 319*38905Sborman * be in. 320*38905Sborman * resp - We count the number of requests we have sent out. 321*38905Sborman * 322*38905Sborman * These correspond to the following states: 323*38905Sborman * my_state = the last negotiated state 324*38905Sborman * want_state = what I want the state to go to 325*38905Sborman * want_resp = how many requests I have sent 326*38905Sborman * All state defaults are negative, and resp defaults to 0. 327*38905Sborman * 328*38905Sborman * When initiating a request to change state to new_state: 329*38905Sborman * 330*38905Sborman * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 331*38905Sborman * do nothing; 332*38905Sborman * } else { 333*38905Sborman * want_state = new_state; 334*38905Sborman * send new_state; 335*38905Sborman * want_resp++; 336*38905Sborman * } 337*38905Sborman * 338*38905Sborman * When receiving new_state: 339*38905Sborman * 340*38905Sborman * if (want_resp) { 341*38905Sborman * want_resp--; 342*38905Sborman * if (want_resp && (new_state == my_state)) 343*38905Sborman * want_resp--; 344*38905Sborman * } 345*38905Sborman * if ((want_resp == 0) && (new_state != want_state)) { 346*38905Sborman * if (ok_to_switch_to new_state) 347*38905Sborman * want_state = new_state; 348*38905Sborman * else 349*38905Sborman * want_resp++; 350*38905Sborman * send want_state; 351*38905Sborman * } 352*38905Sborman * my_state = new_state; 353*38905Sborman * 354*38905Sborman * Note that new_state is implied in these functions by the function itself. 355*38905Sborman * will and do imply positive new_state, wont and dont imply negative. 356*38905Sborman * 357*38905Sborman * Finally, there is one catch. If we send a negative response to a 358*38905Sborman * positive request, my_state will be the positive while want_state will 359*38905Sborman * remain negative. my_state will revert to negative when the negative 360*38905Sborman * acknowlegment arrives from the peer. Thus, my_state generally tells 361*38905Sborman * us not only the last negotiated state, but also tells us what the peer 362*38905Sborman * wants to be doing as well. It is important to understand this difference 363*38905Sborman * as we may wish to be processing data streams based on our desired state 364*38905Sborman * (want_state) or based on what the peer thinks the state is (my_state). 365*38905Sborman * 366*38905Sborman * This all works fine because if the peer sends a positive request, the data 367*38905Sborman * that we receive prior to negative acknowlegment will probably be affected 368*38905Sborman * by the positive state, and we can process it as such (if we can; if we 369*38905Sborman * can't then it really doesn't matter). If it is that important, then the 370*38905Sborman * peer probably should be buffering until this option state negotiation 371*38905Sborman * is complete. 372*38905Sborman * 373*38905Sborman * In processing options, request signifies whether this is a request 374*38905Sborman * to send or a response. request is true if this is a request to 375*38905Sborman * send generated locally. 376*38905Sborman */ 377*38905Sborman willoption(option, request) 378*38905Sborman int option, request; 379*38905Sborman { 380*38905Sborman int changeok = 0; 381*38905Sborman char *fmt = (char *)0; 382*38905Sborman 383*38905Sborman /* 384*38905Sborman * process input from peer. 385*38905Sborman */ 386*38905Sborman if (request == 0) { 387*38905Sborman switch (option) { 388*38905Sborman 389*38905Sborman case TELOPT_BINARY: 390*38905Sborman init_termbuf(); 391*38905Sborman tty_binaryin(1); 392*38905Sborman set_termbuf(); 393*38905Sborman changeok++; 394*38905Sborman break; 395*38905Sborman 396*38905Sborman case TELOPT_ECHO: 397*38905Sborman not42 = 0; /* looks like a 4.2 system */ 398*38905Sborman /* 399*38905Sborman * Now, in a 4.2 system, to break them out of ECHOing 400*38905Sborman * (to the terminal) mode, we need to send a 401*38905Sborman * "WILL ECHO". Kludge upon kludge! 402*38905Sborman */ 403*38905Sborman if (myopts[TELOPT_ECHO] == OPT_YES) { 404*38905Sborman dooption(TELOPT_ECHO, 1); 405*38905Sborman } 406*38905Sborman /* 407*38905Sborman * Fool the state machine into sending a don't. 408*38905Sborman * This also allows the initial echo sending code to 409*38905Sborman * break out of the loop that it is 410*38905Sborman * in. (Look in telnet()) 411*38905Sborman */ 412*38905Sborman hiswants[TELOPT_ECHO] = OPT_NO; 413*38905Sborman break; 414*38905Sborman 415*38905Sborman case TELOPT_TM: 416*38905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 417*38905Sborman /* 418*38905Sborman * This telnetd implementation does not really support 419*38905Sborman * timing marks, it just uses them to support the kludge 420*38905Sborman * linemode stuff. If we receive a will or wont TM in 421*38905Sborman * response to our do TM request that may have been sent 422*38905Sborman * to determine kludge linemode support, process it, 423*38905Sborman * otherwise TM should get a negative response back. 424*38905Sborman */ 425*38905Sborman /* 426*38905Sborman * Handle the linemode kludge stuff. 427*38905Sborman * If we are not currently supporting any 428*38905Sborman * linemode at all, then we assume that this 429*38905Sborman * is the client telling us to use kludge 430*38905Sborman * linemode in response to our query. Set the 431*38905Sborman * linemode type that is to be supported, note 432*38905Sborman * that the client wishes to use linemode, and 433*38905Sborman * eat the will TM as though it never arrived. 434*38905Sborman */ 435*38905Sborman if (lmodetype < KLUDGE_LINEMODE) { 436*38905Sborman lmodetype = KLUDGE_LINEMODE; 437*38905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 438*38905Sborman dontoption(TELOPT_SGA, 0); 439*38905Sborman } 440*38905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 441*38905Sborman /* 442*38905Sborman * cheat the state machine so that it 443*38905Sborman * looks like we never sent the TM at 444*38905Sborman * all. The bad part of this is that 445*38905Sborman * if the client sends a will TM on his 446*38905Sborman * own to turn on linemode, then he 447*38905Sborman * won't get a response. 448*38905Sborman */ 449*38905Sborman hiswants[TELOPT_TM] = OPT_NO; 450*38905Sborman resp[TELOPT_TM]--; 451*38905Sborman return; 452*38905Sborman 453*38905Sborman case TELOPT_LFLOW: 454*38905Sborman /* 455*38905Sborman * If we are going to support flow control option, 456*38905Sborman * then don't worry peer that we can't change the 457*38905Sborman * flow control characters. 458*38905Sborman */ 459*38905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 460*38905Sborman slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 461*38905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 462*38905Sborman slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 463*38905Sborman case TELOPT_TTYPE: 464*38905Sborman case TELOPT_SGA: 465*38905Sborman case TELOPT_NAWS: 466*38905Sborman case TELOPT_TSPEED: 467*38905Sborman #ifdef LINEMODE 468*38905Sborman case TELOPT_LINEMODE: 469*38905Sborman #endif LINEMODE 470*38905Sborman changeok++; 471*38905Sborman break; 472*38905Sborman 473*38905Sborman default: 474*38905Sborman break; 475*38905Sborman } 476*38905Sborman 477*38905Sborman } 478*38905Sborman 479*38905Sborman if (request) { 480*38905Sborman if (!((resp[option] == 0 && hisopts[option] == OPT_YES) || 481*38905Sborman hiswants[option] == OPT_YES)) { 482*38905Sborman hiswants[option] = OPT_YES; 483*38905Sborman fmt = doopt; 484*38905Sborman resp[option]++; 485*38905Sborman } 486*38905Sborman } else { 487*38905Sborman if (resp[option]) { 488*38905Sborman resp[option]--; 489*38905Sborman if (resp[option] && hisopts[option] == OPT_YES) 490*38905Sborman resp[option]--; 491*38905Sborman } 492*38905Sborman if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) { 493*38905Sborman if (changeok) 494*38905Sborman hiswants[option] = OPT_YES; 495*38905Sborman else 496*38905Sborman resp[option]++; 497*38905Sborman fmt = (hiswants[option] ? doopt : dont); 498*38905Sborman } 499*38905Sborman hisopts[option] = OPT_YES; 500*38905Sborman } 501*38905Sborman 502*38905Sborman if (fmt) { 503*38905Sborman (void) sprintf(nfrontp, fmt, option); 504*38905Sborman nfrontp += sizeof (dont) - 2; 505*38905Sborman } 506*38905Sborman 507*38905Sborman /* 508*38905Sborman * Handle other processing that should occur after we have 509*38905Sborman * responded to client input. 510*38905Sborman */ 511*38905Sborman if (!request) { 512*38905Sborman switch (option) { 513*38905Sborman #ifdef LINEMODE 514*38905Sborman case TELOPT_LINEMODE: 515*38905Sborman # ifdef KLUDGELINEMODE 516*38905Sborman /* 517*38905Sborman * Note client's desire to use linemode. 518*38905Sborman */ 519*38905Sborman lmodetype = REAL_LINEMODE; 520*38905Sborman # endif /* KLUDGELINEMODE */ 521*38905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 522*38905Sborman break; 523*38905Sborman #endif LINEMODE 524*38905Sborman 525*38905Sborman default: 526*38905Sborman break; 527*38905Sborman } 528*38905Sborman } 529*38905Sborman 530*38905Sborman } /* end of willoption */ 531*38905Sborman 532*38905Sborman wontoption(option, request) 533*38905Sborman int option, request; 534*38905Sborman { 535*38905Sborman char *fmt = (char *)0; 536*38905Sborman 537*38905Sborman /* 538*38905Sborman * Process client input. 539*38905Sborman */ 540*38905Sborman if (!request) { 541*38905Sborman switch (option) { 542*38905Sborman case TELOPT_ECHO: 543*38905Sborman not42 = 1; /* doesn't seem to be a 4.2 system */ 544*38905Sborman break; 545*38905Sborman 546*38905Sborman case TELOPT_BINARY: 547*38905Sborman init_termbuf(); 548*38905Sborman tty_binaryin(0); 549*38905Sborman set_termbuf(); 550*38905Sborman break; 551*38905Sborman 552*38905Sborman #ifdef LINEMODE 553*38905Sborman case TELOPT_LINEMODE: 554*38905Sborman # ifdef KLUDGELINEMODE 555*38905Sborman /* 556*38905Sborman * If real linemode is supported, then client is 557*38905Sborman * asking to turn linemode off. 558*38905Sborman */ 559*38905Sborman if (lmodetype == REAL_LINEMODE) 560*38905Sborman # endif /* KLUDGELINEMODE */ 561*38905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 562*38905Sborman break; 563*38905Sborman #endif LINEMODE 564*38905Sborman 565*38905Sborman case TELOPT_TM: 566*38905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 567*38905Sborman if (lmodetype < REAL_LINEMODE) { 568*38905Sborman lmodetype = NO_LINEMODE; 569*38905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 570*38905Sborman dooption(TELOPT_SGA, 0); 571*38905Sborman } 572*38905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 573*38905Sborman /* 574*38905Sborman * If we get a WONT TM, and had sent a DO TM, don't 575*38905Sborman * respond with a DONT TM, just leave it as is. 576*38905Sborman * Short circut the state machine to achive this. 577*38905Sborman * The bad part of this is that if the client sends 578*38905Sborman * a WONT TM on his own to turn off linemode, then he 579*38905Sborman * won't get a response. 580*38905Sborman */ 581*38905Sborman hiswants[TELOPT_TM] = OPT_NO; 582*38905Sborman resp[TELOPT_TM]--; 583*38905Sborman return; 584*38905Sborman 585*38905Sborman case TELOPT_LFLOW: 586*38905Sborman /* 587*38905Sborman * If we are not going to support flow control option, 588*38905Sborman * then let peer know that we can't change the 589*38905Sborman * flow control characters. 590*38905Sborman */ 591*38905Sborman slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 592*38905Sborman slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 593*38905Sborman slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 594*38905Sborman slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 595*38905Sborman break; 596*38905Sborman 597*38905Sborman default: 598*38905Sborman break; 599*38905Sborman } 600*38905Sborman } 601*38905Sborman 602*38905Sborman 603*38905Sborman if (request) { 604*38905Sborman if (!((resp[option] == 0 && hisopts[option] == OPT_NO) || 605*38905Sborman hiswants[option] == OPT_NO)) { 606*38905Sborman hiswants[option] = OPT_NO; 607*38905Sborman fmt = dont; 608*38905Sborman resp[option]++; 609*38905Sborman } 610*38905Sborman } else { 611*38905Sborman if (resp[option]) { 612*38905Sborman resp[option]--; 613*38905Sborman if (resp[option] && hisopts[option] == OPT_NO) 614*38905Sborman resp[option]--; 615*38905Sborman } 616*38905Sborman if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) { 617*38905Sborman /* it is always ok to change to negative state */ 618*38905Sborman hiswants[option] = OPT_NO; 619*38905Sborman fmt = dont; 620*38905Sborman } 621*38905Sborman hisopts[option] = OPT_NO; 622*38905Sborman } 623*38905Sborman 624*38905Sborman if (fmt) { 625*38905Sborman (void) sprintf(nfrontp, fmt, option); 626*38905Sborman nfrontp += sizeof (doopt) - 2; 627*38905Sborman } 628*38905Sborman 629*38905Sborman } /* end of wontoption */ 630*38905Sborman 631*38905Sborman dooption(option, request) 632*38905Sborman int option, request; 633*38905Sborman { 634*38905Sborman int changeok = 0; 635*38905Sborman char *fmt = (char *)0; 636*38905Sborman 637*38905Sborman /* 638*38905Sborman * Process client input. 639*38905Sborman */ 640*38905Sborman if (!request) { 641*38905Sborman switch (option) { 642*38905Sborman case TELOPT_ECHO: 643*38905Sborman #ifdef LINEMODE 644*38905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 645*38905Sborman #endif 646*38905Sborman init_termbuf(); 647*38905Sborman tty_setecho(1); 648*38905Sborman set_termbuf(); 649*38905Sborman #ifdef LINEMODE 650*38905Sborman } 651*38905Sborman #endif 652*38905Sborman changeok++; 653*38905Sborman break; 654*38905Sborman 655*38905Sborman case TELOPT_BINARY: 656*38905Sborman init_termbuf(); 657*38905Sborman tty_binaryout(1); 658*38905Sborman set_termbuf(); 659*38905Sborman changeok++; 660*38905Sborman break; 661*38905Sborman 662*38905Sborman case TELOPT_SGA: 663*38905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 664*38905Sborman /* 665*38905Sborman * If kludge linemode is in use, then we must process 666*38905Sborman * an incoming do SGA for linemode purposes. 667*38905Sborman */ 668*38905Sborman if (lmodetype == KLUDGE_LINEMODE) { 669*38905Sborman /* 670*38905Sborman * Receipt of "do SGA" in kludge linemode 671*38905Sborman * is the peer asking us to turn off linemode. 672*38905Sborman * Make note of the request. 673*38905Sborman */ 674*38905Sborman clientstat(TELOPT_LINEMODE, WONT, 0); 675*38905Sborman /* 676*38905Sborman * If linemode did not get turned off then 677*38905Sborman * don't tell peer that we did. Breaking 678*38905Sborman * here forces a wont SGA to be returned. 679*38905Sborman */ 680*38905Sborman if (linemode) 681*38905Sborman break; 682*38905Sborman } 683*38905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 684*38905Sborman changeok++; 685*38905Sborman break; 686*38905Sborman 687*38905Sborman case TELOPT_STATUS: 688*38905Sborman changeok++; 689*38905Sborman break; 690*38905Sborman 691*38905Sborman case TELOPT_TM: 692*38905Sborman case TELOPT_LINEMODE: 693*38905Sborman case TELOPT_TTYPE: 694*38905Sborman case TELOPT_NAWS: 695*38905Sborman case TELOPT_TSPEED: 696*38905Sborman case TELOPT_LFLOW: 697*38905Sborman default: 698*38905Sborman break; 699*38905Sborman } 700*38905Sborman } 701*38905Sborman 702*38905Sborman if (request) { 703*38905Sborman if (!((resp[option] == 0 && myopts[option] == OPT_YES) || 704*38905Sborman mywants[option] == OPT_YES)) { 705*38905Sborman mywants[option] = OPT_YES; 706*38905Sborman fmt = will; 707*38905Sborman resp[option]++; 708*38905Sborman } 709*38905Sborman } else { 710*38905Sborman if (resp[option]) { 711*38905Sborman resp[option]--; 712*38905Sborman if (resp[option] && myopts[option] == OPT_YES) 713*38905Sborman resp[option]--; 714*38905Sborman } 715*38905Sborman if ((resp[option] == 0) && (mywants[option] != OPT_YES)) { 716*38905Sborman if (changeok) 717*38905Sborman mywants[option] = OPT_YES; 718*38905Sborman else 719*38905Sborman resp[option]++; 720*38905Sborman fmt = (mywants[option] ? will : wont); 721*38905Sborman } 722*38905Sborman myopts[option] = OPT_YES; 723*38905Sborman } 724*38905Sborman 725*38905Sborman if (fmt) { 726*38905Sborman (void) sprintf(nfrontp, fmt, option); 727*38905Sborman nfrontp += sizeof (doopt) - 2; 728*38905Sborman } 729*38905Sborman 730*38905Sborman } /* end of dooption */ 731*38905Sborman 732*38905Sborman 733*38905Sborman dontoption(option, request) 734*38905Sborman int option, request; 735*38905Sborman { 736*38905Sborman char *fmt = (char *)0; 737*38905Sborman 738*38905Sborman /* 739*38905Sborman * Process client input. 740*38905Sborman */ 741*38905Sborman if (!request) { 742*38905Sborman switch (option) { 743*38905Sborman case TELOPT_BINARY: 744*38905Sborman init_termbuf(); 745*38905Sborman tty_binaryout(0); 746*38905Sborman set_termbuf(); 747*38905Sborman break; 748*38905Sborman 749*38905Sborman case TELOPT_ECHO: /* we should stop echoing */ 750*38905Sborman #ifdef LINEMODE 751*38905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 752*38905Sborman #endif 753*38905Sborman init_termbuf(); 754*38905Sborman tty_setecho(0); 755*38905Sborman set_termbuf(); 756*38905Sborman #ifdef LINEMODE 757*38905Sborman } 758*38905Sborman #endif 759*38905Sborman break; 760*38905Sborman 761*38905Sborman case TELOPT_SGA: 762*38905Sborman #if defined(LINEMODE) && defined(KLUDGELINEMODE) 763*38905Sborman /* 764*38905Sborman * If kludge linemode is in use, then we must process an 765*38905Sborman * incoming do SGA for linemode purposes. 766*38905Sborman */ 767*38905Sborman if (lmodetype == KLUDGE_LINEMODE) { 768*38905Sborman /* 769*38905Sborman * The client is asking us to turn linemode 770*38905Sborman * on. 771*38905Sborman */ 772*38905Sborman clientstat(TELOPT_LINEMODE, WILL, 0); 773*38905Sborman /* 774*38905Sborman * If we did not turn line mode on, then what do 775*38905Sborman * we say? Will SGA? This violates design of 776*38905Sborman * telnet. Gross. Very Gross. 777*38905Sborman */ 778*38905Sborman } 779*38905Sborman #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 780*38905Sborman 781*38905Sborman default: 782*38905Sborman break; 783*38905Sborman } 784*38905Sborman } 785*38905Sborman 786*38905Sborman if (request) { 787*38905Sborman if (!((resp[option] == 0 && myopts[option] == OPT_NO) || 788*38905Sborman mywants[option] == OPT_NO)) { 789*38905Sborman mywants[option] = OPT_NO; 790*38905Sborman fmt = wont; 791*38905Sborman resp[option]++; 792*38905Sborman } 793*38905Sborman } else { 794*38905Sborman if (resp[option]) { 795*38905Sborman resp[option]--; 796*38905Sborman if (resp[option] && myopts[option] == OPT_NO) 797*38905Sborman resp[option]--; 798*38905Sborman } 799*38905Sborman if ((resp[option] == 0) && (mywants[option] != OPT_NO)) { 800*38905Sborman mywants[option] = OPT_NO; 801*38905Sborman fmt = wont; 802*38905Sborman } 803*38905Sborman myopts[option] = OPT_NO; 804*38905Sborman } 805*38905Sborman 806*38905Sborman if (fmt) { 807*38905Sborman (void) sprintf(nfrontp, fmt, option); 808*38905Sborman nfrontp += sizeof (wont) - 2; 809*38905Sborman } 810*38905Sborman 811*38905Sborman } /* end of dontoption */ 812*38905Sborman 813*38905Sborman /* 814*38905Sborman * suboption() 815*38905Sborman * 816*38905Sborman * Look at the sub-option buffer, and try to be helpful to the other 817*38905Sborman * side. 818*38905Sborman * 819*38905Sborman * Currently we recognize: 820*38905Sborman * 821*38905Sborman * Terminal type is 822*38905Sborman * Linemode 823*38905Sborman * Window size 824*38905Sborman * Terminal speed 825*38905Sborman */ 826*38905Sborman suboption() 827*38905Sborman { 828*38905Sborman register int subchar; 829*38905Sborman 830*38905Sborman subchar = SB_GET(); 831*38905Sborman switch (subchar) { 832*38905Sborman case TELOPT_TSPEED: { 833*38905Sborman register int xspeed, rspeed; 834*38905Sborman 835*38905Sborman if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ 836*38905Sborman break; 837*38905Sborman 838*38905Sborman settimer(tspeedsubopt); 839*38905Sborman 840*38905Sborman if (SB_EOF() || SB_GET() != TELQUAL_IS) 841*38905Sborman return; 842*38905Sborman 843*38905Sborman xspeed = atoi(subpointer); 844*38905Sborman 845*38905Sborman while (SB_GET() != ',' && !SB_EOF()); 846*38905Sborman if (SB_EOF()) 847*38905Sborman return; 848*38905Sborman 849*38905Sborman rspeed = atoi(subpointer); 850*38905Sborman clientstat(TELOPT_TSPEED, xspeed, rspeed); 851*38905Sborman 852*38905Sborman break; 853*38905Sborman 854*38905Sborman } /* end of case TELOPT_TSPEED */ 855*38905Sborman 856*38905Sborman case TELOPT_TTYPE: { /* Yaaaay! */ 857*38905Sborman static char terminalname[5+41] = "TERM="; 858*38905Sborman 859*38905Sborman if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ 860*38905Sborman break; 861*38905Sborman settimer(ttypesubopt); 862*38905Sborman 863*38905Sborman if (SB_GET() != TELQUAL_IS) { 864*38905Sborman return; /* ??? XXX but, this is the most robust */ 865*38905Sborman } 866*38905Sborman 867*38905Sborman terminaltype = terminalname+sizeof("TERM=")-1; 868*38905Sborman 869*38905Sborman while ((terminaltype < (terminalname + sizeof terminalname-1)) && 870*38905Sborman !SB_EOF()) { 871*38905Sborman register int c; 872*38905Sborman 873*38905Sborman c = SB_GET(); 874*38905Sborman if (isupper(c)) { 875*38905Sborman c = tolower(c); 876*38905Sborman } 877*38905Sborman *terminaltype++ = c; /* accumulate name */ 878*38905Sborman } 879*38905Sborman *terminaltype = 0; 880*38905Sborman terminaltype = terminalname; 881*38905Sborman break; 882*38905Sborman } /* end of case TELOPT_TTYPE */ 883*38905Sborman 884*38905Sborman case TELOPT_NAWS: { 885*38905Sborman register int xwinsize, ywinsize; 886*38905Sborman 887*38905Sborman if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ 888*38905Sborman break; 889*38905Sborman 890*38905Sborman if (SB_EOF()) 891*38905Sborman return; 892*38905Sborman xwinsize = SB_GET() << 8; 893*38905Sborman if (SB_EOF()) 894*38905Sborman return; 895*38905Sborman xwinsize |= SB_GET(); 896*38905Sborman if (SB_EOF()) 897*38905Sborman return; 898*38905Sborman ywinsize = SB_GET() << 8; 899*38905Sborman if (SB_EOF()) 900*38905Sborman return; 901*38905Sborman ywinsize |= SB_GET(); 902*38905Sborman clientstat(TELOPT_NAWS, xwinsize, ywinsize); 903*38905Sborman 904*38905Sborman break; 905*38905Sborman 906*38905Sborman } /* end of case TELOPT_NAWS */ 907*38905Sborman 908*38905Sborman #ifdef LINEMODE 909*38905Sborman case TELOPT_LINEMODE: { 910*38905Sborman register int request; 911*38905Sborman 912*38905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ 913*38905Sborman break; 914*38905Sborman /* 915*38905Sborman * Process linemode suboptions. 916*38905Sborman */ 917*38905Sborman if (SB_EOF()) break; /* garbage was sent */ 918*38905Sborman request = SB_GET(); /* get will/wont */ 919*38905Sborman if (SB_EOF()) break; /* another garbage check */ 920*38905Sborman 921*38905Sborman if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ 922*38905Sborman /* 923*38905Sborman * Process suboption buffer of slc's 924*38905Sborman */ 925*38905Sborman start_slc(1); 926*38905Sborman do_opt_slc(subpointer, subend - subpointer); 927*38905Sborman end_slc(0); 928*38905Sborman 929*38905Sborman } else if (request == LM_MODE) { 930*38905Sborman useeditmode = SB_GET(); /* get mode flag */ 931*38905Sborman clientstat(LM_MODE, 0, 0); 932*38905Sborman } 933*38905Sborman 934*38905Sborman switch (SB_GET()) { /* what suboption? */ 935*38905Sborman case LM_FORWARDMASK: 936*38905Sborman /* 937*38905Sborman * According to spec, only server can send request for 938*38905Sborman * forwardmask, and client can only return a positive response. 939*38905Sborman * So don't worry about it. 940*38905Sborman */ 941*38905Sborman 942*38905Sborman default: 943*38905Sborman break; 944*38905Sborman } 945*38905Sborman 946*38905Sborman } /* end of case TELOPT_LINEMODE */ 947*38905Sborman #endif 948*38905Sborman case TELOPT_STATUS: { 949*38905Sborman int mode; 950*38905Sborman 951*38905Sborman mode = SB_GET(); 952*38905Sborman switch (mode) { 953*38905Sborman case TELQUAL_SEND: 954*38905Sborman if (myopts[TELOPT_STATUS] == OPT_YES) 955*38905Sborman send_status(); 956*38905Sborman break; 957*38905Sborman 958*38905Sborman case TELQUAL_IS: 959*38905Sborman break; 960*38905Sborman 961*38905Sborman default: 962*38905Sborman break; 963*38905Sborman } 964*38905Sborman } 965*38905Sborman 966*38905Sborman default: 967*38905Sborman break; 968*38905Sborman } /* end of switch */ 969*38905Sborman 970*38905Sborman } /* end of suboption */ 971*38905Sborman 972*38905Sborman #define ADD(c) *ncp++ = c; 973*38905Sborman #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } 974*38905Sborman send_status() 975*38905Sborman { 976*38905Sborman char statusbuf[256]; 977*38905Sborman register char *ncp; 978*38905Sborman register int i; 979*38905Sborman 980*38905Sborman ncp = statusbuf; 981*38905Sborman 982*38905Sborman netflush(); /* get rid of anything waiting to go out */ 983*38905Sborman 984*38905Sborman ADD(IAC); 985*38905Sborman ADD(SB); 986*38905Sborman ADD(TELOPT_STATUS); 987*38905Sborman ADD(TELQUAL_IS); 988*38905Sborman 989*38905Sborman for (i = 0; i < NTELOPTS; i++) { 990*38905Sborman if (myopts[i] == OPT_YES) { 991*38905Sborman ADD(WILL); 992*38905Sborman ADD_DATA(i); 993*38905Sborman if (i == IAC) 994*38905Sborman ADD(IAC); 995*38905Sborman } 996*38905Sborman if (hisopts[i] == OPT_YES) { 997*38905Sborman ADD(DO); 998*38905Sborman ADD_DATA(i); 999*38905Sborman if (i == IAC) 1000*38905Sborman ADD(IAC); 1001*38905Sborman } 1002*38905Sborman } 1003*38905Sborman 1004*38905Sborman #ifdef LINEMODE 1005*38905Sborman if (hisopts[TELOPT_LINEMODE] == OPT_YES) { 1006*38905Sborman char *cp, *cpe; 1007*38905Sborman int len; 1008*38905Sborman 1009*38905Sborman ADD(SB); 1010*38905Sborman ADD(TELOPT_LINEMODE); 1011*38905Sborman ADD(LM_MODE); 1012*38905Sborman ADD_DATA(editmode); 1013*38905Sborman if (editmode == IAC) 1014*38905Sborman ADD(IAC); 1015*38905Sborman ADD(SE); 1016*38905Sborman 1017*38905Sborman ADD(SB); 1018*38905Sborman ADD(TELOPT_LINEMODE); 1019*38905Sborman ADD(LM_SLC); 1020*38905Sborman start_slc(0); 1021*38905Sborman send_slc(); 1022*38905Sborman len = end_slc(&cp); 1023*38905Sborman for (cpe = cp + len; cp < cpe; cp++) 1024*38905Sborman ADD_DATA(*cp); 1025*38905Sborman ADD(SE); 1026*38905Sborman } 1027*38905Sborman #endif /* LINEMODE */ 1028*38905Sborman 1029*38905Sborman ADD(IAC); 1030*38905Sborman ADD(SE); 1031*38905Sborman 1032*38905Sborman writenet(statusbuf, ncp - statusbuf); 1033*38905Sborman netflush(); /* Send it on its way */ 1034*38905Sborman } 1035