1 /* 2 * Copyright (c) 1988 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 this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)telnet.c 5.31 (Berkeley) 05/15/88"; 15 #endif /* not lint */ 16 17 #include <sys/types.h> 18 19 #if defined(unix) 20 #include <signal.h> 21 /* By the way, we need to include curses.h before telnet.h since, 22 * among other things, telnet.h #defines 'DO', which is a variable 23 * declared in curses.h. 24 */ 25 #include <curses.h> 26 #endif /* defined(unix) */ 27 28 #include <arpa/telnet.h> 29 30 #if defined(unix) 31 #include <strings.h> 32 #else /* defined(unix) */ 33 #include <string.h> 34 #endif /* defined(unix) */ 35 36 #include "ring.h" 37 38 #include "defines.h" 39 #include "externs.h" 40 #include "types.h" 41 #include "general.h" 42 43 44 #define strip(x) ((x)&0x7f) 45 46 47 static char subbuffer[SUBBUFSIZE], 48 *subpointer, *subend; /* buffer for sub-options */ 49 #define SB_CLEAR() subpointer = subbuffer; 50 #define SB_TERM() subend = subpointer; 51 #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 52 *subpointer++ = (c); \ 53 } 54 55 char hisopts[256]; 56 char myopts[256]; 57 58 char doopt[] = { IAC, DO, '%', 'c', 0 }; 59 char dont[] = { IAC, DONT, '%', 'c', 0 }; 60 char will[] = { IAC, WILL, '%', 'c', 0 }; 61 char wont[] = { IAC, WONT, '%', 'c', 0 }; 62 63 int 64 connected, 65 showoptions, 66 In3270, /* Are we in 3270 mode? */ 67 ISend, /* trying to send network data in */ 68 debug = 0, 69 crmod, 70 netdata, /* Print out network data flow */ 71 crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ 72 noasynch = 0, /* User specified "-noasynch" on command line */ 73 askedSGA = 0, /* We have talked about suppress go ahead */ 74 telnetport, 75 SYNCHing, /* we are in TELNET SYNCH mode */ 76 flushout, /* flush output */ 77 autoflush = 0, /* flush output when interrupting? */ 78 autosynch, /* send interrupt characters with SYNCH? */ 79 localchars, /* we recognize interrupt/quit */ 80 donelclchars, /* the user has set "localchars" */ 81 donebinarytoggle, /* the user has put us in binary */ 82 dontlecho, /* do we suppress local echoing right now? */ 83 globalmode; 84 85 #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 86 87 char 88 *prompt = 0, 89 escape, 90 echoc; 91 92 /* 93 * Telnet receiver states for fsm 94 */ 95 #define TS_DATA 0 96 #define TS_IAC 1 97 #define TS_WILL 2 98 #define TS_WONT 3 99 #define TS_DO 4 100 #define TS_DONT 5 101 #define TS_CR 6 102 #define TS_SB 7 /* sub-option collection */ 103 #define TS_SE 8 /* looking for sub-option end */ 104 105 static int telrcv_state; 106 107 jmp_buf toplevel = { 0 }; 108 jmp_buf peerdied; 109 110 int flushline; 111 112 /* 113 * The following are some clocks used to decide how to interpret 114 * the relationship between various variables. 115 */ 116 117 Clocks clocks; 118 119 Modelist modelist[] = { 120 { "telnet command mode", COMMAND_LINE }, 121 { "character-at-a-time mode", 0 }, 122 { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 123 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 124 { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 125 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 126 { "3270 mode", 0 }, 127 }; 128 129 130 /* 131 * Initialize telnet environment. 132 */ 133 134 init_telnet() 135 { 136 SB_CLEAR(); 137 ClearArray(hisopts); 138 ClearArray(myopts); 139 140 connected = In3270 = ISend = donebinarytoggle = 0; 141 142 #if defined(unix) && defined(TN3270) 143 HaveInput = 0; 144 #endif /* defined(unix) && defined(TN3270) */ 145 146 SYNCHing = 0; 147 148 /* Don't change NetTrace */ 149 150 escape = CONTROL(']'); 151 echoc = CONTROL('E'); 152 153 flushline = 1; 154 telrcv_state = TS_DATA; 155 } 156 157 158 #include <varargs.h> 159 160 static void 161 printring(va_alist) 162 va_dcl 163 { 164 va_list ap; 165 char buffer[100]; /* where things go */ 166 char *ptr; 167 char *format; 168 char *string; 169 Ring *ring; 170 int i; 171 172 va_start(ap); 173 174 ring = va_arg(ap, Ring *); 175 format = va_arg(ap, char *); 176 ptr = buffer; 177 178 while ((i = *format++) != 0) { 179 if (i == '%') { 180 i = *format++; 181 switch (i) { 182 case 'c': 183 *ptr++ = va_arg(ap, int); 184 break; 185 case 's': 186 string = va_arg(ap, char *); 187 ring_supply_data(ring, buffer, ptr-buffer); 188 ring_supply_data(ring, string, strlen(string)); 189 ptr = buffer; 190 break; 191 case 0: 192 ExitString("printring: trailing %%.\n", 1); 193 /*NOTREACHED*/ 194 default: 195 ExitString("printring: unknown format character.\n", 1); 196 /*NOTREACHED*/ 197 } 198 } else { 199 *ptr++ = i; 200 } 201 } 202 ring_supply_data(ring, buffer, ptr-buffer); 203 } 204 205 206 void 207 willoption(option, reply) 208 int option, reply; 209 { 210 char *fmt; 211 212 switch (option) { 213 214 case TELOPT_ECHO: 215 # if defined(TN3270) 216 /* 217 * The following is a pain in the rear-end. 218 * Various IBM servers (some versions of Wiscnet, 219 * possibly Fibronics/Spartacus, and who knows who 220 * else) will NOT allow us to send "DO SGA" too early 221 * in the setup proceedings. On the other hand, 222 * 4.2 servers (telnetd) won't set SGA correctly. 223 * So, we are stuck. Empirically (but, based on 224 * a VERY small sample), the IBM servers don't send 225 * out anything about ECHO, so we postpone our sending 226 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 227 * DO send). 228 */ 229 { 230 if (askedSGA == 0) { 231 askedSGA = 1; 232 if (!hisopts[TELOPT_SGA]) { 233 willoption(TELOPT_SGA, 0); 234 } 235 } 236 } 237 /* Fall through */ 238 case TELOPT_EOR: 239 case TELOPT_BINARY: 240 #endif /* defined(TN3270) */ 241 case TELOPT_SGA: 242 settimer(modenegotiated); 243 hisopts[option] = 1; 244 fmt = doopt; 245 setconnmode(); /* possibly set new tty mode */ 246 break; 247 248 case TELOPT_TM: 249 return; /* Never reply to TM will's/wont's */ 250 251 default: 252 fmt = dont; 253 break; 254 } 255 printring(&netoring, fmt, option); 256 if (reply) 257 printoption(">SENT", fmt, option, reply); 258 else 259 printoption("<SENT", fmt, option, reply); 260 } 261 262 void 263 wontoption(option, reply) 264 int option, reply; 265 { 266 char *fmt; 267 268 switch (option) { 269 270 case TELOPT_ECHO: 271 case TELOPT_SGA: 272 settimer(modenegotiated); 273 hisopts[option] = 0; 274 fmt = dont; 275 setconnmode(); /* Set new tty mode */ 276 break; 277 278 case TELOPT_TM: 279 return; /* Never reply to TM will's/wont's */ 280 281 default: 282 fmt = dont; 283 } 284 printring(&netoring, fmt, option); 285 if (reply) 286 printoption(">SENT", fmt, option, reply); 287 else 288 printoption("<SENT", fmt, option, reply); 289 } 290 291 static void 292 dooption(option) 293 int option; 294 { 295 char *fmt; 296 297 switch (option) { 298 299 case TELOPT_TM: 300 fmt = will; 301 break; 302 303 # if defined(TN3270) 304 case TELOPT_EOR: 305 case TELOPT_BINARY: 306 # endif /* defined(TN3270) */ 307 case TELOPT_TTYPE: /* terminal type option */ 308 case TELOPT_SGA: /* no big deal */ 309 fmt = will; 310 myopts[option] = 1; 311 break; 312 313 case TELOPT_ECHO: /* We're never going to echo... */ 314 default: 315 fmt = wont; 316 break; 317 } 318 printring(&netoring, fmt, option); 319 printoption(">SENT", fmt, option, 0); 320 } 321 322 /* 323 * suboption() 324 * 325 * Look at the sub-option buffer, and try to be helpful to the other 326 * side. 327 * 328 * Currently we recognize: 329 * 330 * Terminal type, send request. 331 */ 332 333 static void 334 suboption() 335 { 336 printsub("<", subbuffer, subend-subbuffer+1); 337 switch (subbuffer[0]&0xff) { 338 case TELOPT_TTYPE: 339 if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 340 ; 341 } else { 342 char *name; 343 char namebuf[41]; 344 extern char *getenv(); 345 int len; 346 347 #if defined(TN3270) 348 if (tn3270_ttype()) { 349 return; 350 } 351 #endif /* defined(TN3270) */ 352 name = getenv("TERM"); 353 if ((name == 0) || ((len = strlen(name)) > 40)) { 354 name = "UNKNOWN"; 355 len = strlen(name); 356 } 357 if ((len + 4+2) < NETROOM()) { 358 strcpy(namebuf, name); 359 upcase(namebuf); 360 printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 361 TELQUAL_IS, namebuf, IAC, SE); 362 /* XXX */ 363 /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */ 364 } else { 365 ExitString("No room in buffer for terminal type.\n", 366 1); 367 /*NOTREACHED*/ 368 } 369 } 370 371 default: 372 break; 373 } 374 } 375 376 377 int 378 telrcv() 379 { 380 register int c; 381 register int scc; 382 register char *sbp; 383 int count; 384 int returnValue = 0; 385 386 scc = 0; 387 count = 0; 388 while (TTYROOM() > 2) { 389 if (scc == 0) { 390 if (count) { 391 ring_consumed(&netiring, count); 392 returnValue = 1; 393 count = 0; 394 } 395 sbp = netiring.consume; 396 scc = ring_full_consecutive(&netiring); 397 if (scc == 0) { 398 /* No more data coming in */ 399 break; 400 } 401 } 402 403 c = *sbp++ & 0xff, scc--; count++; 404 405 switch (telrcv_state) { 406 407 case TS_CR: 408 telrcv_state = TS_DATA; 409 if (c == '\0') { 410 break; /* Ignore \0 after CR */ 411 } else if (c == '\n') { 412 if ((!hisopts[TELOPT_ECHO]) && !crmod) { 413 TTYADD(c); 414 } 415 break; 416 } 417 /* Else, fall through */ 418 419 case TS_DATA: 420 if (c == IAC) { 421 telrcv_state = TS_IAC; 422 break; 423 } 424 # if defined(TN3270) 425 if (In3270) { 426 *Ifrontp++ = c; 427 while (scc > 0) { 428 c = *sbp++ & 0377, scc--; count++; 429 if (c == IAC) { 430 telrcv_state = TS_IAC; 431 break; 432 } 433 *Ifrontp++ = c; 434 } 435 } else 436 # endif /* defined(TN3270) */ 437 /* 438 * The 'crmod' hack (see following) is needed 439 * since we can't * set CRMOD on output only. 440 * Machines like MULTICS like to send \r without 441 * \n; since we must turn off CRMOD to get proper 442 * input, the mapping is done here (sigh). 443 */ 444 if ((c == '\r') && !hisopts[TELOPT_BINARY]) { 445 if (scc > 0) { 446 c = *sbp&0xff; 447 if (c == 0) { 448 sbp++, scc--; count++; 449 /* a "true" CR */ 450 TTYADD('\r'); 451 } else if (!hisopts[TELOPT_ECHO] && 452 (c == '\n')) { 453 sbp++, scc--; count++; 454 TTYADD('\n'); 455 } else { 456 TTYADD('\r'); 457 if (crmod) { 458 TTYADD('\n'); 459 } 460 } 461 } else { 462 telrcv_state = TS_CR; 463 TTYADD('\r'); 464 if (crmod) { 465 TTYADD('\n'); 466 } 467 } 468 } else { 469 TTYADD(c); 470 } 471 continue; 472 473 case TS_IAC: 474 switch (c) { 475 476 case WILL: 477 telrcv_state = TS_WILL; 478 continue; 479 480 case WONT: 481 telrcv_state = TS_WONT; 482 continue; 483 484 case DO: 485 telrcv_state = TS_DO; 486 continue; 487 488 case DONT: 489 telrcv_state = TS_DONT; 490 continue; 491 492 case DM: 493 /* 494 * We may have missed an urgent notification, 495 * so make sure we flush whatever is in the 496 * buffer currently. 497 */ 498 SYNCHing = 1; 499 ttyflush(1); 500 SYNCHing = stilloob(); 501 settimer(gotDM); 502 break; 503 504 case NOP: 505 case GA: 506 break; 507 508 case SB: 509 SB_CLEAR(); 510 telrcv_state = TS_SB; 511 continue; 512 513 # if defined(TN3270) 514 case EOR: 515 if (In3270) { 516 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 517 if (Ibackp == Ifrontp) { 518 Ibackp = Ifrontp = Ibuf; 519 ISend = 0; /* should have been! */ 520 } else { 521 ISend = 1; 522 } 523 } 524 break; 525 # endif /* defined(TN3270) */ 526 527 case IAC: 528 # if !defined(TN3270) 529 TTYADD(IAC); 530 # else /* !defined(TN3270) */ 531 if (In3270) { 532 *Ifrontp++ = IAC; 533 } else { 534 TTYADD(IAC); 535 } 536 # endif /* !defined(TN3270) */ 537 break; 538 539 default: 540 break; 541 } 542 telrcv_state = TS_DATA; 543 continue; 544 545 case TS_WILL: 546 printoption(">RCVD", will, c, !hisopts[c]); 547 if (c == TELOPT_TM) { 548 if (flushout) { 549 flushout = 0; 550 } 551 } else if (!hisopts[c]) { 552 willoption(c, 1); 553 } 554 SetIn3270(); 555 telrcv_state = TS_DATA; 556 continue; 557 558 case TS_WONT: 559 printoption(">RCVD", wont, c, hisopts[c]); 560 if (c == TELOPT_TM) { 561 if (flushout) { 562 flushout = 0; 563 } 564 } else if (hisopts[c]) { 565 wontoption(c, 1); 566 } 567 SetIn3270(); 568 telrcv_state = TS_DATA; 569 continue; 570 571 case TS_DO: 572 printoption(">RCVD", doopt, c, !myopts[c]); 573 if (!myopts[c]) 574 dooption(c); 575 SetIn3270(); 576 telrcv_state = TS_DATA; 577 continue; 578 579 case TS_DONT: 580 printoption(">RCVD", dont, c, myopts[c]); 581 if (myopts[c]) { 582 myopts[c] = 0; 583 printring(&netoring, wont, c); 584 flushline = 1; 585 setconnmode(); /* set new tty mode (maybe) */ 586 printoption(">SENT", wont, c, 0); 587 } 588 SetIn3270(); 589 telrcv_state = TS_DATA; 590 continue; 591 592 case TS_SB: 593 if (c == IAC) { 594 telrcv_state = TS_SE; 595 } else { 596 SB_ACCUM(c); 597 } 598 continue; 599 600 case TS_SE: 601 if (c != SE) { 602 if (c != IAC) { 603 SB_ACCUM(IAC); 604 } 605 SB_ACCUM(c); 606 telrcv_state = TS_SB; 607 } else { 608 SB_TERM(); 609 suboption(); /* handle sub-option */ 610 SetIn3270(); 611 telrcv_state = TS_DATA; 612 } 613 } 614 } 615 if (count) 616 ring_consumed(&netiring, count); 617 return returnValue||count; 618 } 619 620 static int 621 telsnd() 622 { 623 int tcc; 624 int count; 625 int returnValue = 0; 626 char *tbp; 627 628 tcc = 0; 629 count = 0; 630 while (NETROOM() > 2) { 631 register int sc; 632 register int c; 633 634 if (tcc == 0) { 635 if (count) { 636 ring_consumed(&ttyiring, count); 637 returnValue = 1; 638 count = 0; 639 } 640 tbp = ttyiring.consume; 641 tcc = ring_full_consecutive(&ttyiring); 642 if (tcc == 0) { 643 break; 644 } 645 } 646 c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; 647 if (sc == escape) { 648 command(0); 649 tcc = 0; 650 flushline = 1; 651 break; 652 } else if (MODE_LINE(globalmode) && (sc == echoc)) { 653 if (tcc > 0 && strip(*tbp) == echoc) { 654 tcc--; tbp++; count++; 655 } else { 656 dontlecho = !dontlecho; 657 settimer(echotoggle); 658 setconnmode(); 659 flushline = 1; 660 break; 661 } 662 } 663 if (localchars) { 664 if (TerminalSpecialChars(sc) == 0) { 665 break; 666 } 667 } 668 if (!myopts[TELOPT_BINARY]) { 669 switch (c) { 670 case '\n': 671 /* 672 * If we are in CRMOD mode (\r ==> \n) 673 * on our local machine, then probably 674 * a newline (unix) is CRLF (TELNET). 675 */ 676 if (MODE_LOCAL_CHARS(globalmode)) { 677 NETADD('\r'); 678 } 679 NETADD('\n'); 680 flushline = 1; 681 break; 682 case '\r': 683 if (!crlf) { 684 NET2ADD('\r', '\0'); 685 } else { 686 NET2ADD('\r', '\n'); 687 } 688 flushline = 1; 689 break; 690 case IAC: 691 NET2ADD(IAC, IAC); 692 break; 693 default: 694 NETADD(c); 695 break; 696 } 697 } else if (c == IAC) { 698 NET2ADD(IAC, IAC); 699 } else { 700 NETADD(c); 701 } 702 } 703 if (count) 704 ring_consumed(&ttyiring, count); 705 return returnValue||count; /* Non-zero if we did anything */ 706 } 707 708 /* 709 * Scheduler() 710 * 711 * Try to do something. 712 * 713 * If we do something useful, return 1; else return 0. 714 * 715 */ 716 717 718 int 719 Scheduler(block) 720 int block; /* should we block in the select ? */ 721 { 722 register int c; 723 /* One wants to be a bit careful about setting returnValue 724 * to one, since a one implies we did some useful work, 725 * and therefore probably won't be called to block next 726 * time (TN3270 mode only). 727 */ 728 int returnValue; 729 int netin, netout, netex, ttyin, ttyout; 730 731 /* Decide which rings should be processed */ 732 733 netout = ring_full_count(&netoring) && 734 (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]); 735 ttyout = ring_full_count(&ttyoring); 736 737 #if defined(TN3270) 738 ttyin = ring_empty_count(&ttyiring) && (shell_active == 0); 739 #else /* defined(TN3270) */ 740 ttyin = ring_empty_count(&ttyiring); 741 #endif /* defined(TN3270) */ 742 743 #if defined(TN3270) 744 netin = ring_empty_count(&netiring); 745 # else /* !defined(TN3270) */ 746 netin = !ISend && ring_empty_count(&netiring); 747 # endif /* !defined(TN3270) */ 748 749 netex = !SYNCHing; 750 751 /* If we have seen a signal recently, reset things */ 752 # if defined(TN3270) && defined(unix) 753 if (HaveInput) { 754 HaveInput = 0; 755 signal(SIGIO, inputAvailable); 756 } 757 #endif /* defined(TN3270) && defined(unix) */ 758 759 /* Call to system code to process rings */ 760 761 returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); 762 763 /* Now, look at the input rings, looking for work to do. */ 764 765 if (ring_full_count(&ttyiring)) { 766 # if defined(TN3270) 767 if (In3270) { 768 c = DataFromTerminal(ttyiring.consume, 769 ring_full_consecutive(&ttyiring)); 770 if (c) { 771 returnValue = 1; 772 ring_consumed(&ttyiring, c); 773 } 774 } else { 775 # endif /* defined(TN3270) */ 776 returnValue |= telsnd(); 777 # if defined(TN3270) 778 } 779 # endif /* defined(TN3270) */ 780 } 781 782 if (ring_full_count(&netiring)) { 783 # if !defined(TN3270) 784 returnValue |= telrcv(); 785 # else /* !defined(TN3270) */ 786 returnValue = Push3270(); 787 # endif /* !defined(TN3270) */ 788 } 789 return returnValue; 790 } 791 792 /* 793 * Select from tty and network... 794 */ 795 void 796 telnet() 797 { 798 sys_telnet_init(); 799 800 # if !defined(TN3270) 801 if (telnetport) { 802 if (!hisopts[TELOPT_SGA]) { 803 willoption(TELOPT_SGA, 0); 804 } 805 if (!myopts[TELOPT_TTYPE]) { 806 dooption(TELOPT_TTYPE, 0); 807 } 808 } 809 # endif /* !defined(TN3270) */ 810 811 # if !defined(TN3270) 812 for (;;) { 813 int schedValue; 814 815 while ((schedValue = Scheduler(0)) != 0) { 816 if (schedValue == -1) { 817 setcommandmode(); 818 return; 819 } 820 } 821 822 if (Scheduler(1) == -1) { 823 setcommandmode(); 824 return; 825 } 826 } 827 # else /* !defined(TN3270) */ 828 for (;;) { 829 int schedValue; 830 831 while (!In3270 && !shell_active) { 832 if (Scheduler(1) == -1) { 833 setcommandmode(); 834 return; 835 } 836 } 837 838 while ((schedValue = Scheduler(0)) != 0) { 839 if (schedValue == -1) { 840 setcommandmode(); 841 return; 842 } 843 } 844 /* If there is data waiting to go out to terminal, don't 845 * schedule any more data for the terminal. 846 */ 847 if (ring_full_count(&ttyoring)) { 848 schedValue = 1; 849 } else { 850 if (shell_active) { 851 if (shell_continue() == 0) { 852 ConnectScreen(); 853 } 854 } else if (In3270) { 855 schedValue = DoTerminalOutput(); 856 } 857 } 858 if (schedValue && (shell_active == 0)) { 859 if (Scheduler(1) == -1) { 860 setcommandmode(); 861 return; 862 } 863 } 864 } 865 # endif /* !defined(TN3270) */ 866 } 867 868 /* 869 * nextitem() 870 * 871 * Return the address of the next "item" in the TELNET data 872 * stream. This will be the address of the next character if 873 * the current address is a user data character, or it will 874 * be the address of the character following the TELNET command 875 * if the current address is a TELNET IAC ("I Am a Command") 876 * character. 877 */ 878 879 static char * 880 nextitem(current) 881 char *current; 882 { 883 if ((*current&0xff) != IAC) { 884 return current+1; 885 } 886 switch (*(current+1)&0xff) { 887 case DO: 888 case DONT: 889 case WILL: 890 case WONT: 891 return current+3; 892 case SB: /* loop forever looking for the SE */ 893 { 894 register char *look = current+2; 895 896 for (;;) { 897 if ((*look++&0xff) == IAC) { 898 if ((*look++&0xff) == SE) { 899 return look; 900 } 901 } 902 } 903 } 904 default: 905 return current+2; 906 } 907 } 908 909 /* 910 * netclear() 911 * 912 * We are about to do a TELNET SYNCH operation. Clear 913 * the path to the network. 914 * 915 * Things are a bit tricky since we may have sent the first 916 * byte or so of a previous TELNET command into the network. 917 * So, we have to scan the network buffer from the beginning 918 * until we are up to where we want to be. 919 * 920 * A side effect of what we do, just to keep things 921 * simple, is to clear the urgent data pointer. The principal 922 * caller should be setting the urgent data pointer AFTER calling 923 * us in any case. 924 */ 925 926 static void 927 netclear() 928 { 929 #if 0 /* XXX */ 930 register char *thisitem, *next; 931 char *good; 932 #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 933 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 934 935 thisitem = netobuf; 936 937 while ((next = nextitem(thisitem)) <= netobuf.send) { 938 thisitem = next; 939 } 940 941 /* Now, thisitem is first before/at boundary. */ 942 943 good = netobuf; /* where the good bytes go */ 944 945 while (netoring.add > thisitem) { 946 if (wewant(thisitem)) { 947 int length; 948 949 next = thisitem; 950 do { 951 next = nextitem(next); 952 } while (wewant(next) && (nfrontp > next)); 953 length = next-thisitem; 954 memcpy(good, thisitem, length); 955 good += length; 956 thisitem = next; 957 } else { 958 thisitem = nextitem(thisitem); 959 } 960 } 961 962 #endif /* 0 */ 963 } 964 965 /* 966 * These routines add various telnet commands to the data stream. 967 */ 968 969 static void 970 doflush() 971 { 972 NET2ADD(IAC, DO); 973 NETADD(TELOPT_TM); 974 flushline = 1; 975 flushout = 1; 976 ttyflush(1); /* Flush/drop output */ 977 /* do printoption AFTER flush, otherwise the output gets tossed... */ 978 printoption("<SENT", doopt, TELOPT_TM, 0); 979 } 980 981 void 982 xmitAO() 983 { 984 NET2ADD(IAC, AO); 985 if (autoflush) { 986 doflush(); 987 } 988 } 989 990 991 void 992 xmitEL() 993 { 994 NET2ADD(IAC, EL); 995 } 996 997 void 998 xmitEC() 999 { 1000 NET2ADD(IAC, EC); 1001 } 1002 1003 1004 #if defined(NOT43) 1005 int 1006 #else /* defined(NOT43) */ 1007 void 1008 #endif /* defined(NOT43) */ 1009 dosynch() 1010 { 1011 netclear(); /* clear the path to the network */ 1012 NETADD(IAC); 1013 setneturg(); 1014 NETADD(DM); 1015 1016 #if defined(NOT43) 1017 return 0; 1018 #endif /* defined(NOT43) */ 1019 } 1020 1021 void 1022 intp() 1023 { 1024 NET2ADD(IAC, IP); 1025 flushline = 1; 1026 if (autoflush) { 1027 doflush(); 1028 } 1029 if (autosynch) { 1030 dosynch(); 1031 } 1032 } 1033 1034 void 1035 sendbrk() 1036 { 1037 NET2ADD(IAC, BREAK); 1038 flushline = 1; 1039 if (autoflush) { 1040 doflush(); 1041 } 1042 if (autosynch) { 1043 dosynch(); 1044 } 1045 } 1046