|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1989 Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * Redistribution and use in source and binary forms are permitted provided ! 6: * that: (1) source distributions retain this entire copyright notice and ! 7: * comment, and (2) distributions including binaries display the following ! 8: * acknowledgement: ``This product includes software developed by the ! 9: * University of California, Berkeley and its contributors'' in the ! 10: * documentation or other materials provided with the distribution and in ! 11: * all advertising materials mentioning features or use of this software. ! 12: * Neither the name of the University nor the names of its contributors may ! 13: * be used to endorse or promote products derived from this software without ! 14: * specific prior written permission. ! 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 18: */ ! 19: ! 20: #ifndef lint ! 21: static char sccsid[] = "@(#)state.c 5.7 (Berkeley) 6/28/90"; ! 22: #endif /* not lint */ ! 23: ! 24: #include "telnetd.h" ! 25: ! 26: char doopt[] = { IAC, DO, '%', 'c', 0 }; ! 27: char dont[] = { IAC, DONT, '%', 'c', 0 }; ! 28: char will[] = { IAC, WILL, '%', 'c', 0 }; ! 29: char wont[] = { IAC, WONT, '%', 'c', 0 }; ! 30: int not42 = 1; ! 31: ! 32: /* ! 33: * Buffer for sub-options, and macros ! 34: * for suboptions buffer manipulations ! 35: */ ! 36: char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; ! 37: ! 38: #define SB_CLEAR() subpointer = subbuffer; ! 39: #define SB_TERM() { subend = subpointer; SB_CLEAR(); } ! 40: #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ ! 41: *subpointer++ = (c); \ ! 42: } ! 43: #define SB_GET() ((*subpointer++)&0xff) ! 44: #define SB_EOF() (subpointer >= subend) ! 45: #define SB_LEN() (subend - subpointer) ! 46: ! 47: ! 48: ! 49: /* ! 50: * State for recv fsm ! 51: */ ! 52: #define TS_DATA 0 /* base state */ ! 53: #define TS_IAC 1 /* look for double IAC's */ ! 54: #define TS_CR 2 /* CR-LF ->'s CR */ ! 55: #define TS_SB 3 /* throw away begin's... */ ! 56: #define TS_SE 4 /* ...end's (suboption negotiation) */ ! 57: #define TS_WILL 5 /* will option negotiation */ ! 58: #define TS_WONT 6 /* wont " */ ! 59: #define TS_DO 7 /* do " */ ! 60: #define TS_DONT 8 /* dont " */ ! 61: ! 62: telrcv() ! 63: { ! 64: register int c; ! 65: static int state = TS_DATA; ! 66: #if defined(CRAY2) && defined(UNICOS5) ! 67: char *opfrontp = pfrontp; ! 68: #endif ! 69: ! 70: while (ncc > 0) { ! 71: if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) ! 72: break; ! 73: c = *netip++ & 0377, ncc--; ! 74: switch (state) { ! 75: ! 76: case TS_CR: ! 77: state = TS_DATA; ! 78: /* Strip off \n or \0 after a \r */ ! 79: if ((c == 0) || (c == '\n')) { ! 80: break; ! 81: } ! 82: /* FALL THROUGH */ ! 83: ! 84: case TS_DATA: ! 85: if (c == IAC) { ! 86: state = TS_IAC; ! 87: break; ! 88: } ! 89: /* ! 90: * We now map \r\n ==> \r for pragmatic reasons. ! 91: * Many client implementations send \r\n when ! 92: * the user hits the CarriageReturn key. ! 93: * ! 94: * We USED to map \r\n ==> \n, since \r\n says ! 95: * that we want to be in column 1 of the next ! 96: * printable line, and \n is the standard ! 97: * unix way of saying that (\r is only good ! 98: * if CRMOD is set, which it normally is). ! 99: */ ! 100: if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { ! 101: /* ! 102: * If we are operating in linemode, ! 103: * convert to local end-of-line. ! 104: */ ! 105: if ((linemode) && (ncc > 0)&&('\n' == *netip)) { ! 106: netip++; ncc--; ! 107: c = '\n'; ! 108: } else { ! 109: state = TS_CR; ! 110: } ! 111: } ! 112: *pfrontp++ = c; ! 113: break; ! 114: ! 115: case TS_IAC: ! 116: gotiac: switch (c) { ! 117: ! 118: /* ! 119: * Send the process on the pty side an ! 120: * interrupt. Do this with a NULL or ! 121: * interrupt char; depending on the tty mode. ! 122: */ ! 123: case IP: ! 124: #ifdef DIAGNOSTICS ! 125: if (diagnostic & TD_OPTIONS) ! 126: printoption("td: recv IAC", c); ! 127: #endif /* DIAGNOSTICS */ ! 128: interrupt(); ! 129: break; ! 130: ! 131: case BREAK: ! 132: #ifdef DIAGNOSTICS ! 133: if (diagnostic & TD_OPTIONS) ! 134: printoption("td: recv IAC", c); ! 135: #endif /* DIAGNOSTICS */ ! 136: sendbrk(); ! 137: break; ! 138: ! 139: /* ! 140: * Are You There? ! 141: */ ! 142: case AYT: ! 143: #ifdef DIAGNOSTICS ! 144: if (diagnostic & TD_OPTIONS) ! 145: printoption("td: recv IAC", c); ! 146: #endif /* DIAGNOSTICS */ ! 147: (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); ! 148: nfrontp += 9; ! 149: break; ! 150: ! 151: /* ! 152: * Abort Output ! 153: */ ! 154: case AO: ! 155: { ! 156: #ifdef DIAGNOSTICS ! 157: if (diagnostic & TD_OPTIONS) ! 158: printoption("td: recv IAC", c); ! 159: #endif /* DIAGNOSTICS */ ! 160: ptyflush(); /* half-hearted */ ! 161: init_termbuf(); ! 162: ! 163: if (slctab[SLC_AO].sptr && ! 164: *slctab[SLC_AO].sptr != (cc_t)-1) { ! 165: *pfrontp++ = ! 166: (unsigned char)*slctab[SLC_AO].sptr; ! 167: } ! 168: ! 169: netclear(); /* clear buffer back */ ! 170: *nfrontp++ = IAC; ! 171: *nfrontp++ = DM; ! 172: neturg = nfrontp-1; /* off by one XXX */ ! 173: #ifdef DIAGNOSTICS ! 174: if (diagnostic & TD_OPTIONS) ! 175: printoption("td: send IAC", DM); ! 176: #endif /* DIAGNOSTICS */ ! 177: break; ! 178: } ! 179: ! 180: /* ! 181: * Erase Character and ! 182: * Erase Line ! 183: */ ! 184: case EC: ! 185: case EL: ! 186: { ! 187: cc_t ch; ! 188: ! 189: #ifdef DIAGNOSTICS ! 190: if (diagnostic & TD_OPTIONS) ! 191: printoption("td: recv IAC", c); ! 192: #endif /* DIAGNOSTICS */ ! 193: ptyflush(); /* half-hearted */ ! 194: init_termbuf(); ! 195: if (c == EC) ! 196: ch = *slctab[SLC_EC].sptr; ! 197: else ! 198: ch = *slctab[SLC_EL].sptr; ! 199: if (ch != (cc_t)-1) ! 200: *pfrontp++ = (unsigned char)ch; ! 201: break; ! 202: } ! 203: ! 204: /* ! 205: * Check for urgent data... ! 206: */ ! 207: case DM: ! 208: #ifdef DIAGNOSTICS ! 209: if (diagnostic & TD_OPTIONS) ! 210: printoption("td: recv IAC", c); ! 211: #endif /* DIAGNOSTICS */ ! 212: SYNCHing = stilloob(net); ! 213: settimer(gotDM); ! 214: break; ! 215: ! 216: ! 217: /* ! 218: * Begin option subnegotiation... ! 219: */ ! 220: case SB: ! 221: state = TS_SB; ! 222: SB_CLEAR(); ! 223: continue; ! 224: ! 225: case WILL: ! 226: state = TS_WILL; ! 227: continue; ! 228: ! 229: case WONT: ! 230: state = TS_WONT; ! 231: continue; ! 232: ! 233: case DO: ! 234: state = TS_DO; ! 235: continue; ! 236: ! 237: case DONT: ! 238: state = TS_DONT; ! 239: continue; ! 240: case EOR: ! 241: if (his_state_is_will(TELOPT_EOR)) ! 242: doeof(); ! 243: break; ! 244: ! 245: /* ! 246: * Handle RFC 10xx Telnet linemode option additions ! 247: * to command stream (EOF, SUSP, ABORT). ! 248: */ ! 249: case xEOF: ! 250: doeof(); ! 251: break; ! 252: ! 253: case SUSP: ! 254: sendsusp(); ! 255: break; ! 256: ! 257: case ABORT: ! 258: sendbrk(); ! 259: break; ! 260: ! 261: case IAC: ! 262: *pfrontp++ = c; ! 263: break; ! 264: } ! 265: state = TS_DATA; ! 266: break; ! 267: ! 268: case TS_SB: ! 269: if (c == IAC) { ! 270: state = TS_SE; ! 271: } else { ! 272: SB_ACCUM(c); ! 273: } ! 274: break; ! 275: ! 276: case TS_SE: ! 277: if (c != SE) { ! 278: if (c != IAC) { ! 279: /* ! 280: * bad form of suboption negotiation. ! 281: * handle it in such a way as to avoid ! 282: * damage to local state. Parse ! 283: * suboption buffer found so far, ! 284: * then treat remaining stream as ! 285: * another command sequence. ! 286: */ ! 287: #ifdef DIAGNOSTICS ! 288: SB_ACCUM(IAC); ! 289: SB_ACCUM(c); ! 290: subpointer -= 2; ! 291: #endif ! 292: SB_TERM(); ! 293: suboption(); ! 294: state = TS_IAC; ! 295: goto gotiac; ! 296: } ! 297: SB_ACCUM(c); ! 298: state = TS_SB; ! 299: } else { ! 300: #ifdef DIAGNOSTICS ! 301: SB_ACCUM(IAC); ! 302: SB_ACCUM(SE); ! 303: subpointer -= 2; ! 304: #endif ! 305: SB_TERM(); ! 306: suboption(); /* handle sub-option */ ! 307: state = TS_DATA; ! 308: } ! 309: break; ! 310: ! 311: case TS_WILL: ! 312: willoption(c); ! 313: state = TS_DATA; ! 314: continue; ! 315: ! 316: case TS_WONT: ! 317: wontoption(c); ! 318: state = TS_DATA; ! 319: continue; ! 320: ! 321: case TS_DO: ! 322: dooption(c); ! 323: state = TS_DATA; ! 324: continue; ! 325: ! 326: case TS_DONT: ! 327: dontoption(c); ! 328: state = TS_DATA; ! 329: continue; ! 330: ! 331: default: ! 332: syslog(LOG_ERR, "telnetd: panic state=%d\n", state); ! 333: printf("telnetd: panic state=%d\n", state); ! 334: exit(1); ! 335: } ! 336: } ! 337: #if defined(CRAY2) && defined(UNICOS5) ! 338: if (!linemode) { ! 339: char xptyobuf[BUFSIZ+NETSLOP]; ! 340: char xbuf2[BUFSIZ]; ! 341: register char *cp; ! 342: int n = pfrontp - opfrontp, oc; ! 343: bcopy(opfrontp, xptyobuf, n); ! 344: pfrontp = opfrontp; ! 345: pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, ! 346: xbuf2, &oc, BUFSIZ); ! 347: for (cp = xbuf2; oc > 0; --oc) ! 348: if ((*nfrontp++ = *cp++) == IAC) ! 349: *nfrontp++ = IAC; ! 350: } ! 351: #endif /* defined(CRAY2) && defined(UNICOS5) */ ! 352: } /* end of telrcv */ ! 353: ! 354: /* ! 355: * The will/wont/do/dont state machines are based on Dave Borman's ! 356: * Telnet option processing state machine. ! 357: * ! 358: * These correspond to the following states: ! 359: * my_state = the last negotiated state ! 360: * want_state = what I want the state to go to ! 361: * want_resp = how many requests I have sent ! 362: * All state defaults are negative, and resp defaults to 0. ! 363: * ! 364: * When initiating a request to change state to new_state: ! 365: * ! 366: * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { ! 367: * do nothing; ! 368: * } else { ! 369: * want_state = new_state; ! 370: * send new_state; ! 371: * want_resp++; ! 372: * } ! 373: * ! 374: * When receiving new_state: ! 375: * ! 376: * if (want_resp) { ! 377: * want_resp--; ! 378: * if (want_resp && (new_state == my_state)) ! 379: * want_resp--; ! 380: * } ! 381: * if ((want_resp == 0) && (new_state != want_state)) { ! 382: * if (ok_to_switch_to new_state) ! 383: * want_state = new_state; ! 384: * else ! 385: * want_resp++; ! 386: * send want_state; ! 387: * } ! 388: * my_state = new_state; ! 389: * ! 390: * Note that new_state is implied in these functions by the function itself. ! 391: * will and do imply positive new_state, wont and dont imply negative. ! 392: * ! 393: * Finally, there is one catch. If we send a negative response to a ! 394: * positive request, my_state will be the positive while want_state will ! 395: * remain negative. my_state will revert to negative when the negative ! 396: * acknowlegment arrives from the peer. Thus, my_state generally tells ! 397: * us not only the last negotiated state, but also tells us what the peer ! 398: * wants to be doing as well. It is important to understand this difference ! 399: * as we may wish to be processing data streams based on our desired state ! 400: * (want_state) or based on what the peer thinks the state is (my_state). ! 401: * ! 402: * This all works fine because if the peer sends a positive request, the data ! 403: * that we receive prior to negative acknowlegment will probably be affected ! 404: * by the positive state, and we can process it as such (if we can; if we ! 405: * can't then it really doesn't matter). If it is that important, then the ! 406: * peer probably should be buffering until this option state negotiation ! 407: * is complete. ! 408: * ! 409: */ ! 410: send_do(option, init) ! 411: int option, init; ! 412: { ! 413: if (init) { ! 414: if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || ! 415: his_want_state_is_will(option)) ! 416: return; ! 417: /* ! 418: * Special case for TELOPT_TM: We send a DO, but pretend ! 419: * that we sent a DONT, so that we can send more DOs if ! 420: * we want to. ! 421: */ ! 422: if (option == TELOPT_TM) ! 423: set_his_want_state_wont(option); ! 424: else ! 425: set_his_want_state_will(option); ! 426: do_dont_resp[option]++; ! 427: } ! 428: (void) sprintf(nfrontp, doopt, option); ! 429: nfrontp += sizeof (dont) - 2; ! 430: #ifdef DIAGNOSTICS ! 431: /* ! 432: * Report sending option to other side. ! 433: */ ! 434: if (diagnostic & TD_OPTIONS) { ! 435: printoption("td: send do", option); ! 436: } ! 437: #endif /* DIAGNOSTICS */ ! 438: } ! 439: ! 440: willoption(option) ! 441: int option; ! 442: { ! 443: int changeok = 0; ! 444: ! 445: /* ! 446: * process input from peer. ! 447: */ ! 448: ! 449: #ifdef DIAGNOSTICS ! 450: /* ! 451: * Report receiving option from other side. ! 452: */ ! 453: if (diagnostic & TD_OPTIONS) { ! 454: printoption("td: recv will", option); ! 455: } ! 456: #endif /* DIAGNOSTICS */ ! 457: ! 458: if (do_dont_resp[option]) { ! 459: do_dont_resp[option]--; ! 460: if (do_dont_resp[option] && his_state_is_will(option)) ! 461: do_dont_resp[option]--; ! 462: } ! 463: if (do_dont_resp[option] == 0) { ! 464: if (his_want_state_is_wont(option)) { ! 465: switch (option) { ! 466: ! 467: case TELOPT_BINARY: ! 468: init_termbuf(); ! 469: tty_binaryin(1); ! 470: set_termbuf(); ! 471: changeok++; ! 472: break; ! 473: ! 474: case TELOPT_ECHO: ! 475: /* ! 476: * See comments below for more info. ! 477: */ ! 478: not42 = 0; /* looks like a 4.2 system */ ! 479: break; ! 480: ! 481: case TELOPT_TM: ! 482: #if defined(LINEMODE) && defined(KLUDGELINEMODE) ! 483: /* ! 484: * This telnetd implementation does not really ! 485: * support timing marks, it just uses them to ! 486: * support the kludge linemode stuff. If we ! 487: * receive a will or wont TM in response to our ! 488: * do TM request that may have been sent to ! 489: * determine kludge linemode support, process ! 490: * it, otherwise TM should get a negative ! 491: * response back. ! 492: */ ! 493: /* ! 494: * Handle the linemode kludge stuff. ! 495: * If we are not currently supporting any ! 496: * linemode at all, then we assume that this ! 497: * is the client telling us to use kludge ! 498: * linemode in response to our query. Set the ! 499: * linemode type that is to be supported, note ! 500: * that the client wishes to use linemode, and ! 501: * eat the will TM as though it never arrived. ! 502: */ ! 503: if (lmodetype < KLUDGE_LINEMODE) { ! 504: lmodetype = KLUDGE_LINEMODE; ! 505: clientstat(TELOPT_LINEMODE, WILL, 0); ! 506: send_wont(TELOPT_SGA, 1); ! 507: } ! 508: #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ ! 509: /* ! 510: * We never respond to a WILL TM, and ! 511: * we leave the state WONT. ! 512: */ ! 513: return; ! 514: ! 515: case TELOPT_LFLOW: ! 516: /* ! 517: * If we are going to support flow control ! 518: * option, then don't worry peer that we can't ! 519: * change the flow control characters. ! 520: */ ! 521: slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; ! 522: slctab[SLC_XON].defset.flag |= SLC_DEFAULT; ! 523: slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; ! 524: slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; ! 525: case TELOPT_TTYPE: ! 526: case TELOPT_SGA: ! 527: case TELOPT_NAWS: ! 528: case TELOPT_TSPEED: ! 529: case TELOPT_XDISPLOC: ! 530: case TELOPT_ENVIRON: ! 531: changeok++; ! 532: break; ! 533: ! 534: #ifdef LINEMODE ! 535: case TELOPT_LINEMODE: ! 536: /* ! 537: * Local processing of 'will linemode' should ! 538: * occur after placing 'do linemode' in the data ! 539: * stream, because we may wish to send other ! 540: * linemode related messages. So, we duplicate ! 541: * the other three lines of code here, and then ! 542: * return. ! 543: */ ! 544: set_his_want_state_will(option); ! 545: send_do(option, 0); ! 546: set_his_state_will(option); ! 547: # ifdef KLUDGELINEMODE ! 548: /* ! 549: * Note client's desire to use linemode. ! 550: */ ! 551: lmodetype = REAL_LINEMODE; ! 552: # endif /* KLUDGELINEMODE */ ! 553: clientstat(TELOPT_LINEMODE, WILL, 0); ! 554: return; ! 555: #endif /* LINEMODE */ ! 556: ! 557: default: ! 558: break; ! 559: } ! 560: if (changeok) { ! 561: set_his_want_state_will(option); ! 562: send_do(option, 0); ! 563: } else { ! 564: do_dont_resp[option]++; ! 565: send_dont(option, 0); ! 566: } ! 567: } else { ! 568: /* ! 569: * Option processing that should happen when ! 570: * we receive conformation of a change in ! 571: * state that we had requested. ! 572: */ ! 573: switch (option) { ! 574: case TELOPT_ECHO: ! 575: not42 = 0; /* looks like a 4.2 system */ ! 576: /* ! 577: * Egads, he responded "WILL ECHO". Turn ! 578: * it off right now! ! 579: */ ! 580: send_dont(option, 1); ! 581: /* ! 582: * "WILL ECHO". Kludge upon kludge! ! 583: * A 4.2 client is now echoing user input at ! 584: * the tty. This is probably undesireable and ! 585: * it should be stopped. The client will ! 586: * respond WONT TM to the DO TM that we send to ! 587: * check for kludge linemode. When the WONT TM ! 588: * arrives, linemode will be turned off and a ! 589: * change propogated to the pty. This change ! 590: * will cause us to process the new pty state ! 591: * in localstat(), which will notice that ! 592: * linemode is off and send a WILL ECHO ! 593: * so that we are properly in character mode and ! 594: * all is well. ! 595: */ ! 596: break; ! 597: #ifdef LINEMODE ! 598: case TELOPT_LINEMODE: ! 599: # ifdef KLUDGELINEMODE ! 600: /* ! 601: * Note client's desire to use linemode. ! 602: */ ! 603: lmodetype = REAL_LINEMODE; ! 604: # endif /* KLUDGELINEMODE */ ! 605: clientstat(TELOPT_LINEMODE, WILL, 0); ! 606: #endif /* LINEMODE */ ! 607: } ! 608: } ! 609: } ! 610: set_his_state_will(option); ! 611: } /* end of willoption */ ! 612: ! 613: send_dont(option, init) ! 614: int option, init; ! 615: { ! 616: if (init) { ! 617: if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || ! 618: his_want_state_is_wont(option)) ! 619: return; ! 620: set_his_want_state_wont(option); ! 621: do_dont_resp[option]++; ! 622: } ! 623: (void) sprintf(nfrontp, dont, option); ! 624: nfrontp += sizeof (doopt) - 2; ! 625: #ifdef DIAGNOSTICS ! 626: /* ! 627: * Report sending option to other side. ! 628: */ ! 629: if (diagnostic & TD_OPTIONS) { ! 630: printoption("td: send dont", option); ! 631: } ! 632: #endif /* DIAGNOSTICS */ ! 633: } ! 634: ! 635: wontoption(option) ! 636: int option; ! 637: { ! 638: /* ! 639: * Process client input. ! 640: */ ! 641: ! 642: #ifdef DIAGNOSTICS ! 643: /* ! 644: * Report receiving option from other side. ! 645: */ ! 646: if (diagnostic & TD_OPTIONS) { ! 647: printoption("td: recv wont", option); ! 648: } ! 649: #endif /* DIAGNOSTICS */ ! 650: ! 651: if (do_dont_resp[option]) { ! 652: do_dont_resp[option]--; ! 653: if (do_dont_resp[option] && his_state_is_wont(option)) ! 654: do_dont_resp[option]--; ! 655: } ! 656: if (do_dont_resp[option] == 0) { ! 657: if (his_want_state_is_will(option)) { ! 658: /* it is always ok to change to negative state */ ! 659: switch (option) { ! 660: case TELOPT_ECHO: ! 661: not42 = 1; /* doesn't seem to be a 4.2 system */ ! 662: break; ! 663: ! 664: case TELOPT_BINARY: ! 665: init_termbuf(); ! 666: tty_binaryin(0); ! 667: set_termbuf(); ! 668: break; ! 669: ! 670: #ifdef LINEMODE ! 671: case TELOPT_LINEMODE: ! 672: # ifdef KLUDGELINEMODE ! 673: /* ! 674: * If real linemode is supported, then client is ! 675: * asking to turn linemode off. ! 676: */ ! 677: if (lmodetype != REAL_LINEMODE) ! 678: break; ! 679: lmodetype = KLUDGE_LINEMODE; ! 680: # endif /* KLUDGELINEMODE */ ! 681: clientstat(TELOPT_LINEMODE, WONT, 0); ! 682: break; ! 683: #endif LINEMODE ! 684: ! 685: case TELOPT_TM: ! 686: /* ! 687: * If we get a WONT TM, and had sent a DO TM, ! 688: * don't respond with a DONT TM, just leave it ! 689: * as is. Short circut the state machine to ! 690: * achive this. ! 691: */ ! 692: set_his_want_state_wont(TELOPT_TM); ! 693: return; ! 694: ! 695: case TELOPT_LFLOW: ! 696: /* ! 697: * If we are not going to support flow control ! 698: * option, then let peer know that we can't ! 699: * change the flow control characters. ! 700: */ ! 701: slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; ! 702: slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; ! 703: slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; ! 704: slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; ! 705: break; ! 706: ! 707: /* ! 708: * For options that we might spin waiting for ! 709: * sub-negotiation, if the client turns off the ! 710: * option rather than responding to the request, ! 711: * we have to treat it here as if we got a response ! 712: * to the sub-negotiation, (by updating the timers) ! 713: * so that we'll break out of the loop. ! 714: */ ! 715: case TELOPT_TTYPE: ! 716: settimer(ttypesubopt); ! 717: break; ! 718: ! 719: case TELOPT_TSPEED: ! 720: settimer(tspeedsubopt); ! 721: break; ! 722: ! 723: case TELOPT_XDISPLOC: ! 724: settimer(xdisplocsubopt); ! 725: break; ! 726: ! 727: case TELOPT_ENVIRON: ! 728: settimer(environsubopt); ! 729: break; ! 730: ! 731: default: ! 732: break; ! 733: } ! 734: set_his_want_state_wont(option); ! 735: if (his_state_is_will(option)) ! 736: send_dont(option, 0); ! 737: } else { ! 738: switch (option) { ! 739: case TELOPT_TM: ! 740: #if defined(LINEMODE) && defined(KLUDGELINEMODE) ! 741: if (lmodetype < REAL_LINEMODE) { ! 742: lmodetype = NO_LINEMODE; ! 743: clientstat(TELOPT_LINEMODE, WONT, 0); ! 744: send_will(TELOPT_SGA, 1); ! 745: /*@*/ send_will(TELOPT_ECHO, 1); ! 746: } ! 747: #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ ! 748: default: ! 749: break; ! 750: } ! 751: } ! 752: } ! 753: set_his_state_wont(option); ! 754: ! 755: } /* end of wontoption */ ! 756: ! 757: send_will(option, init) ! 758: int option, init; ! 759: { ! 760: if (init) { ! 761: if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| ! 762: my_want_state_is_will(option)) ! 763: return; ! 764: set_my_want_state_will(option); ! 765: will_wont_resp[option]++; ! 766: } ! 767: (void) sprintf(nfrontp, will, option); ! 768: nfrontp += sizeof (doopt) - 2; ! 769: #ifdef DIAGNOSTICS ! 770: /* ! 771: * Report sending option to other side. ! 772: */ ! 773: if (diagnostic & TD_OPTIONS) { ! 774: printoption("td: send will", option); ! 775: } ! 776: #endif /* DIAGNOSTICS */ ! 777: } ! 778: ! 779: dooption(option) ! 780: int option; ! 781: { ! 782: int changeok = 0; ! 783: ! 784: /* ! 785: * Process client input. ! 786: */ ! 787: ! 788: #ifdef DIAGNOSTICS ! 789: /* ! 790: * Report receiving option from other side. ! 791: */ ! 792: if (diagnostic & TD_OPTIONS) { ! 793: printoption("td: recv do", option); ! 794: } ! 795: #endif /* DIAGNOSTICS */ ! 796: ! 797: if (will_wont_resp[option]) { ! 798: will_wont_resp[option]--; ! 799: if (will_wont_resp[option] && my_state_is_will(option)) ! 800: will_wont_resp[option]--; ! 801: } ! 802: if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { ! 803: switch (option) { ! 804: case TELOPT_ECHO: ! 805: #ifdef LINEMODE ! 806: if (lmodetype == NO_LINEMODE) { ! 807: #endif ! 808: init_termbuf(); ! 809: tty_setecho(1); ! 810: set_termbuf(); ! 811: #ifdef LINEMODE ! 812: } ! 813: #endif ! 814: changeok++; ! 815: break; ! 816: ! 817: case TELOPT_BINARY: ! 818: init_termbuf(); ! 819: tty_binaryout(1); ! 820: set_termbuf(); ! 821: changeok++; ! 822: break; ! 823: ! 824: case TELOPT_SGA: ! 825: #if defined(LINEMODE) && defined(KLUDGELINEMODE) ! 826: /* ! 827: * If kludge linemode is in use, then we must ! 828: * process an incoming do SGA for linemode ! 829: * purposes. ! 830: */ ! 831: if (lmodetype == KLUDGE_LINEMODE) { ! 832: /* ! 833: * Receipt of "do SGA" in kludge ! 834: * linemode is the peer asking us to ! 835: * turn off linemode. Make note of ! 836: * the request. ! 837: */ ! 838: clientstat(TELOPT_LINEMODE, WONT, 0); ! 839: /* ! 840: * If linemode did not get turned off ! 841: * then don't tell peer that we did. ! 842: * Breaking here forces a wont SGA to ! 843: * be returned. ! 844: */ ! 845: if (linemode) ! 846: break; ! 847: } ! 848: #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ ! 849: changeok++; ! 850: break; ! 851: ! 852: case TELOPT_STATUS: ! 853: changeok++; ! 854: break; ! 855: ! 856: case TELOPT_TM: ! 857: /* ! 858: * Special case for TM. We send a WILL, but ! 859: * pretend we sent a WONT. ! 860: */ ! 861: send_will(option, 0); ! 862: set_my_want_state_wont(option); ! 863: set_my_state_wont(option); ! 864: return; ! 865: ! 866: case TELOPT_LINEMODE: ! 867: case TELOPT_TTYPE: ! 868: case TELOPT_NAWS: ! 869: case TELOPT_TSPEED: ! 870: case TELOPT_LFLOW: ! 871: case TELOPT_XDISPLOC: ! 872: case TELOPT_ENVIRON: ! 873: default: ! 874: break; ! 875: } ! 876: if (changeok) { ! 877: set_my_want_state_will(option); ! 878: send_will(option, 0); ! 879: } else { ! 880: will_wont_resp[option]++; ! 881: send_wont(option, 0); ! 882: } ! 883: } ! 884: set_my_state_will(option); ! 885: ! 886: } /* end of dooption */ ! 887: ! 888: send_wont(option, init) ! 889: int option, init; ! 890: { ! 891: if (init) { ! 892: if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || ! 893: my_want_state_is_wont(option)) ! 894: return; ! 895: set_my_want_state_wont(option); ! 896: will_wont_resp[option]++; ! 897: } ! 898: (void) sprintf(nfrontp, wont, option); ! 899: nfrontp += sizeof (wont) - 2; ! 900: #ifdef DIAGNOSTICS ! 901: /* ! 902: * Report sending option to other side. ! 903: */ ! 904: if (diagnostic & TD_OPTIONS) { ! 905: printoption("td: send wont", option); ! 906: } ! 907: #endif /* DIAGNOSTICS */ ! 908: } ! 909: ! 910: dontoption(option) ! 911: int option; ! 912: { ! 913: /* ! 914: * Process client input. ! 915: */ ! 916: #ifdef DIAGNOSTICS ! 917: /* ! 918: * Report receiving option from other side. ! 919: */ ! 920: if (diagnostic & TD_OPTIONS) { ! 921: printoption("td: recv dont", option); ! 922: } ! 923: #endif /* DIAGNOSTICS */ ! 924: ! 925: if (will_wont_resp[option]) { ! 926: will_wont_resp[option]--; ! 927: if (will_wont_resp[option] && my_state_is_wont(option)) ! 928: will_wont_resp[option]--; ! 929: } ! 930: if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { ! 931: switch (option) { ! 932: case TELOPT_BINARY: ! 933: init_termbuf(); ! 934: tty_binaryout(0); ! 935: set_termbuf(); ! 936: break; ! 937: ! 938: case TELOPT_ECHO: /* we should stop echoing */ ! 939: #ifdef LINEMODE ! 940: if (lmodetype == NO_LINEMODE) { ! 941: #endif ! 942: init_termbuf(); ! 943: tty_setecho(0); ! 944: set_termbuf(); ! 945: #ifdef LINEMODE ! 946: } ! 947: #endif ! 948: break; ! 949: ! 950: case TELOPT_SGA: ! 951: #if defined(LINEMODE) && defined(KLUDGELINEMODE) ! 952: /* ! 953: * If kludge linemode is in use, then we ! 954: * must process an incoming do SGA for ! 955: * linemode purposes. ! 956: */ ! 957: if (lmodetype == KLUDGE_LINEMODE) { ! 958: /* ! 959: * The client is asking us to turn ! 960: * linemode on. ! 961: */ ! 962: clientstat(TELOPT_LINEMODE, WILL, 0); ! 963: /* ! 964: * If we did not turn line mode on, ! 965: * then what do we say? Will SGA? ! 966: * This violates design of telnet. ! 967: * Gross. Very Gross. ! 968: */ ! 969: } ! 970: #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ ! 971: ! 972: default: ! 973: break; ! 974: } ! 975: ! 976: set_my_want_state_wont(option); ! 977: if (my_state_is_will(option)) ! 978: send_wont(option, 0); ! 979: } ! 980: set_my_state_wont(option); ! 981: ! 982: } /* end of dontoption */ ! 983: ! 984: /* ! 985: * suboption() ! 986: * ! 987: * Look at the sub-option buffer, and try to be helpful to the other ! 988: * side. ! 989: * ! 990: * Currently we recognize: ! 991: * ! 992: * Terminal type is ! 993: * Linemode ! 994: * Window size ! 995: * Terminal speed ! 996: */ ! 997: suboption() ! 998: { ! 999: register int subchar; ! 1000: extern void unsetenv(); ! 1001: ! 1002: #ifdef DIAGNOSTICS ! 1003: /* ! 1004: * Report receiving option from other side. ! 1005: */ ! 1006: if (diagnostic & TD_OPTIONS) { ! 1007: netflush(); /* get rid of anything waiting to go out */ ! 1008: printsub("td: recv", subpointer, SB_LEN()+2); ! 1009: } ! 1010: #endif DIAGNOSTIC ! 1011: subchar = SB_GET(); ! 1012: switch (subchar) { ! 1013: case TELOPT_TSPEED: { ! 1014: register int xspeed, rspeed; ! 1015: ! 1016: if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ ! 1017: break; ! 1018: ! 1019: settimer(tspeedsubopt); ! 1020: ! 1021: if (SB_EOF() || SB_GET() != TELQUAL_IS) ! 1022: return; ! 1023: ! 1024: xspeed = atoi(subpointer); ! 1025: ! 1026: while (SB_GET() != ',' && !SB_EOF()); ! 1027: if (SB_EOF()) ! 1028: return; ! 1029: ! 1030: rspeed = atoi(subpointer); ! 1031: clientstat(TELOPT_TSPEED, xspeed, rspeed); ! 1032: ! 1033: break; ! 1034: ! 1035: } /* end of case TELOPT_TSPEED */ ! 1036: ! 1037: case TELOPT_TTYPE: { /* Yaaaay! */ ! 1038: static char terminalname[41]; ! 1039: ! 1040: if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ ! 1041: break; ! 1042: settimer(ttypesubopt); ! 1043: ! 1044: if (SB_GET() != TELQUAL_IS) { ! 1045: return; /* ??? XXX but, this is the most robust */ ! 1046: } ! 1047: ! 1048: terminaltype = terminalname; ! 1049: ! 1050: while ((terminaltype < (terminalname + sizeof terminalname-1)) && ! 1051: !SB_EOF()) { ! 1052: register int c; ! 1053: ! 1054: c = SB_GET(); ! 1055: if (isupper(c)) { ! 1056: c = tolower(c); ! 1057: } ! 1058: *terminaltype++ = c; /* accumulate name */ ! 1059: } ! 1060: *terminaltype = 0; ! 1061: terminaltype = terminalname; ! 1062: break; ! 1063: } /* end of case TELOPT_TTYPE */ ! 1064: ! 1065: case TELOPT_NAWS: { ! 1066: register int xwinsize, ywinsize; ! 1067: ! 1068: if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ ! 1069: break; ! 1070: ! 1071: if (SB_EOF()) ! 1072: return; ! 1073: xwinsize = SB_GET() << 8; ! 1074: if (SB_EOF()) ! 1075: return; ! 1076: xwinsize |= SB_GET(); ! 1077: if (SB_EOF()) ! 1078: return; ! 1079: ywinsize = SB_GET() << 8; ! 1080: if (SB_EOF()) ! 1081: return; ! 1082: ywinsize |= SB_GET(); ! 1083: clientstat(TELOPT_NAWS, xwinsize, ywinsize); ! 1084: ! 1085: break; ! 1086: ! 1087: } /* end of case TELOPT_NAWS */ ! 1088: ! 1089: #ifdef LINEMODE ! 1090: case TELOPT_LINEMODE: { ! 1091: register int request; ! 1092: ! 1093: if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ ! 1094: break; ! 1095: /* ! 1096: * Process linemode suboptions. ! 1097: */ ! 1098: if (SB_EOF()) break; /* garbage was sent */ ! 1099: request = SB_GET(); /* get will/wont */ ! 1100: if (SB_EOF()) break; /* another garbage check */ ! 1101: ! 1102: if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ ! 1103: /* ! 1104: * Process suboption buffer of slc's ! 1105: */ ! 1106: start_slc(1); ! 1107: do_opt_slc(subpointer, subend - subpointer); ! 1108: end_slc(0); ! 1109: ! 1110: } else if (request == LM_MODE) { ! 1111: useeditmode = SB_GET(); /* get mode flag */ ! 1112: clientstat(LM_MODE, 0, 0); ! 1113: } ! 1114: ! 1115: switch (SB_GET()) { /* what suboption? */ ! 1116: case LM_FORWARDMASK: ! 1117: /* ! 1118: * According to spec, only server can send request for ! 1119: * forwardmask, and client can only return a positive response. ! 1120: * So don't worry about it. ! 1121: */ ! 1122: ! 1123: default: ! 1124: break; ! 1125: } ! 1126: break; ! 1127: } /* end of case TELOPT_LINEMODE */ ! 1128: #endif ! 1129: case TELOPT_STATUS: { ! 1130: int mode; ! 1131: ! 1132: mode = SB_GET(); ! 1133: switch (mode) { ! 1134: case TELQUAL_SEND: ! 1135: if (my_state_is_will(TELOPT_STATUS)) ! 1136: send_status(); ! 1137: break; ! 1138: ! 1139: case TELQUAL_IS: ! 1140: break; ! 1141: ! 1142: default: ! 1143: break; ! 1144: } ! 1145: break; ! 1146: } /* end of case TELOPT_STATUS */ ! 1147: ! 1148: case TELOPT_XDISPLOC: { ! 1149: if (SB_EOF() || SB_GET() != TELQUAL_IS) ! 1150: return; ! 1151: settimer(xdisplocsubopt); ! 1152: subpointer[SB_LEN()] = '\0'; ! 1153: setenv("DISPLAY", subpointer, 1); ! 1154: break; ! 1155: } /* end of case TELOPT_XDISPLOC */ ! 1156: ! 1157: case TELOPT_ENVIRON: { ! 1158: register int c; ! 1159: register char *cp, *varp, *valp; ! 1160: ! 1161: if (SB_EOF()) ! 1162: return; ! 1163: c = SB_GET(); ! 1164: if (c == TELQUAL_IS) ! 1165: settimer(environsubopt); ! 1166: else if (c != TELQUAL_INFO) ! 1167: return; ! 1168: ! 1169: while (!SB_EOF() && SB_GET() != ENV_VAR) ! 1170: ; ! 1171: ! 1172: if (SB_EOF()) ! 1173: return; ! 1174: ! 1175: cp = varp = subpointer; ! 1176: valp = 0; ! 1177: ! 1178: while (!SB_EOF()) { ! 1179: switch (c = SB_GET()) { ! 1180: case ENV_VALUE: ! 1181: *cp = '\0'; ! 1182: cp = valp = subpointer; ! 1183: break; ! 1184: ! 1185: case ENV_VAR: ! 1186: *cp = '\0'; ! 1187: if (valp) ! 1188: setenv(varp, valp, 1); ! 1189: else ! 1190: unsetenv(varp); ! 1191: cp = varp = subpointer; ! 1192: valp = 0; ! 1193: break; ! 1194: ! 1195: case ENV_ESC: ! 1196: if (SB_EOF()) ! 1197: break; ! 1198: c = SB_GET(); ! 1199: /* FALL THROUGH */ ! 1200: default: ! 1201: *cp++ = c; ! 1202: break; ! 1203: } ! 1204: } ! 1205: *cp = '\0'; ! 1206: if (valp) ! 1207: setenv(varp, valp, 1); ! 1208: else ! 1209: unsetenv(varp); ! 1210: break; ! 1211: } /* end of case TELOPT_ENVIRON */ ! 1212: ! 1213: default: ! 1214: break; ! 1215: } /* end of switch */ ! 1216: ! 1217: } /* end of suboption */ ! 1218: ! 1219: #define ADD(c) *ncp++ = c; ! 1220: #define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } ! 1221: send_status() ! 1222: { ! 1223: unsigned char statusbuf[256]; ! 1224: register unsigned char *ncp; ! 1225: register unsigned char i; ! 1226: ! 1227: ncp = statusbuf; ! 1228: ! 1229: netflush(); /* get rid of anything waiting to go out */ ! 1230: ! 1231: ADD(IAC); ! 1232: ADD(SB); ! 1233: ADD(TELOPT_STATUS); ! 1234: ADD(TELQUAL_IS); ! 1235: ! 1236: for (i = 0; i < NTELOPTS; i++) { ! 1237: if (my_state_is_will(i)) { ! 1238: ADD(WILL); ! 1239: ADD_DATA(i); ! 1240: if (i == IAC) ! 1241: ADD(IAC); ! 1242: } ! 1243: if (his_state_is_will(i)) { ! 1244: ADD(DO); ! 1245: ADD_DATA(i); ! 1246: if (i == IAC) ! 1247: ADD(IAC); ! 1248: } ! 1249: } ! 1250: ! 1251: #ifdef LINEMODE ! 1252: if (his_state_is_will(TELOPT_LINEMODE)) { ! 1253: unsigned char *cp, *cpe; ! 1254: int len; ! 1255: ! 1256: ADD(SB); ! 1257: ADD(TELOPT_LINEMODE); ! 1258: ADD(LM_MODE); ! 1259: ADD_DATA(editmode); ! 1260: if (editmode == IAC) ! 1261: ADD(IAC); ! 1262: ADD(SE); ! 1263: ! 1264: ADD(SB); ! 1265: ADD(TELOPT_LINEMODE); ! 1266: ADD(LM_SLC); ! 1267: start_slc(0); ! 1268: send_slc(); ! 1269: len = end_slc(&cp); ! 1270: for (cpe = cp + len; cp < cpe; cp++) ! 1271: ADD_DATA(*cp); ! 1272: ADD(SE); ! 1273: } ! 1274: #endif /* LINEMODE */ ! 1275: ! 1276: ADD(IAC); ! 1277: ADD(SE); ! 1278: ! 1279: writenet(statusbuf, ncp - statusbuf); ! 1280: netflush(); /* Send it on its way */ ! 1281: #ifdef DIAGNOSTICS ! 1282: /* ! 1283: * Report sending status suboption. ! 1284: */ ! 1285: if (diagnostic & TD_OPTIONS) { ! 1286: printsub("td: send", statusbuf, ncp - statusbuf); ! 1287: netflush(); /* Send it on its way */ ! 1288: } ! 1289: #endif DIAGNOSTIC ! 1290: } ! 1291: ! 1292: #ifdef NO_SETENV ! 1293: #include "setenv.c" ! 1294: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.