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