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