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