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