1 /* 2 * Copyright (c) 1984-1987 by the Regents of the 3 * University of California and by Gregory Glenn Minshall. 4 * 5 * Permission to use, copy, modify, and distribute these 6 * programs and their documentation for any purpose and 7 * without fee is hereby granted, provided that this 8 * copyright and permission appear on all copies and 9 * supporting documentation, the name of the Regents of 10 * the University of California not be used in advertising 11 * or publicity pertaining to distribution of the programs 12 * without specific prior permission, and notice be given in 13 * supporting documentation that copying and distribution is 14 * by permission of the Regents of the University of California 15 * and by Gregory Glenn Minshall. Neither the Regents of the 16 * University of California nor Gregory Glenn Minshall make 17 * representations about the suitability of this software 18 * for any purpose. It is provided "as is" without 19 * express or implied warranty. 20 */ 21 22 #ifndef lint 23 static char copyright[] = 24 "@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\ 25 All rights reserved.\n"; 26 #endif /* not lint */ 27 28 #ifndef lint 29 static char sccsid[] = "@(#)telnet.c 1.18.1.1 (Berkeley) 12/04/88"; 30 #endif /* not lint */ 31 32 /* 33 * User telnet program, modified for use by tn3270.c. 34 * 35 * Many of the FUNCTIONAL changes in this newest version of TELNET 36 * were suggested by Dave Borman of Cray Research, Inc. 37 * 38 * Other changes in the tn3270 side come from Alan Crosswell (Columbia), 39 * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley). 40 * 41 * This code is common between telnet(1c) and tn3270(1c). There are the 42 * following defines used to generate the various versions: 43 * 44 * TN3270 - This is to be linked with tn3270. 45 * 46 * NOT43 - Allows the program to compile and run on 47 * a 4.2BSD system. 48 * 49 * PUTCHAR - Within tn3270, on a NOT43 system, 50 * allows the use of the 4.3 curses 51 * (greater speed updating the screen). 52 * You need the 4.3 curses for this to work. 53 * 54 * FD_SETSIZE - On whichever system, if this isn't defined, 55 * we patch over the FD_SET, etc., macros with 56 * some homebrewed ones. 57 * 58 * SO_OOBINLINE - This is a socket option which we would like 59 * to set to allow TCP urgent data to come 60 * to us "inline". This is NECESSARY for 61 * CORRECT operation, and desireable for 62 * simpler operation. 63 * 64 * LNOFLSH - Detects the presence of the LNOFLSH bit 65 * in the tty structure. 66 * 67 * unix - Compiles in unix specific stuff. 68 * 69 * MSDOS - Compiles in MSDOS specific stuff. 70 * 71 */ 72 73 #if !defined(TN3270) 74 #define ExitString(f,s,r) { fprintf(f, s); exit(r); } 75 #define Exit(x) exit(x) 76 #define SetIn3270() 77 78 void setcommandmode(), command(); /* forward declarations */ 79 #endif /* !defined(TN3270) */ 80 81 #include <sys/types.h> 82 #include <sys/socket.h> 83 84 #include <netinet/in.h> 85 86 #if defined(unix) 87 /* By the way, we need to include curses.h before telnet.h since, 88 * among other things, telnet.h #defines 'DO', which is a variable 89 * declared in curses.h. 90 */ 91 #include <curses.h> 92 #endif /* defined(unix) */ 93 94 #define TELOPTS 95 #include <arpa/telnet.h> 96 97 #if !defined(NOT43) 98 #include <arpa/inet.h> 99 #else /* !defined(NOT43) */ 100 extern unsigned long inet_addr(); 101 extern char *inet_ntoa(); 102 #endif /* !defined(NOT43) */ 103 104 #include <stdio.h> 105 #include <ctype.h> 106 #include <errno.h> 107 #include <setjmp.h> 108 #include <netdb.h> 109 110 #if defined(unix) 111 #include <strings.h> 112 #else /* defined(unix) */ 113 #include <string.h> 114 #endif /* defined(unix) */ 115 116 #if defined(TN3270) 117 #include "ascii/termin.ext" 118 #include "ctlr/screen.h" 119 #include "ctlr/oia.h" 120 #include "ctlr/options.ext" 121 #include "ctlr/outbound.ext" 122 #include "general/globals.h" 123 #include "telnet.ext" 124 #endif /* defined(TN3270) */ 125 126 #include "general/general.h" 127 128 129 130 #ifndef FD_SETSIZE 131 /* 132 * The following is defined just in case someone should want to run 133 * this telnet on a 4.2 system. 134 * 135 */ 136 137 #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) 138 #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) 139 #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) 140 #define FD_ZERO(p) ((p)->fds_bits[0] = 0) 141 142 #endif 143 144 #define strip(x) ((x)&0x7f) 145 #define min(x,y) ((x<y)? x:y) 146 147 #if defined(TN3270) 148 static char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp; 149 #endif /* defined(TN3270) */ 150 151 static char ttyobuf[2*BUFSIZ], *tfrontp, *tbackp; 152 #define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } } 153 #define TTYLOC() (tfrontp) 154 #define TTYMAX() (ttyobuf+sizeof ttyobuf-1) 155 #define TTYMIN() (netobuf) 156 #define TTYBYTES() (tfrontp-tbackp) 157 #define TTYROOM() (TTYMAX()-TTYLOC()+1) 158 159 static char netobuf[2*BUFSIZ], *nfrontp, *nbackp; 160 #define NETADD(c) { *nfrontp++ = c; } 161 #define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); } 162 #define NETLOC() (nfrontp) 163 #define NETMAX() (netobuf+sizeof netobuf-1) 164 #define NETBYTES() (nfrontp-nbackp) 165 #define NETROOM() (NETMAX()-NETLOC()+1) 166 static char *neturg; /* one past last byte of urgent data */ 167 168 static char subbuffer[100], 169 *subpointer, *subend; /* buffer for sub-options */ 170 #define SB_CLEAR() subpointer = subbuffer; 171 #define SB_TERM() subend = subpointer; 172 #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 173 *subpointer++ = (c); \ 174 } 175 176 static char sb_terminal[] = { IAC, SB, 177 TELOPT_TTYPE, TELQUAL_IS, 178 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2', 179 IAC, SE }; 180 #define SBTERMMODEL 13 181 182 183 static char hisopts[256]; 184 static char myopts[256]; 185 186 static char doopt[] = { IAC, DO, '%', 'c', 0 }; 187 static char dont[] = { IAC, DONT, '%', 'c', 0 }; 188 static char will[] = { IAC, WILL, '%', 'c', 0 }; 189 static char wont[] = { IAC, WONT, '%', 'c', 0 }; 190 191 struct cmd { 192 char *name; /* command name */ 193 char *help; /* help string */ 194 int (*handler)(); /* routine which executes command */ 195 int dohelp; /* Should we give general help information? */ 196 int needconnect; /* Do we need to be connected to execute? */ 197 }; 198 199 static char sibuf[BUFSIZ], *sbp; 200 static char tibuf[BUFSIZ], *tbp; 201 static fd_set ibits, obits, xbits; 202 203 204 static int 205 connected, 206 net, 207 scc, 208 tcc, 209 showoptions, 210 In3270, /* Are we in 3270 mode? */ 211 ISend, /* trying to send network data in */ 212 debug = 0, 213 crmod, 214 netdata, 215 noasynch = 0, /* User specified "-noasynch" on command line */ 216 askedSGA = 0, /* We have talked about suppress go ahead */ 217 telnetport = 1; 218 219 static FILE *NetTrace = 0; /* Not in bss, since needs to stay */ 220 221 #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 222 223 static char 224 *prompt = 0, 225 escape, 226 echoc; 227 228 static int 229 SYNCHing, /* we are in TELNET SYNCH mode */ 230 flushout, /* flush output */ 231 autoflush = 0, /* flush output when interrupting? */ 232 autosynch, /* send interrupt characters with SYNCH? */ 233 localchars, /* we recognize interrupt/quit */ 234 donelclchars, /* the user has set "localchars" */ 235 donebinarytoggle, /* the user has put us in binary */ 236 dontlecho, /* do we suppress local echoing right now? */ 237 globalmode; 238 239 /* The following are some tn3270 specific flags */ 240 #if defined(TN3270) 241 242 static int 243 Sent3270TerminalType; /* Have we said we are a 3270? */ 244 245 /* Some real, live, globals. */ 246 int 247 tout, /* Output file descriptor */ 248 tin; /* Input file descriptor */ 249 250 #else /* defined(TN3270) */ 251 static int tin, tout; /* file descriptors */ 252 #endif /* defined(TN3270) */ 253 254 255 /* 256 * Telnet receiver states for fsm 257 */ 258 #define TS_DATA 0 259 #define TS_IAC 1 260 #define TS_WILL 2 261 #define TS_WONT 3 262 #define TS_DO 4 263 #define TS_DONT 5 264 #define TS_CR 6 265 #define TS_SB 7 /* sub-option collection */ 266 #define TS_SE 8 /* looking for sub-option end */ 267 268 static int telrcv_state = TS_DATA; 269 270 static char line[200]; 271 static int margc; 272 static char *margv[20]; 273 274 static jmp_buf toplevel = { 0 }; 275 static jmp_buf peerdied; 276 277 extern int errno; 278 279 280 static struct sockaddr_in sin; 281 282 static struct servent *sp = 0; 283 284 static int flushline; 285 286 static char *hostname; 287 static char hnamebuf[32]; 288 289 /* 290 * The following are some clocks used to decide how to interpret 291 * the relationship between various variables. 292 */ 293 294 static struct { 295 int 296 system, /* what the current time is */ 297 echotoggle, /* last time user entered echo character */ 298 modenegotiated, /* last time operating mode negotiated */ 299 didnetreceive, /* last time we read data from network */ 300 gotDM; /* when did we last see a data mark */ 301 } clocks; 302 303 #define settimer(x) clocks.x = clocks.system++ 304 305 /* Various modes */ 306 #define MODE_LINE(m) (modelist[m].modetype & LINE) 307 #define MODE_LOCAL_CHARS(m) (modelist[m].modetype & LOCAL_CHARS) 308 #define MODE_LOCAL_ECHO(m) (modelist[m].modetype & LOCAL_ECHO) 309 #define MODE_COMMAND_LINE(m) (modelist[m].modetype & COMMAND_LINE) 310 311 #define LOCAL_CHARS 0x01 /* Characters processed locally */ 312 #define LINE 0x02 /* Line-by-line mode of operation */ 313 #define LOCAL_ECHO 0x04 /* Echoing locally */ 314 #define COMMAND_LINE 0x08 /* Command line mode */ 315 316 static struct { 317 char *modedescriptions; 318 char modetype; 319 } modelist[] = { 320 { "telnet command mode", COMMAND_LINE }, 321 { "character-at-a-time mode", 0 }, 322 { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 323 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 324 { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 325 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 326 { "3270 mode", 0 }, 327 }; 328 329 330 /* 331 * The following routines try to encapsulate what is system dependent 332 * (at least between 4.x and dos) which is used in telnet.c. 333 */ 334 335 #if defined(unix) 336 #include <sys/ioctl.h> 337 #include <sys/time.h> 338 #include <signal.h> 339 340 int 341 HaveInput; /* There is input available to scan */ 342 343 #if defined(TN3270) 344 static char tline[200]; 345 char *transcom = 0; /* transparent mode command (default: none) */ 346 #endif /* defined(TN3270) */ 347 348 static struct tchars otc = { 0 }, ntc = { 0 }; 349 static struct ltchars oltc = { 0 }, nltc = { 0 }; 350 static struct sgttyb ottyb = { 0 }, nttyb = { 0 }; 351 352 353 #define TerminalWrite(fd,buf,n) write(fd,buf,n) 354 #define TerminalRead(fd,buf,n) read(fd,buf,n) 355 356 /* 357 * 358 */ 359 360 static int 361 TerminalAutoFlush() /* unix */ 362 { 363 #if defined(LNOFLSH) 364 ioctl(0, TIOCLGET, (char *)&autoflush); 365 return !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */ 366 #else /* LNOFLSH */ 367 return 1; 368 #endif /* LNOFLSH */ 369 } 370 371 /* 372 * TerminalSpecialChars() 373 * 374 * Look at an input character to see if it is a special character 375 * and decide what to do. 376 * 377 * Output: 378 * 379 * 0 Don't add this character. 380 * 1 Do add this character 381 */ 382 383 int 384 TerminalSpecialChars(c) /* unix */ 385 int c; 386 { 387 void doflush(), intp(), sendbrk(); 388 389 if (c == ntc.t_intrc) { 390 intp(); 391 return 0; 392 } else if (c == ntc.t_quitc) { 393 sendbrk(); 394 return 0; 395 } else if (c == nltc.t_flushc) { 396 NET2ADD(IAC, AO); 397 if (autoflush) { 398 doflush(); 399 } 400 return 0; 401 } else if (!MODE_LOCAL_CHARS(globalmode)) { 402 if (c == nttyb.sg_kill) { 403 NET2ADD(IAC, EL); 404 return 0; 405 } else if (c == nttyb.sg_erase) { 406 NET2ADD(IAC, EC); 407 return 0; 408 } 409 } 410 return 1; 411 } 412 413 414 /* 415 * Flush output to the terminal 416 */ 417 418 static void 419 TerminalFlushOutput() /* unix */ 420 { 421 (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0); 422 } 423 424 static void 425 TerminalSaveState() /* unix */ 426 { 427 ioctl(0, TIOCGETP, (char *)&ottyb); 428 ioctl(0, TIOCGETC, (char *)&otc); 429 ioctl(0, TIOCGLTC, (char *)&oltc); 430 431 ntc = otc; 432 nltc = oltc; 433 nttyb = ottyb; 434 } 435 436 static void 437 TerminalRestoreState() /* unix */ 438 { 439 } 440 441 /* 442 * TerminalNewMode - set up terminal to a specific mode. 443 */ 444 445 446 static void 447 TerminalNewMode(f) /* unix */ 448 register int f; 449 { 450 static int prevmode = 0; 451 struct tchars *tc; 452 struct tchars tc3; 453 struct ltchars *ltc; 454 struct sgttyb sb; 455 int onoff; 456 int old; 457 struct tchars notc2; 458 struct ltchars noltc2; 459 static struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 460 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 461 462 globalmode = f; 463 if (prevmode == f) 464 return; 465 old = prevmode; 466 prevmode = f; 467 sb = nttyb; 468 469 switch (f) { 470 471 case 0: 472 onoff = 0; 473 tc = &otc; 474 ltc = &oltc; 475 break; 476 477 case 1: /* remote character processing, remote echo */ 478 case 2: /* remote character processing, local echo */ 479 case 6: /* 3270 mode - like 1, but with xon/xoff local */ 480 /* (might be nice to have "6" in telnet also...) */ 481 sb.sg_flags |= CBREAK; 482 if ((f == 1) || (f == 6)) { 483 sb.sg_flags &= ~(ECHO|CRMOD); 484 } else { 485 sb.sg_flags |= ECHO|CRMOD; 486 } 487 sb.sg_erase = sb.sg_kill = -1; 488 if (f == 6) { 489 tc = &tc3; 490 tc3 = notc; 491 /* get XON, XOFF characters */ 492 tc3.t_startc = otc.t_startc; 493 tc3.t_stopc = otc.t_stopc; 494 } else { 495 /* 496 * If user hasn't specified one way or the other, 497 * then default to not trapping signals. 498 */ 499 if (!donelclchars) { 500 localchars = 0; 501 } 502 if (localchars) { 503 notc2 = notc; 504 notc2.t_intrc = ntc.t_intrc; 505 notc2.t_quitc = ntc.t_quitc; 506 tc = ¬c2; 507 } else { 508 tc = ¬c; 509 } 510 } 511 ltc = &noltc; 512 onoff = 1; 513 break; 514 case 3: /* local character processing, remote echo */ 515 case 4: /* local character processing, local echo */ 516 case 5: /* local character processing, no echo */ 517 sb.sg_flags &= ~CBREAK; 518 sb.sg_flags |= CRMOD; 519 if (f == 4) 520 sb.sg_flags |= ECHO; 521 else 522 sb.sg_flags &= ~ECHO; 523 notc2 = ntc; 524 tc = ¬c2; 525 noltc2 = oltc; 526 ltc = &noltc2; 527 /* 528 * If user hasn't specified one way or the other, 529 * then default to trapping signals. 530 */ 531 if (!donelclchars) { 532 localchars = 1; 533 } 534 if (localchars) { 535 notc2.t_brkc = nltc.t_flushc; 536 noltc2.t_flushc = -1; 537 } else { 538 notc2.t_intrc = notc2.t_quitc = -1; 539 } 540 noltc2.t_suspc = escape; 541 noltc2.t_dsuspc = -1; 542 onoff = 1; 543 break; 544 545 default: 546 return; 547 } 548 ioctl(tin, TIOCSLTC, (char *)ltc); 549 ioctl(tin, TIOCSETC, (char *)tc); 550 ioctl(tin, TIOCSETP, (char *)&sb); 551 #if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) 552 ioctl(tin, FIONBIO, (char *)&onoff); 553 ioctl(tout, FIONBIO, (char *)&onoff); 554 #endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */ 555 #if defined(TN3270) 556 if (noasynch == 0) { 557 ioctl(tin, FIOASYNC, (char *)&onoff); 558 } 559 #endif /* defined(TN3270) */ 560 561 if (MODE_LINE(f)) { 562 void doescape(); 563 564 signal(SIGTSTP, doescape); 565 } else if (MODE_LINE(old)) { 566 signal(SIGTSTP, SIG_DFL); 567 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); 568 } 569 } 570 571 572 int 573 NetClose(net) 574 int net; 575 { 576 return close(net); 577 } 578 579 580 static void 581 NetNonblockingIO(fd, onoff) /* unix */ 582 int 583 fd, 584 onoff; 585 { 586 ioctl(net, FIONBIO, (char *)&onoff); 587 } 588 589 static void 590 NetSigIO(fd, onoff) /* unix */ 591 int 592 fd, 593 onoff; 594 { 595 ioctl(net, FIOASYNC, (char *)&onoff); /* hear about input */ 596 } 597 598 static void 599 NetSetPgrp(fd) /* unix */ 600 int fd; 601 { 602 int myPid; 603 604 myPid = getpid(); 605 #if defined(NOT43) 606 myPid = -myPid; 607 #endif /* defined(NOT43) */ 608 ioctl(net, SIOCSPGRP, (char *)&myPid); /* set my pid */ 609 } 610 611 612 #endif /* defined(unix) */ 613 614 #if defined(MSDOS) 615 #include <time.h> 616 #include <signal.h> 617 #include <process.h> 618 #include <fcntl.h> 619 #include <io.h> 620 #include <dos.h> 621 622 #if !defined(SO_OOBINLINE) 623 #define SO_OOBINLINE 624 #endif /* !defined(SO_OOBINLINE) */ 625 626 627 static char 628 termEofChar, 629 termEraseChar, 630 termFlushChar, 631 termIntChar, 632 termKillChar, 633 termLiteralNextChar, 634 termQuitChar; 635 636 637 /* 638 * MSDOS doesn't have anyway of deciding whether a full-edited line 639 * is ready to be read in, so we need to do character-by-character 640 * reads, and then do the editing in the program (in the case where 641 * we are supporting line-by-line mode). 642 * 643 * The following routines, which are internal to the MSDOS-specific 644 * code, accomplish this miracle. 645 */ 646 647 #define Hex(c) HEX[(c)&0xff] 648 649 static survivorSetup = 0; /* Do we have ^C hooks in? */ 650 651 static int 652 lineend = 0, /* There is a line terminator */ 653 ctrlCCount = 0; 654 655 static char linein[200], /* Where input line is assembled */ 656 *nextin = linein, /* Next input character */ 657 *nextout = linein; /* Next character to be consumed */ 658 659 #define consumechar() \ 660 if ((++nextout) >= nextin) { \ 661 nextout = nextin = linein; \ 662 lineend = 0; \ 663 } 664 665 #define characteratatime() (!MODE_LINE(globalmode)) /* one by one */ 666 667 668 /* 669 * killone() 670 * 671 * Erase the last character on the line. 672 */ 673 674 static void 675 killone() 676 { 677 if (lineend) { 678 return; /* ??? XXX */ 679 } 680 if (nextin == linein) { 681 return; /* Nothing to do */ 682 } 683 nextin--; 684 if (!(isspace(*nextin) || isprint(*nextin))) { 685 putchar('\b'); 686 putchar(' '); 687 putchar('\b'); 688 } 689 putchar('\b'); 690 putchar(' '); 691 putchar('\b'); 692 } 693 694 695 /* 696 * setlineend() 697 * 698 * Decide if it's time to send the current line up to the user 699 * process. 700 */ 701 702 static void 703 setlineend() 704 { 705 if (nextin == nextout) { 706 return; 707 } 708 if (characteratatime()) { 709 lineend = 1; 710 } else if (nextin >= (linein+sizeof linein)) { 711 lineend = 1; 712 } else { 713 int c = *(nextin-1); 714 if ((c == termIntChar) 715 || (c == termQuitChar) 716 || (c == termEofChar)) { 717 lineend = 1; 718 } else if (c == termFlushChar) { 719 lineend = 1; 720 } else if ((c == '\n') || (c == '\r')) { 721 lineend = 1; 722 } 723 } 724 /* Otherwise, leave it alone (reset by 'consumechar') */ 725 } 726 727 /* 728 * OK, what we do here is: 729 * 730 * o If we are echoing, then 731 * o Look for character erase, line kill characters 732 * o Echo the character (using '^' if a control character) 733 * o Put the character in the input buffer 734 * o Set 'lineend' as necessary 735 */ 736 737 static void 738 DoNextChar(c) 739 int c; /* Character to process */ 740 { 741 static char literalnextcharacter = 0; 742 743 if (nextin >= (linein+sizeof linein)) { 744 putchar('\7'); /* Ring bell */ 745 setlineend(); 746 return; 747 } 748 if (MODE_LOCAL_CHARS(globalmode)) { 749 /* Look for some special character */ 750 if (!literalnextcharacter) { 751 if (c == termEraseChar) { 752 killone(); 753 setlineend(); 754 return; 755 } else if (c == termKillChar) { 756 while (nextin != linein) { 757 killone(); 758 } 759 setlineend(); 760 return; 761 } else if (c == termLiteralNextChar) { 762 literalnextcharacter = 1; 763 return; 764 } 765 } 766 767 if (MODE_LOCAL_ECHO(globalmode)) { 768 if ((literalnextcharacter == 0) && ((c == '\r') || (c == '\n'))) { 769 putchar('\r'); 770 putchar('\n'); 771 c = '\n'; 772 } else if (!isprint(c) && !isspace(c)) { 773 putchar('^'); 774 putchar(c^0x40); 775 } else { 776 putchar(c); 777 } 778 } 779 literalnextcharacter = 0; 780 } 781 *nextin++ = c; 782 setlineend(); 783 } 784 785 static int 786 inputExists() 787 { 788 int input; 789 static state = 0; 790 791 while (ctrlCCount) { 792 DoNextChar(0x03); 793 ctrlCCount--; 794 } 795 if (lineend) { 796 return 1; 797 } 798 #if 1 /* For BIOS variety of calls */ 799 if (kbhit() == 0) { 800 return lineend; 801 } 802 input = getch(); /* MSC - get console character */ 803 if ((input&0xff) == 0) { 804 DoNextChar(0x01); /* ^A */ 805 } else { 806 DoNextChar(input&0xff); 807 } 808 #else /* 0 */ 809 if ((input = dirconio()) == -1) { 810 return lineend; 811 } 812 if ((input&0xff) == 0) { 813 if ((input&0xff00) == 0x0300) { /* Null */ 814 DoNextChar(0); 815 } else { 816 DoNextChar(0x01); 817 if (input&0x8000) { 818 DoNextChar(0x01); 819 DoNextChar((input>>8)&0x7f); 820 } else { 821 DoNextChar((input>>8)&0xff); 822 } 823 } 824 } else { 825 DoNextChar(input&0xff); 826 } 827 #endif /* 0 */ 828 return lineend; 829 } 830 831 832 void 833 CtrlCInterrupt() 834 { 835 if (!MODE_COMMAND_LINE(globalmode)) { 836 ctrlCCount++; /* XXX */ 837 signal(SIGINT, CtrlCInterrupt); 838 } else { 839 closeallsockets(); 840 exit(1); 841 } 842 } 843 844 /* 845 * The MSDOS routines, called from elsewhere. 846 */ 847 848 849 static int 850 TerminalAutoFlush() /* MSDOS */ 851 { 852 return 1; 853 } 854 855 static int 856 TerminalCanRead() 857 { 858 return inputExists(); 859 } 860 861 862 /* 863 * Flush output to the terminal 864 */ 865 866 static void 867 TerminalFlushOutput() /* MSDOS */ 868 { 869 } 870 871 872 static void 873 TerminalNewMode(f) /* MSDOS */ 874 register int f; 875 { 876 union REGS inregs; 877 struct SREGS segregs; 878 static old_1b_offset = 0, old_1b_segment = 0; 879 880 globalmode = f; 881 if (MODE_COMMAND_LINE(f)) { 882 signal(SIGINT, SIG_DFL); 883 if (old_1b_segment|old_1b_offset) { 884 inregs.h.ah = 0x25; 885 inregs.h.al = 0x1b; 886 inregs.x.dx = old_1b_offset; 887 segregs.ds = old_1b_segment; 888 intdosx(&inregs, &inregs, &segregs); 889 old_1b_segment = old_1b_offset = 0; 890 } 891 if (setmode(fileno(stdout), O_TEXT) == -1) { 892 ExitPerror("setmode (text)", 1); 893 } 894 if (setmode(fileno(stdin), O_TEXT) == -1) { 895 ExitPerror("setmode (text)", 1); 896 } 897 } else { 898 signal(SIGINT, CtrlCInterrupt); 899 if ((old_1b_segment|old_1b_offset) == 0) { 900 extern void iret_subr(); 901 void (far *foo_subr)() = iret_subr; 902 903 inregs.h.ah = 0x35; 904 inregs.h.al = 0x1b; 905 intdosx(&inregs, &inregs, &segregs); 906 old_1b_segment = segregs.es; 907 old_1b_offset = inregs.x.bx; 908 inregs.h.ah = 0x25; 909 inregs.h.al = 0x1b; 910 inregs.x.dx = FP_OFF(foo_subr); 911 segregs.ds = FP_SEG(foo_subr); 912 intdosx(&inregs, &inregs, &segregs); 913 } 914 if (setmode(fileno(stdout), O_BINARY) == -1) { 915 ExitPerror("setmode (binary)", 1); 916 } 917 if (setmode(fileno(stdin), O_BINARY) == -1) { 918 ExitPerror("setmode (binary)", 1); 919 } 920 } 921 } 922 923 924 int 925 TerminalRead(fd, buffer, count) 926 int fd; 927 char *buffer; 928 int count; 929 { 930 int done = 0; 931 932 for (;;) { 933 while (inputExists() && (done < count)) { 934 *buffer++ = *nextout; 935 consumechar(); 936 done++; 937 } 938 if (done) { 939 return(done); 940 } else { 941 return 0; 942 } 943 } 944 } 945 946 947 static void 948 TerminalSaveState() /* MSDOS */ 949 { 950 } 951 952 int 953 TerminalSpecialChars(c) /* MSDOS */ 954 { 955 return 1; 956 } 957 958 959 static void 960 TerminalRestoreState() /* MSDOS */ 961 { 962 } 963 964 965 static int 966 TerminalWrite(fd, buffer, count) /* MSDOS */ 967 int fd; 968 char *buffer; 969 int count; 970 { 971 return fwrite(buffer, sizeof (char), count, stdout); 972 } 973 974 975 static int 976 NetClose(fd) 977 { 978 return closesocket(fd); 979 } 980 981 static void 982 NetNonblockingIO(fd, onoff) /* MSDOS */ 983 int 984 fd, 985 onoff; 986 { 987 if (SetSockOpt(net, SOL_SOCKET, SO_NONBLOCKING, onoff)) { 988 perror("setsockop (SO_NONBLOCKING) "); 989 ExitString(stderr, "exiting\n", 1); 990 } 991 } 992 993 static void 994 NetSigIO(fd) /* MSDOS */ 995 int fd; 996 { 997 } 998 999 static void 1000 NetSetPgrp(fd) /* MSDOS */ 1001 int fd; 1002 { 1003 } 1004 1005 1006 #endif /* defined(MSDOS) */ 1007 1008 /* 1009 * Initialize variables. 1010 */ 1011 1012 static void 1013 tninit() 1014 { 1015 #if defined(TN3270) 1016 Sent3270TerminalType = 0; 1017 Ifrontp = Ibackp = Ibuf; 1018 #endif /* defined(TN3270) */ 1019 1020 tfrontp = tbackp = ttyobuf; 1021 nfrontp = nbackp = netobuf; 1022 1023 /* Don't change telnetport */ 1024 SB_CLEAR(); 1025 ClearArray(hisopts); 1026 ClearArray(myopts); 1027 sbp = sibuf; 1028 tbp = tibuf; 1029 1030 connected = net = scc = tcc = In3270 = ISend = donebinarytoggle = 0; 1031 telnetport = 0; 1032 #if defined(unix) 1033 HaveInput = 0; 1034 #endif /* defined(unix) */ 1035 1036 SYNCHing = 0; 1037 1038 errno = 0; 1039 1040 flushline = 0; 1041 1042 /* Don't change NetTrace */ 1043 1044 escape = CONTROL(']'); 1045 echoc = CONTROL('E'); 1046 1047 flushline = 1; 1048 sp = getservbyname("telnet", "tcp"); 1049 if (sp == 0) { 1050 ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1); 1051 /*NOTREACHED*/ 1052 } 1053 1054 #if defined(TN3270) 1055 init_ctlr(); /* Initialize some things */ 1056 init_keyboard(); 1057 init_screen(); 1058 init_system(); 1059 #endif /* defined(TN3270) */ 1060 } 1061 1062 /* 1063 * Various utility routines. 1064 */ 1065 1066 static void 1067 makeargv() 1068 { 1069 register char *cp; 1070 register char **argp = margv; 1071 1072 margc = 0; 1073 cp = line; 1074 if (*cp == '!') { /* Special case shell escape */ 1075 *argp++ = "!"; /* No room in string to get this */ 1076 margc++; 1077 cp++; 1078 } 1079 while (*cp) { 1080 while (isspace(*cp)) 1081 cp++; 1082 if (*cp == '\0') 1083 break; 1084 *argp++ = cp; 1085 margc += 1; 1086 while (*cp != '\0' && !isspace(*cp)) 1087 cp++; 1088 if (*cp == '\0') 1089 break; 1090 *cp++ = '\0'; 1091 } 1092 *argp++ = 0; 1093 } 1094 1095 static char *ambiguous; /* special return value */ 1096 #define Ambiguous(t) ((t)&ambiguous) 1097 1098 1099 static char ** 1100 genget(name, table, next) 1101 char *name; /* name to match */ 1102 char **table; /* name entry in table */ 1103 char **(*next)(); /* routine to return next entry in table */ 1104 { 1105 register char *p, *q; 1106 register char **c, **found; 1107 register int nmatches, longest; 1108 1109 if (name == 0) { 1110 return 0; 1111 } 1112 longest = 0; 1113 nmatches = 0; 1114 found = 0; 1115 for (c = table; (p = *c) != 0; c = (*next)(c)) { 1116 for (q = name; 1117 (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++) 1118 if (*q == 0) /* exact match? */ 1119 return (c); 1120 if (!*q) { /* the name was a prefix */ 1121 if (q - name > longest) { 1122 longest = q - name; 1123 nmatches = 1; 1124 found = c; 1125 } else if (q - name == longest) 1126 nmatches++; 1127 } 1128 } 1129 if (nmatches > 1) 1130 return Ambiguous(char **); 1131 return (found); 1132 } 1133 1134 /* 1135 * Make a character string into a number. 1136 * 1137 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). 1138 */ 1139 1140 static 1141 special(s) 1142 register char *s; 1143 { 1144 register char c; 1145 char b; 1146 1147 switch (*s) { 1148 case '^': 1149 b = *++s; 1150 if (b == '?') { 1151 c = b | 0x40; /* DEL */ 1152 } else { 1153 c = b & 0x1f; 1154 } 1155 break; 1156 default: 1157 c = *s; 1158 break; 1159 } 1160 return c; 1161 } 1162 1163 /* 1164 * Construct a control character sequence 1165 * for a special character. 1166 */ 1167 static char * 1168 control(c) 1169 register int c; 1170 { 1171 static char buf[3]; 1172 1173 if (c == 0x7f) 1174 return ("^?"); 1175 if (c == '\377') { 1176 return "off"; 1177 } 1178 if (c >= 0x20) { 1179 buf[0] = c; 1180 buf[1] = 0; 1181 } else { 1182 buf[0] = '^'; 1183 buf[1] = '@'+c; 1184 buf[2] = 0; 1185 } 1186 return (buf); 1187 } 1188 1189 1190 /* 1191 * upcase() 1192 * 1193 * Upcase (in place) the argument. 1194 */ 1195 1196 static void 1197 upcase(argument) 1198 register char *argument; 1199 { 1200 register int c; 1201 1202 while ((c = *argument) != 0) { 1203 if (islower(c)) { 1204 *argument = toupper(c); 1205 } 1206 argument++; 1207 } 1208 } 1209 1210 /* 1211 * SetSockOpt() 1212 * 1213 * Compensate for differences in 4.2 and 4.3 systems. 1214 */ 1215 1216 static int 1217 SetSockOpt(fd, level, option, yesno) 1218 int 1219 fd, 1220 level, 1221 option, 1222 yesno; 1223 { 1224 #ifndef NOT43 1225 return setsockopt(fd, level, option, 1226 (char *)&yesno, sizeof yesno); 1227 #else /* NOT43 */ 1228 if (yesno == 0) { /* Can't do that in 4.2! */ 1229 fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n", 1230 option); 1231 return -1; 1232 } 1233 return setsockopt(fd, level, option, 0, 0); 1234 #endif /* NOT43 */ 1235 } 1236 1237 /* 1238 * The following are routines used to print out debugging information. 1239 */ 1240 1241 1242 static void 1243 Dump(direction, buffer, length) 1244 char direction; 1245 char *buffer; 1246 int length; 1247 { 1248 # define BYTES_PER_LINE 32 1249 # define min(x,y) ((x<y)? x:y) 1250 char *pThis; 1251 int offset; 1252 1253 offset = 0; 1254 1255 while (length) { 1256 /* print one line */ 1257 fprintf(NetTrace, "%c 0x%x\t", direction, offset); 1258 pThis = buffer; 1259 buffer = buffer+min(length, BYTES_PER_LINE); 1260 while (pThis < buffer) { 1261 fprintf(NetTrace, "%.2x", (*pThis)&0xff); 1262 pThis++; 1263 } 1264 fprintf(NetTrace, "\n"); 1265 length -= BYTES_PER_LINE; 1266 offset += BYTES_PER_LINE; 1267 if (length < 0) { 1268 return; 1269 } 1270 /* find next unique line */ 1271 } 1272 } 1273 1274 1275 /*VARARGS*/ 1276 static void 1277 printoption(direction, fmt, option, what) 1278 char *direction, *fmt; 1279 int option, what; 1280 { 1281 if (!showoptions) 1282 return; 1283 fprintf(NetTrace, "%s ", direction+1); 1284 if (fmt == doopt) 1285 fmt = "do"; 1286 else if (fmt == dont) 1287 fmt = "dont"; 1288 else if (fmt == will) 1289 fmt = "will"; 1290 else if (fmt == wont) 1291 fmt = "wont"; 1292 else 1293 fmt = "???"; 1294 if (option < (sizeof telopts/sizeof telopts[0])) 1295 fprintf(NetTrace, "%s %s", fmt, telopts[option]); 1296 else 1297 fprintf(NetTrace, "%s %d", fmt, option); 1298 if (*direction == '<') { 1299 fprintf(NetTrace, "\r\n"); 1300 return; 1301 } 1302 fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply"); 1303 } 1304 1305 static void 1306 printsub(direction, pointer, length) 1307 char *direction, /* "<" or ">" */ 1308 *pointer; /* where suboption data sits */ 1309 int length; /* length of suboption data */ 1310 { 1311 if (showoptions) { 1312 fprintf(NetTrace, "%s suboption ", 1313 (direction[0] == '<')? "Received":"Sent"); 1314 switch (pointer[0]) { 1315 case TELOPT_TTYPE: 1316 fprintf(NetTrace, "Terminal type "); 1317 switch (pointer[1]) { 1318 case TELQUAL_IS: 1319 { 1320 char tmpbuf[sizeof subbuffer]; 1321 int minlen = min(length, sizeof tmpbuf); 1322 1323 memcpy(tmpbuf, pointer+2, minlen); 1324 tmpbuf[minlen-1] = 0; 1325 fprintf(NetTrace, "is %s.\n", tmpbuf); 1326 } 1327 break; 1328 case TELQUAL_SEND: 1329 fprintf(NetTrace, "- request to send.\n"); 1330 break; 1331 default: 1332 fprintf(NetTrace, 1333 "- unknown qualifier %d (0x%x).\n", pointer[1]); 1334 } 1335 break; 1336 default: 1337 fprintf(NetTrace, "Unknown option %d (0x%x)\n", 1338 pointer[0], pointer[0]); 1339 } 1340 } 1341 } 1342 1343 /* 1344 * Check to see if any out-of-band data exists on a socket (for 1345 * Telnet "synch" processing). 1346 */ 1347 1348 static int 1349 stilloob(s) 1350 int s; /* socket number */ 1351 { 1352 static struct timeval timeout = { 0 }; 1353 fd_set excepts; 1354 int value; 1355 1356 do { 1357 FD_ZERO(&excepts); 1358 FD_SET(s, &excepts); 1359 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 1360 } while ((value == -1) && (errno == EINTR)); 1361 1362 if (value < 0) { 1363 perror("select"); 1364 quit(); 1365 } 1366 if (FD_ISSET(s, &excepts)) { 1367 return 1; 1368 } else { 1369 return 0; 1370 } 1371 } 1372 1373 1374 /* 1375 * netflush 1376 * Send as much data as possible to the network, 1377 * handling requests for urgent data. 1378 * 1379 * The return value indicates whether we did any 1380 * useful work. 1381 */ 1382 1383 1384 int 1385 netflush() 1386 { 1387 int n; 1388 1389 if ((n = nfrontp - nbackp) > 0) { 1390 if (!neturg) { 1391 n = send(net, nbackp, n, 0); /* normal write */ 1392 } else { 1393 n = neturg - nbackp; 1394 /* 1395 * In 4.2 (and 4.3) systems, there is some question about 1396 * what byte in a sendOOB operation is the "OOB" data. 1397 * To make ourselves compatible, we only send ONE byte 1398 * out of band, the one WE THINK should be OOB (though 1399 * we really have more the TCP philosophy of urgent data 1400 * rather than the Unix philosophy of OOB data). 1401 */ 1402 if (n > 1) { 1403 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 1404 } else { 1405 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 1406 } 1407 } 1408 } 1409 if (n < 0) { 1410 if (errno != ENOBUFS && errno != EWOULDBLOCK) { 1411 setcommandmode(); 1412 perror(hostname); 1413 NetClose(net); 1414 neturg = 0; 1415 longjmp(peerdied, -1); 1416 /*NOTREACHED*/ 1417 } 1418 n = 0; 1419 } 1420 if (netdata && n) { 1421 Dump('>', nbackp, n); 1422 } 1423 nbackp += n; 1424 if (nbackp >= neturg) { 1425 neturg = 0; 1426 } 1427 if (nbackp == nfrontp) { 1428 nbackp = nfrontp = netobuf; 1429 } 1430 return n > 0; 1431 } 1432 1433 /* 1434 * nextitem() 1435 * 1436 * Return the address of the next "item" in the TELNET data 1437 * stream. This will be the address of the next character if 1438 * the current address is a user data character, or it will 1439 * be the address of the character following the TELNET command 1440 * if the current address is a TELNET IAC ("I Am a Command") 1441 * character. 1442 */ 1443 1444 static char * 1445 nextitem(current) 1446 char *current; 1447 { 1448 if ((*current&0xff) != IAC) { 1449 return current+1; 1450 } 1451 switch (*(current+1)&0xff) { 1452 case DO: 1453 case DONT: 1454 case WILL: 1455 case WONT: 1456 return current+3; 1457 case SB: /* loop forever looking for the SE */ 1458 { 1459 register char *look = current+2; 1460 1461 for (;;) { 1462 if ((*look++&0xff) == IAC) { 1463 if ((*look++&0xff) == SE) { 1464 return look; 1465 } 1466 } 1467 } 1468 } 1469 default: 1470 return current+2; 1471 } 1472 } 1473 /* 1474 * netclear() 1475 * 1476 * We are about to do a TELNET SYNCH operation. Clear 1477 * the path to the network. 1478 * 1479 * Things are a bit tricky since we may have sent the first 1480 * byte or so of a previous TELNET command into the network. 1481 * So, we have to scan the network buffer from the beginning 1482 * until we are up to where we want to be. 1483 * 1484 * A side effect of what we do, just to keep things 1485 * simple, is to clear the urgent data pointer. The principal 1486 * caller should be setting the urgent data pointer AFTER calling 1487 * us in any case. 1488 */ 1489 1490 static void 1491 netclear() 1492 { 1493 register char *thisitem, *next; 1494 char *good; 1495 #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 1496 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 1497 1498 thisitem = netobuf; 1499 1500 while ((next = nextitem(thisitem)) <= nbackp) { 1501 thisitem = next; 1502 } 1503 1504 /* Now, thisitem is first before/at boundary. */ 1505 1506 good = netobuf; /* where the good bytes go */ 1507 1508 while (nfrontp > thisitem) { 1509 if (wewant(thisitem)) { 1510 int length; 1511 1512 next = thisitem; 1513 do { 1514 next = nextitem(next); 1515 } while (wewant(next) && (nfrontp > next)); 1516 length = next-thisitem; 1517 memcpy(good, thisitem, length); 1518 good += length; 1519 thisitem = next; 1520 } else { 1521 thisitem = nextitem(thisitem); 1522 } 1523 } 1524 1525 nbackp = netobuf; 1526 nfrontp = good; /* next byte to be sent */ 1527 neturg = 0; 1528 } 1529 1530 /* 1531 * These routines add various telnet commands to the data stream. 1532 */ 1533 1534 #if defined(NOT43) 1535 static int 1536 #else /* defined(NOT43) */ 1537 static void 1538 #endif /* defined(NOT43) */ 1539 dosynch() 1540 { 1541 netclear(); /* clear the path to the network */ 1542 NET2ADD(IAC, DM); 1543 neturg = NETLOC()-1; /* Some systems are off by one XXX */ 1544 1545 #if defined(NOT43) 1546 return 0; 1547 #endif /* defined(NOT43) */ 1548 } 1549 1550 static void 1551 doflush() 1552 { 1553 NET2ADD(IAC, DO); 1554 NETADD(TELOPT_TM); 1555 flushline = 1; 1556 flushout = 1; 1557 ttyflush(); 1558 /* do printoption AFTER flush, otherwise the output gets tossed... */ 1559 printoption("<SENT", doopt, TELOPT_TM, 0); 1560 } 1561 1562 static void 1563 intp() 1564 { 1565 NET2ADD(IAC, IP); 1566 if (autoflush) { 1567 doflush(); 1568 } 1569 if (autosynch) { 1570 dosynch(); 1571 } 1572 } 1573 1574 static void 1575 sendbrk() 1576 { 1577 NET2ADD(IAC, BREAK); 1578 if (autoflush) { 1579 doflush(); 1580 } 1581 if (autosynch) { 1582 dosynch(); 1583 } 1584 } 1585 1586 /* 1587 * Send as much data as possible to the terminal. 1588 * 1589 * The return value indicates whether we did any 1590 * useful work. 1591 */ 1592 1593 1594 static int 1595 ttyflush() 1596 { 1597 int n; 1598 1599 if ((n = tfrontp - tbackp) > 0) { 1600 if (!(SYNCHing||flushout)) { 1601 n = TerminalWrite(tout, tbackp, n); 1602 } else { 1603 TerminalFlushOutput(); 1604 /* we leave 'n' alone! */ 1605 } 1606 } 1607 if (n >= 0) { 1608 tbackp += n; 1609 if (tbackp == tfrontp) { 1610 tbackp = tfrontp = ttyobuf; 1611 } 1612 } 1613 return n > 0; 1614 } 1615 1616 #if defined(TN3270) 1617 1618 #if defined(unix) 1619 static void 1620 inputAvailable() 1621 { 1622 HaveInput = 1; 1623 } 1624 #endif /* defined(unix) */ 1625 1626 void 1627 outputPurge() 1628 { 1629 int tmp = flushout; 1630 1631 flushout = 1; 1632 1633 ttyflush(); 1634 1635 flushout = tmp; 1636 } 1637 1638 #endif /* defined(TN3270) */ 1639 1640 #if defined(unix) 1641 /* 1642 * Various signal handling routines. 1643 */ 1644 1645 static void 1646 deadpeer() 1647 { 1648 setcommandmode(); 1649 longjmp(peerdied, -1); 1650 } 1651 1652 static void 1653 intr() 1654 { 1655 if (localchars) { 1656 intp(); 1657 return; 1658 } 1659 setcommandmode(); 1660 longjmp(toplevel, -1); 1661 } 1662 1663 static void 1664 intr2() 1665 { 1666 if (localchars) { 1667 sendbrk(); 1668 return; 1669 } 1670 } 1671 1672 static void 1673 doescape() 1674 { 1675 command(0); 1676 } 1677 #endif /* defined(unix) */ 1678 1679 /* 1680 * These routines decides on what the mode should be (based on the values 1681 * of various global variables). 1682 */ 1683 1684 1685 static 1686 getconnmode() 1687 { 1688 static char newmode[16] = 1689 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 }; 1690 int modeindex = 0; 1691 1692 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) { 1693 modeindex += 1; 1694 } 1695 if (hisopts[TELOPT_ECHO]) { 1696 modeindex += 2; 1697 } 1698 if (hisopts[TELOPT_SGA]) { 1699 modeindex += 4; 1700 } 1701 if (In3270) { 1702 modeindex += 8; 1703 } 1704 return newmode[modeindex]; 1705 } 1706 1707 void 1708 setconnmode() 1709 { 1710 TerminalNewMode(getconnmode()); 1711 } 1712 1713 1714 void 1715 setcommandmode() 1716 { 1717 TerminalNewMode(0); 1718 } 1719 1720 static void 1721 willoption(option, reply) 1722 int option, reply; 1723 { 1724 char *fmt; 1725 1726 switch (option) { 1727 1728 case TELOPT_ECHO: 1729 # if defined(TN3270) 1730 /* 1731 * The following is a pain in the rear-end. 1732 * Various IBM servers (some versions of Wiscnet, 1733 * possibly Fibronics/Spartacus, and who knows who 1734 * else) will NOT allow us to send "DO SGA" too early 1735 * in the setup proceedings. On the other hand, 1736 * 4.2 servers (telnetd) won't set SGA correctly. 1737 * So, we are stuck. Empirically (but, based on 1738 * a VERY small sample), the IBM servers don't send 1739 * out anything about ECHO, so we postpone our sending 1740 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 1741 * DO send). 1742 */ 1743 { 1744 if (askedSGA == 0) { 1745 askedSGA = 1; 1746 if (!hisopts[TELOPT_SGA]) { 1747 willoption(TELOPT_SGA, 0); 1748 } 1749 } 1750 } 1751 /* Fall through */ 1752 case TELOPT_EOR: 1753 case TELOPT_BINARY: 1754 #endif /* defined(TN3270) */ 1755 case TELOPT_SGA: 1756 settimer(modenegotiated); 1757 hisopts[option] = 1; 1758 fmt = doopt; 1759 setconnmode(); /* possibly set new tty mode */ 1760 break; 1761 1762 case TELOPT_TM: 1763 return; /* Never reply to TM will's/wont's */ 1764 1765 default: 1766 fmt = dont; 1767 break; 1768 } 1769 sprintf(nfrontp, fmt, option); 1770 nfrontp += sizeof (dont) - 2; 1771 if (reply) 1772 printoption(">SENT", fmt, option, reply); 1773 else 1774 printoption("<SENT", fmt, option, reply); 1775 } 1776 1777 static void 1778 wontoption(option, reply) 1779 int option, reply; 1780 { 1781 char *fmt; 1782 1783 switch (option) { 1784 1785 case TELOPT_ECHO: 1786 case TELOPT_SGA: 1787 settimer(modenegotiated); 1788 hisopts[option] = 0; 1789 fmt = dont; 1790 setconnmode(); /* Set new tty mode */ 1791 break; 1792 1793 case TELOPT_TM: 1794 return; /* Never reply to TM will's/wont's */ 1795 1796 default: 1797 fmt = dont; 1798 } 1799 sprintf(nfrontp, fmt, option); 1800 nfrontp += sizeof (doopt) - 2; 1801 if (reply) 1802 printoption(">SENT", fmt, option, reply); 1803 else 1804 printoption("<SENT", fmt, option, reply); 1805 } 1806 1807 static void 1808 dooption(option) 1809 int option; 1810 { 1811 char *fmt; 1812 1813 switch (option) { 1814 1815 case TELOPT_TM: 1816 fmt = will; 1817 break; 1818 1819 # if defined(TN3270) 1820 case TELOPT_EOR: 1821 case TELOPT_BINARY: 1822 # endif /* defined(TN3270) */ 1823 case TELOPT_TTYPE: /* terminal type option */ 1824 case TELOPT_SGA: /* no big deal */ 1825 fmt = will; 1826 myopts[option] = 1; 1827 break; 1828 1829 case TELOPT_ECHO: /* We're never going to echo... */ 1830 default: 1831 fmt = wont; 1832 break; 1833 } 1834 sprintf(nfrontp, fmt, option); 1835 nfrontp += sizeof (doopt) - 2; 1836 printoption(">SENT", fmt, option, 0); 1837 } 1838 1839 /* 1840 * suboption() 1841 * 1842 * Look at the sub-option buffer, and try to be helpful to the other 1843 * side. 1844 * 1845 * Currently we recognize: 1846 * 1847 * Terminal type, send request. 1848 */ 1849 1850 static void 1851 suboption() 1852 { 1853 printsub("<", subbuffer, subend-subbuffer+1); 1854 switch (subbuffer[0]&0xff) { 1855 case TELOPT_TTYPE: 1856 if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 1857 ; 1858 } else { 1859 char *name; 1860 char namebuf[41]; 1861 extern char *getenv(); 1862 int len; 1863 1864 #if defined(TN3270) 1865 /* 1866 * Try to send a 3270 type terminal name. Decide which one based 1867 * on the format of our screen, and (in the future) color 1868 * capaiblities. 1869 */ 1870 #if defined(unix) 1871 if (initscr() != ERR) { /* Initialize curses to get line size */ 1872 MaxNumberLines = LINES; 1873 MaxNumberColumns = COLS; 1874 } 1875 #else /* defined(unix) */ 1876 InitTerminal(); 1877 #endif /* defined(unix) */ 1878 if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) { 1879 Sent3270TerminalType = 1; 1880 if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) { 1881 MaxNumberLines = 27; 1882 MaxNumberColumns = 132; 1883 sb_terminal[SBTERMMODEL] = '5'; 1884 } else if (MaxNumberLines >= 43) { 1885 MaxNumberLines = 43; 1886 MaxNumberColumns = 80; 1887 sb_terminal[SBTERMMODEL] = '4'; 1888 } else if (MaxNumberLines >= 32) { 1889 MaxNumberLines = 32; 1890 MaxNumberColumns = 80; 1891 sb_terminal[SBTERMMODEL] = '3'; 1892 } else { 1893 MaxNumberLines = 24; 1894 MaxNumberColumns = 80; 1895 sb_terminal[SBTERMMODEL] = '2'; 1896 } 1897 NumberLines = 24; /* before we start out... */ 1898 NumberColumns = 80; 1899 ScreenSize = NumberLines*NumberColumns; 1900 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) { 1901 ExitString(stderr, 1902 "Programming error: MAXSCREENSIZE too small.\n", 1); 1903 /*NOTREACHED*/ 1904 } 1905 memcpy(nfrontp, sb_terminal, sizeof sb_terminal); 1906 printsub(">", nfrontp+2, sizeof sb_terminal-2); 1907 nfrontp += sizeof sb_terminal; 1908 return; 1909 } 1910 #endif /* defined(TN3270) */ 1911 1912 name = getenv("TERM"); 1913 if ((name == 0) || ((len = strlen(name)) > 40)) { 1914 name = "UNKNOWN"; 1915 } 1916 if ((len + 4+2) < NETROOM()) { 1917 strcpy(namebuf, name); 1918 upcase(namebuf); 1919 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 1920 TELQUAL_IS, namebuf, IAC, SE); 1921 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); 1922 nfrontp += 4+strlen(namebuf)+2; 1923 } else { 1924 ExitString(stderr, "No room in buffer for terminal type.\n", 1925 1); 1926 /*NOTREACHED*/ 1927 } 1928 } 1929 1930 default: 1931 break; 1932 } 1933 } 1934 1935 #if defined(TN3270) 1936 static void 1937 SetIn3270() 1938 { 1939 if (Sent3270TerminalType && myopts[TELOPT_BINARY] 1940 && hisopts[TELOPT_BINARY] && !donebinarytoggle) { 1941 if (!In3270) { 1942 In3270 = 1; 1943 Init3270(); /* Initialize 3270 functions */ 1944 /* initialize terminal key mapping */ 1945 InitTerminal(); /* Start terminal going */ 1946 setconnmode(); 1947 } 1948 } else { 1949 if (In3270) { 1950 StopScreen(1); 1951 In3270 = 0; 1952 Stop3270(); /* Tell 3270 we aren't here anymore */ 1953 setconnmode(); 1954 } 1955 } 1956 } 1957 #endif /* defined(TN3270) */ 1958 1959 1960 static void 1961 telrcv() 1962 { 1963 register int c; 1964 static int telrcv_state = TS_DATA; 1965 # if defined(TN3270) 1966 register int Scc; 1967 register char *Sbp; 1968 # endif /* defined(TN3270) */ 1969 1970 while ((scc > 0) && (TTYROOM() > 2)) { 1971 c = *sbp++ & 0xff, scc--; 1972 switch (telrcv_state) { 1973 1974 case TS_CR: 1975 telrcv_state = TS_DATA; 1976 if (c == '\0') { 1977 break; /* Ignore \0 after CR */ 1978 } else if (c == '\n') { 1979 if (hisopts[TELOPT_ECHO] && !crmod) { 1980 TTYADD(c); 1981 } 1982 break; 1983 } 1984 /* Else, fall through */ 1985 1986 case TS_DATA: 1987 if (c == IAC) { 1988 telrcv_state = TS_IAC; 1989 continue; 1990 } 1991 # if defined(TN3270) 1992 if (In3270) { 1993 *Ifrontp++ = c; 1994 Sbp = sbp; 1995 Scc = scc; 1996 while (Scc > 0) { 1997 c = *Sbp++ & 0377, Scc--; 1998 if (c == IAC) { 1999 telrcv_state = TS_IAC; 2000 break; 2001 } 2002 *Ifrontp++ = c; 2003 } 2004 sbp = Sbp; 2005 scc = Scc; 2006 } else 2007 # endif /* defined(TN3270) */ 2008 /* 2009 * The 'crmod' hack (see following) is needed 2010 * since we can't * set CRMOD on output only. 2011 * Machines like MULTICS like to send \r without 2012 * \n; since we must turn off CRMOD to get proper 2013 * input, the mapping is done here (sigh). 2014 */ 2015 if (c == '\r') { 2016 if (scc > 0) { 2017 c = *sbp&0xff; 2018 if (c == 0) { 2019 sbp++, scc--; 2020 /* a "true" CR */ 2021 TTYADD('\r'); 2022 } else if (!hisopts[TELOPT_ECHO] && 2023 (c == '\n')) { 2024 sbp++, scc--; 2025 TTYADD('\n'); 2026 } else { 2027 TTYADD('\r'); 2028 if (crmod) { 2029 TTYADD('\n'); 2030 } 2031 } 2032 } else { 2033 telrcv_state = TS_CR; 2034 TTYADD('\r'); 2035 if (crmod) { 2036 TTYADD('\n'); 2037 } 2038 } 2039 } else { 2040 TTYADD(c); 2041 } 2042 continue; 2043 2044 case TS_IAC: 2045 switch (c) { 2046 2047 case WILL: 2048 telrcv_state = TS_WILL; 2049 continue; 2050 2051 case WONT: 2052 telrcv_state = TS_WONT; 2053 continue; 2054 2055 case DO: 2056 telrcv_state = TS_DO; 2057 continue; 2058 2059 case DONT: 2060 telrcv_state = TS_DONT; 2061 continue; 2062 2063 case DM: 2064 /* 2065 * We may have missed an urgent notification, 2066 * so make sure we flush whatever is in the 2067 * buffer currently. 2068 */ 2069 SYNCHing = 1; 2070 ttyflush(); 2071 SYNCHing = stilloob(net); 2072 settimer(gotDM); 2073 break; 2074 2075 case NOP: 2076 case GA: 2077 break; 2078 2079 case SB: 2080 SB_CLEAR(); 2081 telrcv_state = TS_SB; 2082 continue; 2083 2084 # if defined(TN3270) 2085 case EOR: 2086 if (In3270) { 2087 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 2088 if (Ibackp == Ifrontp) { 2089 Ibackp = Ifrontp = Ibuf; 2090 ISend = 0; /* should have been! */ 2091 } else { 2092 ISend = 1; 2093 } 2094 } 2095 break; 2096 # endif /* defined(TN3270) */ 2097 2098 case IAC: 2099 # if !defined(TN3270) 2100 TTYADD(IAC); 2101 # else /* !defined(TN3270) */ 2102 if (In3270) { 2103 *Ifrontp++ = IAC; 2104 } else { 2105 TTYADD(IAC); 2106 } 2107 # endif /* !defined(TN3270) */ 2108 break; 2109 2110 default: 2111 break; 2112 } 2113 telrcv_state = TS_DATA; 2114 continue; 2115 2116 case TS_WILL: 2117 printoption(">RCVD", will, c, !hisopts[c]); 2118 if (c == TELOPT_TM) { 2119 if (flushout) { 2120 flushout = 0; 2121 } 2122 } else if (!hisopts[c]) { 2123 willoption(c, 1); 2124 } 2125 SetIn3270(); 2126 telrcv_state = TS_DATA; 2127 continue; 2128 2129 case TS_WONT: 2130 printoption(">RCVD", wont, c, hisopts[c]); 2131 if (c == TELOPT_TM) { 2132 if (flushout) { 2133 flushout = 0; 2134 } 2135 } else if (hisopts[c]) { 2136 wontoption(c, 1); 2137 } 2138 SetIn3270(); 2139 telrcv_state = TS_DATA; 2140 continue; 2141 2142 case TS_DO: 2143 printoption(">RCVD", doopt, c, !myopts[c]); 2144 if (!myopts[c]) 2145 dooption(c); 2146 SetIn3270(); 2147 telrcv_state = TS_DATA; 2148 continue; 2149 2150 case TS_DONT: 2151 printoption(">RCVD", dont, c, myopts[c]); 2152 if (myopts[c]) { 2153 myopts[c] = 0; 2154 sprintf(nfrontp, wont, c); 2155 nfrontp += sizeof (wont) - 2; 2156 flushline = 1; 2157 setconnmode(); /* set new tty mode (maybe) */ 2158 printoption(">SENT", wont, c, 0); 2159 } 2160 SetIn3270(); 2161 telrcv_state = TS_DATA; 2162 continue; 2163 2164 case TS_SB: 2165 if (c == IAC) { 2166 telrcv_state = TS_SE; 2167 } else { 2168 SB_ACCUM(c); 2169 } 2170 continue; 2171 2172 case TS_SE: 2173 if (c != SE) { 2174 if (c != IAC) { 2175 SB_ACCUM(IAC); 2176 } 2177 SB_ACCUM(c); 2178 telrcv_state = TS_SB; 2179 } else { 2180 SB_TERM(); 2181 suboption(); /* handle sub-option */ 2182 SetIn3270(); 2183 telrcv_state = TS_DATA; 2184 } 2185 } 2186 } 2187 } 2188 2189 #if defined(TN3270) 2190 2191 /* 2192 * The following routines are places where the various tn3270 2193 * routines make calls into telnet.c. 2194 */ 2195 2196 /* TtyChars() - returns the number of characters in the TTY buffer */ 2197 TtyChars() 2198 { 2199 return(tfrontp-tbackp); 2200 } 2201 2202 /* 2203 * DataToNetwork - queue up some data to go to network. If "done" is set, 2204 * then when last byte is queued, we add on an IAC EOR sequence (so, 2205 * don't call us with "done" until you want that done...) 2206 * 2207 * We actually do send all the data to the network buffer, since our 2208 * only client needs for us to do that. 2209 */ 2210 2211 int 2212 DataToNetwork(buffer, count, done) 2213 register char *buffer; /* where the data is */ 2214 register int count; /* how much to send */ 2215 int done; /* is this the last of a logical block */ 2216 { 2217 register int c; 2218 int origCount; 2219 fd_set o; 2220 2221 origCount = count; 2222 FD_ZERO(&o); 2223 2224 while (count) { 2225 if ((netobuf+sizeof netobuf - nfrontp) < 6) { 2226 netflush(); 2227 while ((netobuf+sizeof netobuf - nfrontp) < 6) { 2228 FD_SET(net, &o); 2229 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0, 2230 (struct timeval *) 0); 2231 netflush(); 2232 } 2233 } 2234 c = *buffer++; 2235 count--; 2236 if (c == IAC) { 2237 *nfrontp++ = IAC; 2238 *nfrontp++ = IAC; 2239 } else { 2240 *nfrontp++ = c; 2241 } 2242 } 2243 2244 if (done && !count) { 2245 *nfrontp++ = IAC; 2246 *nfrontp++ = EOR; 2247 netflush(); /* try to move along as quickly as ... */ 2248 } 2249 return(origCount - count); 2250 } 2251 2252 /* DataToTerminal - queue up some data to go to terminal. */ 2253 2254 int 2255 DataToTerminal(buffer, count) 2256 register char *buffer; /* where the data is */ 2257 register int count; /* how much to send */ 2258 { 2259 int origCount; 2260 #if defined(unix) 2261 fd_set o; 2262 2263 FD_ZERO(&o); 2264 #endif /* defined(unix) */ 2265 origCount = count; 2266 2267 while (count) { 2268 if (tfrontp >= ttyobuf+sizeof ttyobuf) { 2269 ttyflush(); 2270 while (tfrontp >= ttyobuf+sizeof ttyobuf) { 2271 #if defined(unix) 2272 FD_SET(tout, &o); 2273 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, 2274 (struct timeval *) 0); 2275 #endif /* defined(unix) */ 2276 ttyflush(); 2277 } 2278 } 2279 *tfrontp++ = *buffer++; 2280 count--; 2281 } 2282 return(origCount - count); 2283 } 2284 2285 /* EmptyTerminal - called to make sure that the terminal buffer is empty. 2286 * Note that we consider the buffer to run all the 2287 * way to the kernel (thus the select). 2288 */ 2289 2290 void 2291 EmptyTerminal() 2292 { 2293 #if defined(unix) 2294 fd_set o; 2295 2296 FD_ZERO(&o); 2297 #endif /* defined(unix) */ 2298 2299 if (tfrontp == tbackp) { 2300 #if defined(unix) 2301 FD_SET(tout, &o); 2302 (void) select(tout+1, (int *) 0, &o, (int *) 0, 2303 (struct timeval *) 0); /* wait for TTLOWAT */ 2304 #endif /* defined(unix) */ 2305 } else { 2306 while (tfrontp != tbackp) { 2307 ttyflush(); 2308 #if defined(unix) 2309 FD_SET(tout, &o); 2310 (void) select(tout+1, (int *) 0, &o, (int *) 0, 2311 (struct timeval *) 0); /* wait for TTLOWAT */ 2312 #endif /* defined(unix) */ 2313 } 2314 } 2315 } 2316 2317 /* 2318 * Push3270 - Try to send data along the 3270 output (to screen) direction. 2319 */ 2320 2321 static int 2322 Push3270() 2323 { 2324 int save = scc; 2325 2326 if (scc) { 2327 if (Ifrontp+scc > Ibuf+sizeof Ibuf) { 2328 if (Ibackp != Ibuf) { 2329 memcpy(Ibuf, Ibackp, Ifrontp-Ibackp); 2330 Ifrontp -= (Ibackp-Ibuf); 2331 Ibackp = Ibuf; 2332 } 2333 } 2334 if (Ifrontp+scc < Ibuf+sizeof Ibuf) { 2335 telrcv(); 2336 } 2337 } 2338 return save != scc; 2339 } 2340 2341 2342 /* 2343 * Finish3270 - get the last dregs of 3270 data out to the terminal 2344 * before quitting. 2345 */ 2346 2347 static void 2348 Finish3270() 2349 { 2350 while (Push3270() || !DoTerminalOutput()) { 2351 #if defined(unix) 2352 HaveInput = 0; 2353 #endif /* defined(unix) */ 2354 ; 2355 } 2356 } 2357 2358 2359 2360 /* StringToTerminal - output a null terminated string to the terminal */ 2361 2362 void 2363 StringToTerminal(s) 2364 char *s; 2365 { 2366 int count; 2367 2368 count = strlen(s); 2369 if (count) { 2370 (void) DataToTerminal(s, count); /* we know it always goes... */ 2371 } 2372 } 2373 2374 2375 #if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) 2376 /* _putchar - output a single character to the terminal. This name is so that 2377 * curses(3x) can call us to send out data. 2378 */ 2379 2380 void 2381 _putchar(c) 2382 char c; 2383 { 2384 if (tfrontp >= ttyobuf+sizeof ttyobuf) { 2385 (void) DataToTerminal(&c, 1); 2386 } else { 2387 *tfrontp++ = c; /* optimize if possible. */ 2388 } 2389 } 2390 #endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */ 2391 2392 static void 2393 SetForExit() 2394 { 2395 setconnmode(); 2396 if (In3270) { 2397 Finish3270(); 2398 } 2399 setcommandmode(); 2400 fflush(stdout); 2401 fflush(stderr); 2402 if (In3270) { 2403 StopScreen(1); 2404 } 2405 setconnmode(); 2406 setcommandmode(); 2407 } 2408 2409 static void 2410 Exit(returnCode) 2411 int returnCode; 2412 { 2413 SetForExit(); 2414 exit(returnCode); 2415 } 2416 2417 void 2418 ExitString(file, string, returnCode) 2419 FILE *file; 2420 char *string; 2421 int returnCode; 2422 { 2423 SetForExit(); 2424 fwrite(string, 1, strlen(string), file); 2425 exit(returnCode); 2426 } 2427 2428 void 2429 ExitPerror(string, returnCode) 2430 char *string; 2431 int returnCode; 2432 { 2433 SetForExit(); 2434 perror(string); 2435 exit(returnCode); 2436 } 2437 2438 #endif /* defined(TN3270) */ 2439 2440 /* 2441 * Scheduler() 2442 * 2443 * Try to do something. 2444 * 2445 * If we do something useful, return 1; else return 0. 2446 * 2447 */ 2448 2449 2450 int 2451 Scheduler(block) 2452 int block; /* should we block in the select ? */ 2453 { 2454 register int c; 2455 /* One wants to be a bit careful about setting returnValue 2456 * to one, since a one implies we did some useful work, 2457 * and therefore probably won't be called to block next 2458 * time (TN3270 mode only). 2459 */ 2460 int returnValue = 0; 2461 static struct timeval TimeValue = { 0 }; 2462 2463 if (scc < 0 && tcc < 0) { 2464 return -1; 2465 } 2466 2467 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) { 2468 FD_SET(net, &obits); 2469 } 2470 #if !defined(MSDOS) 2471 if (TTYBYTES()) { 2472 FD_SET(tout, &obits); 2473 } 2474 if ((tcc == 0) && NETROOM() && (shell_active == 0)) { 2475 FD_SET(tin, &ibits); 2476 } 2477 #endif /* !defined(MSDOS) */ 2478 # if !defined(TN3270) 2479 if (TTYROOM()) { 2480 FD_SET(net, &ibits); 2481 } 2482 # else /* !defined(TN3270) */ 2483 if (!ISend && TTYROOM()) { 2484 FD_SET(net, &ibits); 2485 } 2486 # endif /* !defined(TN3270) */ 2487 if (!SYNCHing) { 2488 FD_SET(net, &xbits); 2489 } 2490 # if defined(TN3270) && defined(unix) 2491 if (HaveInput) { 2492 HaveInput = 0; 2493 signal(SIGIO, inputAvailable); 2494 } 2495 #endif /* defined(TN3270) && defined(unix) */ 2496 if ((c = select(16, &ibits, &obits, &xbits, 2497 block? (struct timeval *)0 : &TimeValue)) < 0) { 2498 if (c == -1) { 2499 /* 2500 * we can get EINTR if we are in line mode, 2501 * and the user does an escape (TSTP), or 2502 * some other signal generator. 2503 */ 2504 if (errno == EINTR) { 2505 return 0; 2506 } 2507 # if defined(TN3270) 2508 /* 2509 * we can get EBADF if we were in transparent 2510 * mode, and the transcom process died. 2511 */ 2512 if (errno == EBADF) { 2513 /* 2514 * zero the bits (even though kernel does it) 2515 * to make sure we are selecting on the right 2516 * ones. 2517 */ 2518 FD_ZERO(&ibits); 2519 FD_ZERO(&obits); 2520 FD_ZERO(&xbits); 2521 return 0; 2522 } 2523 # endif /* defined(TN3270) */ 2524 /* I don't like this, does it ever happen? */ 2525 printf("sleep(5) from telnet, after select\r\n"); 2526 #if defined(unix) 2527 sleep(5); 2528 #endif /* defined(unix) */ 2529 } 2530 return 0; 2531 } 2532 2533 /* 2534 * Any urgent data? 2535 */ 2536 if (FD_ISSET(net, &xbits)) { 2537 FD_CLR(net, &xbits); 2538 SYNCHing = 1; 2539 ttyflush(); /* flush already enqueued data */ 2540 } 2541 2542 /* 2543 * Something to read from the network... 2544 */ 2545 if (FD_ISSET(net, &ibits)) { 2546 int canread; 2547 2548 FD_CLR(net, &ibits); 2549 if (scc == 0) { 2550 sbp = sibuf; 2551 } 2552 canread = sibuf + sizeof sibuf - (sbp+scc); 2553 #if !defined(SO_OOBINLINE) 2554 /* 2555 * In 4.2 (and some early 4.3) systems, the 2556 * OOB indication and data handling in the kernel 2557 * is such that if two separate TCP Urgent requests 2558 * come in, one byte of TCP data will be overlaid. 2559 * This is fatal for Telnet, but we try to live 2560 * with it. 2561 * 2562 * In addition, in 4.2 (and...), a special protocol 2563 * is needed to pick up the TCP Urgent data in 2564 * the correct sequence. 2565 * 2566 * What we do is: if we think we are in urgent 2567 * mode, we look to see if we are "at the mark". 2568 * If we are, we do an OOB receive. If we run 2569 * this twice, we will do the OOB receive twice, 2570 * but the second will fail, since the second 2571 * time we were "at the mark", but there wasn't 2572 * any data there (the kernel doesn't reset 2573 * "at the mark" until we do a normal read). 2574 * Once we've read the OOB data, we go ahead 2575 * and do normal reads. 2576 * 2577 * There is also another problem, which is that 2578 * since the OOB byte we read doesn't put us 2579 * out of OOB state, and since that byte is most 2580 * likely the TELNET DM (data mark), we would 2581 * stay in the TELNET SYNCH (SYNCHing) state. 2582 * So, clocks to the rescue. If we've "just" 2583 * received a DM, then we test for the 2584 * presence of OOB data when the receive OOB 2585 * fails (and AFTER we did the normal mode read 2586 * to clear "at the mark"). 2587 */ 2588 if (SYNCHing) { 2589 int atmark; 2590 2591 ioctl(net, SIOCATMARK, (char *)&atmark); 2592 if (atmark) { 2593 c = recv(net, sbp+scc, canread, MSG_OOB); 2594 if ((c == -1) && (errno == EINVAL)) { 2595 c = recv(net, sbp+scc, canread, 0); 2596 if (clocks.didnetreceive < clocks.gotDM) { 2597 SYNCHing = stilloob(net); 2598 } 2599 } 2600 } else { 2601 c = recv(net, sbp+scc, canread, 0); 2602 } 2603 } else { 2604 c = recv(net, sbp+scc, canread, 0); 2605 } 2606 settimer(didnetreceive); 2607 #else /* !defined(SO_OOBINLINE) */ 2608 c = recv(net, sbp+scc, canread, 0); 2609 #endif /* !defined(SO_OOBINLINE) */ 2610 if (c < 0 && errno == EWOULDBLOCK) { 2611 c = 0; 2612 } else if (c <= 0) { 2613 return -1; 2614 } 2615 if (netdata) { 2616 Dump('<', sbp+scc, c); 2617 } 2618 scc += c; 2619 returnValue = 1; 2620 } 2621 2622 /* 2623 * Something to read from the tty... 2624 */ 2625 #if defined(MSDOS) 2626 if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead()) 2627 #else /* defined(MSDOS) */ 2628 if (FD_ISSET(tin, &ibits)) 2629 #endif /* defined(MSDOS) */ 2630 { 2631 FD_CLR(tin, &ibits); 2632 if (tcc == 0) { 2633 tbp = tibuf; /* nothing left, reset */ 2634 } 2635 c = TerminalRead(tin, tbp, tibuf+sizeof tibuf - tbp); 2636 if (c < 0 && errno == EWOULDBLOCK) { 2637 c = 0; 2638 } else { 2639 #if defined(unix) 2640 /* EOF detection for line mode!!!! */ 2641 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) { 2642 /* must be an EOF... */ 2643 *tbp = ntc.t_eofc; 2644 c = 1; 2645 } 2646 #endif /* defined(unix) */ 2647 if (c <= 0) { 2648 tcc = c; 2649 return -1; 2650 } 2651 } 2652 tcc += c; 2653 returnValue = 1; /* did something useful */ 2654 } 2655 2656 # if defined(TN3270) 2657 if (tcc > 0) { 2658 if (In3270) { 2659 c = DataFromTerminal(tbp, tcc); 2660 if (c) { 2661 returnValue = 1; 2662 } 2663 tcc -= c; 2664 tbp += c; 2665 } else { 2666 # endif /* defined(TN3270) */ 2667 returnValue = 1; 2668 while (tcc > 0) { 2669 register int sc; 2670 2671 if (NETROOM() < 2) { 2672 flushline = 1; 2673 break; 2674 } 2675 c = *tbp++ & 0xff, sc = strip(c), tcc--; 2676 if (sc == escape) { 2677 command(0); 2678 tcc = 0; 2679 flushline = 1; 2680 break; 2681 } else if (MODE_LINE(globalmode) && (sc == echoc)) { 2682 if (tcc > 0 && strip(*tbp) == echoc) { 2683 tbp++; 2684 tcc--; 2685 } else { 2686 dontlecho = !dontlecho; 2687 settimer(echotoggle); 2688 setconnmode(); 2689 tcc = 0; 2690 flushline = 1; 2691 break; 2692 } 2693 } 2694 if (localchars) { 2695 if (TerminalSpecialChars(sc) == 0) { 2696 break; 2697 } 2698 } 2699 switch (c) { 2700 case '\n': 2701 /* 2702 * If we are in CRMOD mode (\r ==> \n) 2703 * on our local machine, then probably 2704 * a newline (unix) is CRLF (TELNET). 2705 */ 2706 if (MODE_LOCAL_CHARS(globalmode)) { 2707 NETADD('\r'); 2708 } 2709 NETADD('\n'); 2710 flushline = 1; 2711 break; 2712 case '\r': 2713 NET2ADD('\r', '\0'); 2714 flushline = 1; 2715 break; 2716 case IAC: 2717 NET2ADD(IAC, IAC); 2718 break; 2719 default: 2720 NETADD(c); 2721 break; 2722 } 2723 } 2724 # if defined(TN3270) 2725 } 2726 } 2727 # endif /* defined(TN3270) */ 2728 2729 if ((!MODE_LINE(globalmode) || flushline) && 2730 FD_ISSET(net, &obits) && (NETBYTES() > 0)) { 2731 FD_CLR(net, &obits); 2732 returnValue = netflush(); 2733 } 2734 if (scc > 0) { 2735 # if !defined(TN3270) 2736 telrcv(); 2737 returnValue = 1; 2738 # else /* !defined(TN3270) */ 2739 returnValue = Push3270(); 2740 # endif /* !defined(TN3270) */ 2741 } 2742 #if defined(MSDOS) 2743 if (TTYBYTES()) 2744 #else /* defined(MSDOS) */ 2745 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) 2746 #endif /* defined(MSDOS) */ 2747 { 2748 FD_CLR(tout, &obits); 2749 returnValue = ttyflush(); 2750 } 2751 return returnValue; 2752 } 2753 2754 /* 2755 * Select from tty and network... 2756 */ 2757 static void 2758 telnet() 2759 { 2760 #if defined(MSDOS) 2761 #define SCHED_BLOCK 0 /* Don't block in MSDOS */ 2762 #else /* defined(MSDOS) */ 2763 #define SCHED_BLOCK 1 2764 #endif /* defined(MSDOS) */ 2765 2766 #if defined(TN3270) && defined(unix) 2767 int myPid; 2768 #endif /* defined(TN3270) */ 2769 2770 tout = fileno(stdout); 2771 tin = fileno(stdin); 2772 setconnmode(); 2773 scc = 0; 2774 tcc = 0; 2775 FD_ZERO(&ibits); 2776 FD_ZERO(&obits); 2777 FD_ZERO(&xbits); 2778 2779 NetNonblockingIO(net, 1); 2780 2781 #if defined(TN3270) 2782 if (noasynch == 0) { /* DBX can't handle! */ 2783 NetSigIO(net, 1); 2784 } 2785 NetSetPgrp(net); 2786 #endif /* defined(TN3270) */ 2787 2788 2789 #if defined(SO_OOBINLINE) && !defined(MSDOS) 2790 SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1); 2791 #endif /* defined(SO_OOBINLINE) && !defined(MSDOS) */ 2792 2793 # if !defined(TN3270) 2794 if (telnetport) { 2795 if (!hisopts[TELOPT_SGA]) { 2796 willoption(TELOPT_SGA, 0); 2797 } 2798 if (!myopts[TELOPT_TTYPE]) { 2799 dooption(TELOPT_TTYPE, 0); 2800 } 2801 } 2802 # endif /* !defined(TN3270) */ 2803 2804 # if !defined(TN3270) 2805 for (;;) { 2806 if (Scheduler(SCHED_BLOCK) == -1) { 2807 setcommandmode(); 2808 return; 2809 } 2810 } 2811 # else /* !defined(TN3270) */ 2812 for (;;) { 2813 int schedValue; 2814 2815 while (!In3270 && !shell_active) { 2816 if (Scheduler(SCHED_BLOCK) == -1) { 2817 setcommandmode(); 2818 return; 2819 } 2820 } 2821 2822 while ((schedValue = Scheduler(0)) != 0) { 2823 if (schedValue == -1) { 2824 setcommandmode(); 2825 return; 2826 } 2827 } 2828 /* If there is data waiting to go out to terminal, don't 2829 * schedule any more data for the terminal. 2830 */ 2831 if (tfrontp-tbackp) { 2832 schedValue = 1; 2833 } else { 2834 if (shell_active) { 2835 if (shell_continue() == 0) { 2836 ConnectScreen(); 2837 } 2838 } else if (In3270) { 2839 schedValue = DoTerminalOutput(); 2840 } 2841 } 2842 if (schedValue && (shell_active == 0)) { 2843 if (Scheduler(SCHED_BLOCK) == -1) { 2844 setcommandmode(); 2845 return; 2846 } 2847 } 2848 } 2849 # endif /* !defined(TN3270) */ 2850 } 2851 2852 /* 2853 * The following are data structures and routines for 2854 * the "send" command. 2855 * 2856 */ 2857 2858 struct sendlist { 2859 char *name; /* How user refers to it (case independent) */ 2860 int what; /* Character to be sent (<0 ==> special) */ 2861 char *help; /* Help information (0 ==> no help) */ 2862 #if defined(NOT43) 2863 int (*routine)(); /* Routine to perform (for special ops) */ 2864 #else /* defined(NOT43) */ 2865 void (*routine)(); /* Routine to perform (for special ops) */ 2866 #endif /* defined(NOT43) */ 2867 }; 2868 2869 #define SENDQUESTION -1 2870 #define SENDESCAPE -3 2871 2872 static struct sendlist Sendlist[] = { 2873 { "ao", AO, "Send Telnet Abort output" }, 2874 { "ayt", AYT, "Send Telnet 'Are You There'" }, 2875 { "brk", BREAK, "Send Telnet Break" }, 2876 { "ec", EC, "Send Telnet Erase Character" }, 2877 { "el", EL, "Send Telnet Erase Line" }, 2878 { "escape", SENDESCAPE, "Send current escape character" }, 2879 { "ga", GA, "Send Telnet 'Go Ahead' sequence" }, 2880 { "ip", IP, "Send Telnet Interrupt Process" }, 2881 { "nop", NOP, "Send Telnet 'No operation'" }, 2882 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch }, 2883 { "?", SENDQUESTION, "Display send options" }, 2884 { 0 } 2885 }; 2886 2887 static struct sendlist Sendlist2[] = { /* some synonyms */ 2888 { "break", BREAK, 0 }, 2889 2890 { "intp", IP, 0 }, 2891 { "interrupt", IP, 0 }, 2892 { "intr", IP, 0 }, 2893 2894 { "help", SENDQUESTION, 0 }, 2895 2896 { 0 } 2897 }; 2898 2899 static char ** 2900 getnextsend(name) 2901 char *name; 2902 { 2903 struct sendlist *c = (struct sendlist *) name; 2904 2905 return (char **) (c+1); 2906 } 2907 2908 static struct sendlist * 2909 getsend(name) 2910 char *name; 2911 { 2912 struct sendlist *sl; 2913 2914 if ((sl = (struct sendlist *) 2915 genget(name, (char **) Sendlist, getnextsend)) != 0) { 2916 return sl; 2917 } else { 2918 return (struct sendlist *) 2919 genget(name, (char **) Sendlist2, getnextsend); 2920 } 2921 } 2922 2923 static 2924 sendcmd(argc, argv) 2925 int argc; 2926 char **argv; 2927 { 2928 int what; /* what we are sending this time */ 2929 int count; /* how many bytes we are going to need to send */ 2930 int i; 2931 int question = 0; /* was at least one argument a question */ 2932 struct sendlist *s; /* pointer to current command */ 2933 2934 if (argc < 2) { 2935 printf("need at least one argument for 'send' command\n"); 2936 printf("'send ?' for help\n"); 2937 return 0; 2938 } 2939 /* 2940 * First, validate all the send arguments. 2941 * In addition, we see how much space we are going to need, and 2942 * whether or not we will be doing a "SYNCH" operation (which 2943 * flushes the network queue). 2944 */ 2945 count = 0; 2946 for (i = 1; i < argc; i++) { 2947 s = getsend(argv[i]); 2948 if (s == 0) { 2949 printf("Unknown send argument '%s'\n'send ?' for help.\n", 2950 argv[i]); 2951 return 0; 2952 } else if (s == Ambiguous(struct sendlist *)) { 2953 printf("Ambiguous send argument '%s'\n'send ?' for help.\n", 2954 argv[i]); 2955 return 0; 2956 } 2957 switch (s->what) { 2958 case SENDQUESTION: 2959 break; 2960 case SENDESCAPE: 2961 count += 1; 2962 break; 2963 case SYNCH: 2964 count += 2; 2965 break; 2966 default: 2967 count += 2; 2968 break; 2969 } 2970 } 2971 /* Now, do we have enough room? */ 2972 if (NETROOM() < count) { 2973 printf("There is not enough room in the buffer TO the network\n"); 2974 printf("to process your request. Nothing will be done.\n"); 2975 printf("('send synch' will throw away most data in the network\n"); 2976 printf("buffer, if this might help.)\n"); 2977 return 0; 2978 } 2979 /* OK, they are all OK, now go through again and actually send */ 2980 for (i = 1; i < argc; i++) { 2981 if ((s = getsend(argv[i])) == 0) { 2982 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); 2983 quit(); 2984 /*NOTREACHED*/ 2985 } 2986 if (s->routine) { 2987 (*s->routine)(s); 2988 } else { 2989 switch (what = s->what) { 2990 case SYNCH: 2991 dosynch(); 2992 break; 2993 case SENDQUESTION: 2994 for (s = Sendlist; s->name; s++) { 2995 if (s->help) { 2996 printf(s->name); 2997 if (s->help) { 2998 printf("\t%s", s->help); 2999 } 3000 printf("\n"); 3001 } 3002 } 3003 question = 1; 3004 break; 3005 case SENDESCAPE: 3006 NETADD(escape); 3007 break; 3008 default: 3009 NET2ADD(IAC, what); 3010 break; 3011 } 3012 } 3013 } 3014 return !question; 3015 } 3016 3017 /* 3018 * The following are the routines and data structures referred 3019 * to by the arguments to the "toggle" command. 3020 */ 3021 3022 static 3023 lclchars() 3024 { 3025 donelclchars = 1; 3026 return 1; 3027 } 3028 3029 static 3030 togdebug() 3031 { 3032 #ifndef NOT43 3033 if (net > 0 && 3034 (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { 3035 perror("setsockopt (SO_DEBUG)"); 3036 } 3037 #else /* NOT43 */ 3038 if (debug) { 3039 if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 3040 perror("setsockopt (SO_DEBUG)"); 3041 } else 3042 printf("Cannot turn off socket debugging\n"); 3043 #endif /* NOT43 */ 3044 return 1; 3045 } 3046 3047 3048 static int 3049 togbinary() 3050 { 3051 donebinarytoggle = 1; 3052 3053 if (myopts[TELOPT_BINARY] == 0) { /* Go into binary mode */ 3054 NET2ADD(IAC, DO); 3055 NETADD(TELOPT_BINARY); 3056 printoption("<SENT", doopt, TELOPT_BINARY, 0); 3057 NET2ADD(IAC, WILL); 3058 NETADD(TELOPT_BINARY); 3059 printoption("<SENT", doopt, TELOPT_BINARY, 0); 3060 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1; 3061 printf("Negotiating binary mode with remote host.\n"); 3062 } else { /* Turn off binary mode */ 3063 NET2ADD(IAC, DONT); 3064 NETADD(TELOPT_BINARY); 3065 printoption("<SENT", dont, TELOPT_BINARY, 0); 3066 NET2ADD(IAC, DONT); 3067 NETADD(TELOPT_BINARY); 3068 printoption("<SENT", dont, TELOPT_BINARY, 0); 3069 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0; 3070 printf("Negotiating network ascii mode with remote host.\n"); 3071 } 3072 return 1; 3073 } 3074 3075 3076 3077 extern int togglehelp(); 3078 3079 struct togglelist { 3080 char *name; /* name of toggle */ 3081 char *help; /* help message */ 3082 int (*handler)(); /* routine to do actual setting */ 3083 int dohelp; /* should we display help information */ 3084 int *variable; 3085 char *actionexplanation; 3086 }; 3087 3088 static struct togglelist Togglelist[] = { 3089 { "autoflush", 3090 "toggle flushing of output when sending interrupt characters", 3091 0, 3092 1, 3093 &autoflush, 3094 "flush output when sending interrupt characters" }, 3095 { "autosynch", 3096 "toggle automatic sending of interrupt characters in urgent mode", 3097 0, 3098 1, 3099 &autosynch, 3100 "send interrupt characters in urgent mode" }, 3101 { "binary", 3102 "toggle sending and receiving of binary data", 3103 togbinary, 3104 1, 3105 0, 3106 "send and receive network data in binary mode" }, 3107 { "crmod", 3108 "toggle mapping of received carriage returns", 3109 0, 3110 1, 3111 &crmod, 3112 "map carriage return on output" }, 3113 { "localchars", 3114 "toggle local recognition of certain control characters", 3115 lclchars, 3116 1, 3117 &localchars, 3118 "recognize certain control characters" }, 3119 { " ", "", 0, 1 }, /* empty line */ 3120 { "debug", 3121 "(debugging) toggle debugging", 3122 togdebug, 3123 1, 3124 &debug, 3125 "turn on socket level debugging" }, 3126 { "netdata", 3127 "(debugging) toggle printing of hexadecimal network data", 3128 0, 3129 1, 3130 &netdata, 3131 "print hexadecimal representation of network traffic" }, 3132 { "options", 3133 "(debugging) toggle viewing of options processing", 3134 0, 3135 1, 3136 &showoptions, 3137 "show option processing" }, 3138 { " ", "", 0, 1 }, /* empty line */ 3139 { "?", 3140 "display help information", 3141 togglehelp, 3142 1 }, 3143 { "help", 3144 "display help information", 3145 togglehelp, 3146 0 }, 3147 { 0 } 3148 }; 3149 3150 static 3151 togglehelp() 3152 { 3153 struct togglelist *c; 3154 3155 for (c = Togglelist; c->name; c++) { 3156 if (c->dohelp) { 3157 printf("%s\t%s\n", c->name, c->help); 3158 } 3159 } 3160 return 0; 3161 } 3162 3163 static char ** 3164 getnexttoggle(name) 3165 char *name; 3166 { 3167 struct togglelist *c = (struct togglelist *) name; 3168 3169 return (char **) (c+1); 3170 } 3171 3172 static struct togglelist * 3173 gettoggle(name) 3174 char *name; 3175 { 3176 return (struct togglelist *) 3177 genget(name, (char **) Togglelist, getnexttoggle); 3178 } 3179 3180 static 3181 toggle(argc, argv) 3182 int argc; 3183 char *argv[]; 3184 { 3185 int retval = 1; 3186 char *name; 3187 struct togglelist *c; 3188 3189 if (argc < 2) { 3190 fprintf(stderr, 3191 "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); 3192 return 0; 3193 } 3194 argc--; 3195 argv++; 3196 while (argc--) { 3197 name = *argv++; 3198 c = gettoggle(name); 3199 if (c == Ambiguous(struct togglelist *)) { 3200 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", 3201 name); 3202 return 0; 3203 } else if (c == 0) { 3204 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", 3205 name); 3206 return 0; 3207 } else { 3208 if (c->variable) { 3209 *c->variable = !*c->variable; /* invert it */ 3210 printf("%s %s.\n", *c->variable? "Will" : "Won't", 3211 c->actionexplanation); 3212 } 3213 if (c->handler) { 3214 retval &= (*c->handler)(c); 3215 } 3216 } 3217 } 3218 return retval; 3219 } 3220 3221 /* 3222 * The following perform the "set" command. 3223 */ 3224 3225 struct setlist { 3226 char *name; /* name */ 3227 char *help; /* help information */ 3228 char *charp; /* where it is located at */ 3229 }; 3230 3231 static struct setlist Setlist[] = { 3232 { "echo", "character to toggle local echoing on/off", &echoc }, 3233 { "escape", "character to escape back to telnet command mode", &escape }, 3234 { " ", "" }, 3235 { " ", "The following need 'localchars' to be toggled true", 0 }, 3236 #if defined(unix) 3237 { "erase", "character to cause an Erase Character", &nttyb.sg_erase }, 3238 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc }, 3239 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc }, 3240 { "kill", "character to cause an Erase Line", &nttyb.sg_kill }, 3241 { "quit", "character to cause a Break", &ntc.t_quitc }, 3242 { "eof", "character to cause an EOF ", &ntc.t_eofc }, 3243 #endif /* defined(unix) */ 3244 #if defined(MSDOS) 3245 { "erase", "character to cause an Erase Character", &termEraseChar }, 3246 { "flushoutput", "character to cause an Abort Oubput", &termFlushChar }, 3247 { "interrupt", "character to cause an Interrupt Process", &termIntChar }, 3248 { "kill", "character to cause an Erase Line", &termKillChar }, 3249 { "quit", "character to cause a Break", &termQuitChar }, 3250 { "eof", "character to cause an EOF ", &termEofChar }, 3251 #endif /* defined(MSDOS) */ 3252 { 0 } 3253 }; 3254 3255 static char ** 3256 getnextset(name) 3257 char *name; 3258 { 3259 struct setlist *c = (struct setlist *)name; 3260 3261 return (char **) (c+1); 3262 } 3263 3264 static struct setlist * 3265 getset(name) 3266 char *name; 3267 { 3268 return (struct setlist *) genget(name, (char **) Setlist, getnextset); 3269 } 3270 3271 static 3272 setcmd(argc, argv) 3273 int argc; 3274 char *argv[]; 3275 { 3276 int value; 3277 struct setlist *ct; 3278 3279 /* XXX back we go... sigh */ 3280 if (argc != 3) { 3281 if ((argc == 2) && 3282 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) { 3283 for (ct = Setlist; ct->name; ct++) { 3284 printf("%s\t%s\n", ct->name, ct->help); 3285 } 3286 printf("?\tdisplay help information\n"); 3287 } else { 3288 printf("Format is 'set Name Value'\n'set ?' for help.\n"); 3289 } 3290 return 0; 3291 } 3292 3293 ct = getset(argv[1]); 3294 if (ct == 0) { 3295 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", 3296 argv[1]); 3297 return 0; 3298 } else if (ct == Ambiguous(struct setlist *)) { 3299 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", 3300 argv[1]); 3301 return 0; 3302 } else { 3303 if (strcmp("off", argv[2])) { 3304 value = special(argv[2]); 3305 } else { 3306 value = -1; 3307 } 3308 *(ct->charp) = value; 3309 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); 3310 } 3311 return 1; 3312 } 3313 3314 /* 3315 * The following are the data structures and routines for the 3316 * 'mode' command. 3317 */ 3318 3319 static 3320 dolinemode() 3321 { 3322 if (hisopts[TELOPT_SGA]) { 3323 wontoption(TELOPT_SGA, 0); 3324 } 3325 if (hisopts[TELOPT_ECHO]) { 3326 wontoption(TELOPT_ECHO, 0); 3327 } 3328 return 1; 3329 } 3330 3331 static 3332 docharmode() 3333 { 3334 if (!hisopts[TELOPT_SGA]) { 3335 willoption(TELOPT_SGA, 0); 3336 } 3337 if (!hisopts[TELOPT_ECHO]) { 3338 willoption(TELOPT_ECHO, 0); 3339 } 3340 return 1; 3341 } 3342 3343 static struct cmd Modelist[] = { 3344 { "character", "character-at-a-time mode", docharmode, 1, 1 }, 3345 { "line", "line-by-line mode", dolinemode, 1, 1 }, 3346 { 0 }, 3347 }; 3348 3349 static char ** 3350 getnextmode(name) 3351 char *name; 3352 { 3353 struct cmd *c = (struct cmd *) name; 3354 3355 return (char **) (c+1); 3356 } 3357 3358 static struct cmd * 3359 getmodecmd(name) 3360 char *name; 3361 { 3362 return (struct cmd *) genget(name, (char **) Modelist, getnextmode); 3363 } 3364 3365 static 3366 modecmd(argc, argv) 3367 int argc; 3368 char *argv[]; 3369 { 3370 struct cmd *mt; 3371 3372 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) { 3373 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); 3374 for (mt = Modelist; mt->name; mt++) { 3375 printf("%s\t%s\n", mt->name, mt->help); 3376 } 3377 return 0; 3378 } 3379 mt = getmodecmd(argv[1]); 3380 if (mt == 0) { 3381 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); 3382 return 0; 3383 } else if (mt == Ambiguous(struct cmd *)) { 3384 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); 3385 return 0; 3386 } else { 3387 (*mt->handler)(); 3388 } 3389 return 1; 3390 } 3391 3392 /* 3393 * The following data structures and routines implement the 3394 * "display" command. 3395 */ 3396 3397 static 3398 display(argc, argv) 3399 int argc; 3400 char *argv[]; 3401 { 3402 #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ 3403 if (*tl->variable) { \ 3404 printf("will"); \ 3405 } else { \ 3406 printf("won't"); \ 3407 } \ 3408 printf(" %s.\n", tl->actionexplanation); \ 3409 } 3410 3411 #define doset(sl) if (sl->name && *sl->name != ' ') { \ 3412 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \ 3413 } 3414 3415 struct togglelist *tl; 3416 struct setlist *sl; 3417 3418 if (argc == 1) { 3419 for (tl = Togglelist; tl->name; tl++) { 3420 dotog(tl); 3421 } 3422 printf("\n"); 3423 for (sl = Setlist; sl->name; sl++) { 3424 doset(sl); 3425 } 3426 } else { 3427 int i; 3428 3429 for (i = 1; i < argc; i++) { 3430 sl = getset(argv[i]); 3431 tl = gettoggle(argv[i]); 3432 if ((sl == Ambiguous(struct setlist *)) || 3433 (tl == Ambiguous(struct togglelist *))) { 3434 printf("?Ambiguous argument '%s'.\n", argv[i]); 3435 return 0; 3436 } else if (!sl && !tl) { 3437 printf("?Unknown argument '%s'.\n", argv[i]); 3438 return 0; 3439 } else { 3440 if (tl) { 3441 dotog(tl); 3442 } 3443 if (sl) { 3444 doset(sl); 3445 } 3446 } 3447 } 3448 } 3449 return 1; 3450 #undef doset 3451 #undef dotog 3452 } 3453 3454 /* 3455 * The following are the data structures, and many of the routines, 3456 * relating to command processing. 3457 */ 3458 3459 /* 3460 * Set the escape character. 3461 */ 3462 static 3463 setescape(argc, argv) 3464 int argc; 3465 char *argv[]; 3466 { 3467 register char *arg; 3468 char buf[50]; 3469 3470 printf( 3471 "Deprecated usage - please use 'set escape%s%s' in the future.\n", 3472 (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); 3473 if (argc > 2) 3474 arg = argv[1]; 3475 else { 3476 printf("new escape character: "); 3477 gets(buf); 3478 arg = buf; 3479 } 3480 if (arg[0] != '\0') 3481 escape = arg[0]; 3482 if (!In3270) { 3483 printf("Escape character is '%s'.\n", control(escape)); 3484 } 3485 fflush(stdout); 3486 return 1; 3487 } 3488 3489 /*VARARGS*/ 3490 static 3491 togcrmod() 3492 { 3493 crmod = !crmod; 3494 printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); 3495 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); 3496 fflush(stdout); 3497 return 1; 3498 } 3499 3500 /*VARARGS*/ 3501 suspend() 3502 { 3503 setcommandmode(); 3504 #if defined(unix) 3505 kill(0, SIGTSTP); 3506 #endif /* defined(unix) */ 3507 /* reget parameters in case they were changed */ 3508 TerminalSaveState(); 3509 setconnmode(); 3510 return 1; 3511 } 3512 3513 /*VARARGS*/ 3514 static 3515 bye(argc, argv) 3516 int argc; /* Number of arguments */ 3517 char *argv[]; /* arguments */ 3518 { 3519 if (connected) { 3520 shutdown(net, 2); 3521 printf("Connection closed.\n"); 3522 NetClose(net); 3523 connected = 0; 3524 /* reset options */ 3525 tninit(); 3526 #if defined(TN3270) 3527 SetIn3270(); /* Get out of 3270 mode */ 3528 #endif /* defined(TN3270) */ 3529 } 3530 if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { 3531 longjmp(toplevel, 1); 3532 /* NOTREACHED */ 3533 } 3534 return 1; /* Keep lint, etc., happy */ 3535 } 3536 3537 /*VARARGS*/ 3538 quit() 3539 { 3540 (void) call(bye, "bye", "fromquit", 0); 3541 Exit(0); 3542 /*NOTREACHED*/ 3543 return 1; /* just to keep lint happy */ 3544 } 3545 3546 /* 3547 * Print status about the connection. 3548 */ 3549 static 3550 status(argc, argv) 3551 int argc; 3552 char *argv[]; 3553 { 3554 if (connected) { 3555 printf("Connected to %s.\n", hostname); 3556 if (argc < 2) { 3557 printf("Operating in %s.\n", 3558 modelist[getconnmode()].modedescriptions); 3559 if (localchars) { 3560 printf("Catching signals locally.\n"); 3561 } 3562 } 3563 } else { 3564 printf("No connection.\n"); 3565 } 3566 # if !defined(TN3270) 3567 printf("Escape character is '%s'.\n", control(escape)); 3568 fflush(stdout); 3569 # else /* !defined(TN3270) */ 3570 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { 3571 printf("Escape character is '%s'.\n", control(escape)); 3572 } 3573 # if defined(unix) 3574 if (In3270 && transcom) { 3575 printf("Transparent mode command is '%s'.\n", transcom); 3576 } 3577 # endif /* defined(unix) */ 3578 fflush(stdout); 3579 if (In3270) { 3580 return 0; 3581 } 3582 # endif /* defined(TN3270) */ 3583 return 1; 3584 } 3585 3586 #if defined(TN3270) && defined(unix) 3587 static 3588 settranscom(argc, argv) 3589 int argc; 3590 char *argv[]; 3591 { 3592 int i, len = 0; 3593 char *strcpy(), *strcat(); 3594 3595 if (argc == 1 && transcom) { 3596 transcom = 0; 3597 } 3598 if (argc == 1) { 3599 return; 3600 } 3601 for (i = 1; i < argc; ++i) { 3602 len += 1 + strlen(argv[1]); 3603 } 3604 transcom = tline; 3605 (void) strcpy(transcom, argv[1]); 3606 for (i = 2; i < argc; ++i) { 3607 (void) strcat(transcom, " "); 3608 (void) strcat(transcom, argv[i]); 3609 } 3610 } 3611 #endif /* defined(TN3270) && defined(unix) */ 3612 3613 3614 3615 static 3616 tn(argc, argv) 3617 int argc; 3618 char *argv[]; 3619 { 3620 register struct hostent *host = 0; 3621 #if defined(MSDOS) 3622 char *cp; 3623 #endif /* defined(MSDOS) */ 3624 3625 if (connected) { 3626 printf("?Already connected to %s\n", hostname); 3627 return 0; 3628 } 3629 if (argc < 2) { 3630 (void) strcpy(line, "Connect "); 3631 printf("(to) "); 3632 gets(&line[strlen(line)]); 3633 makeargv(); 3634 argc = margc; 3635 argv = margv; 3636 } 3637 if ((argc < 2) || (argc > 3)) { 3638 printf("usage: %s host-name [port]\n", argv[0]); 3639 return 0; 3640 } 3641 #if defined(MSDOS) 3642 for (cp = argv[1]; *cp; cp++) { 3643 if (isupper(*cp)) { 3644 *cp = tolower(*cp); 3645 } 3646 } 3647 #endif /* defined(MSDOS) */ 3648 sin.sin_addr.s_addr = inet_addr(argv[1]); 3649 if (sin.sin_addr.s_addr != -1) { 3650 sin.sin_family = AF_INET; 3651 (void) strcpy(hnamebuf, argv[1]); 3652 hostname = hnamebuf; 3653 } else { 3654 host = gethostbyname(argv[1]); 3655 if (host) { 3656 sin.sin_family = host->h_addrtype; 3657 #if defined(h_addr) /* In 4.3, this is a #define */ 3658 memcpy((caddr_t)&sin.sin_addr, 3659 host->h_addr_list[0], host->h_length); 3660 #else /* defined(h_addr) */ 3661 memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length); 3662 #endif /* defined(h_addr) */ 3663 hostname = host->h_name; 3664 } else { 3665 printf("%s: unknown host\n", argv[1]); 3666 return 0; 3667 } 3668 } 3669 sin.sin_port = sp->s_port; 3670 if (argc == 3) { 3671 sin.sin_port = atoi(argv[2]); 3672 if (sin.sin_port == 0) { 3673 sp = getservbyname(argv[2], "tcp"); 3674 if (sp) 3675 sin.sin_port = sp->s_port; 3676 else { 3677 printf("%s: bad port number\n", argv[2]); 3678 return 0; 3679 } 3680 } else { 3681 sin.sin_port = atoi(argv[2]); 3682 sin.sin_port = htons(sin.sin_port); 3683 } 3684 telnetport = 0; 3685 } else { 3686 telnetport = 1; 3687 } 3688 #if defined(unix) 3689 signal(SIGINT, intr); 3690 signal(SIGQUIT, intr2); 3691 signal(SIGPIPE, deadpeer); 3692 #endif /* defined(unix) */ 3693 printf("Trying...\n"); 3694 do { 3695 net = socket(AF_INET, SOCK_STREAM, 0); 3696 if (net < 0) { 3697 perror("telnet: socket"); 3698 return 0; 3699 } 3700 if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { 3701 perror("setsockopt (SO_DEBUG)"); 3702 } 3703 3704 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 3705 #if defined(h_addr) /* In 4.3, this is a #define */ 3706 if (host && host->h_addr_list[1]) { 3707 int oerrno = errno; 3708 3709 fprintf(stderr, "telnet: connect to address %s: ", 3710 inet_ntoa(sin.sin_addr)); 3711 errno = oerrno; 3712 perror((char *)0); 3713 host->h_addr_list++; 3714 memcpy((caddr_t)&sin.sin_addr, 3715 host->h_addr_list[0], host->h_length); 3716 fprintf(stderr, "Trying %s...\n", 3717 inet_ntoa(sin.sin_addr)); 3718 (void) NetClose(net); 3719 continue; 3720 } 3721 #endif /* defined(h_addr) */ 3722 perror("telnet: Unable to connect to remote host"); 3723 #if defined(unix) 3724 signal(SIGINT, SIG_DFL); 3725 signal(SIGQUIT, SIG_DFL); 3726 #endif /* defined(unix) */ 3727 return 0; 3728 } 3729 connected++; 3730 } while (connected == 0); 3731 call(status, "status", "notmuch", 0); 3732 if (setjmp(peerdied) == 0) 3733 telnet(); 3734 NetClose(net); 3735 ExitString(stderr, "Connection closed by foreign host.\n",1); 3736 /*NOTREACHED*/ 3737 } 3738 3739 3740 #define HELPINDENT (sizeof ("connect")) 3741 3742 static char 3743 openhelp[] = "connect to a site", 3744 closehelp[] = "close current connection", 3745 quithelp[] = "exit telnet", 3746 statushelp[] = "print status information", 3747 helphelp[] = "print help information", 3748 sendhelp[] = "transmit special characters ('send ?' for more)", 3749 sethelp[] = "set operating parameters ('set ?' for more)", 3750 togglestring[] ="toggle operating parameters ('toggle ?' for more)", 3751 displayhelp[] = "display operating parameters", 3752 #if defined(TN3270) && defined(unix) 3753 transcomhelp[] = "specify Unix command for transparent mode pipe", 3754 #endif /* defined(TN3270) && defined(unix) */ 3755 #if defined(unix) 3756 zhelp[] = "suspend telnet", 3757 #endif /* defined(unix */ 3758 #if defined(TN3270) 3759 shellhelp[] = "invoke a subshell", 3760 #endif /* defined(TN3270) */ 3761 modehelp[] = "try to enter line-by-line or character-at-a-time mode"; 3762 3763 extern int help(), shell(); 3764 3765 static struct cmd cmdtab[] = { 3766 { "close", closehelp, bye, 1, 1 }, 3767 { "display", displayhelp, display, 1, 0 }, 3768 { "mode", modehelp, modecmd, 1, 1 }, 3769 { "open", openhelp, tn, 1, 0 }, 3770 { "quit", quithelp, quit, 1, 0 }, 3771 { "send", sendhelp, sendcmd, 1, 1 }, 3772 { "set", sethelp, setcmd, 1, 0 }, 3773 { "status", statushelp, status, 1, 0 }, 3774 { "toggle", togglestring, toggle, 1, 0 }, 3775 #if defined(TN3270) && defined(unix) 3776 { "transcom", transcomhelp, settranscom, 1, 0 }, 3777 #endif /* defined(TN3270) && defined(unix) */ 3778 #if defined(unix) 3779 { "z", zhelp, suspend, 1, 0 }, 3780 #endif /* defined(unix) */ 3781 #if defined(TN3270) 3782 { "!", shellhelp, shell, 1, 1 }, 3783 #endif /* defined(TN3270) */ 3784 { "?", helphelp, help, 1, 0 }, 3785 0 3786 }; 3787 3788 static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; 3789 static char escapehelp[] = "deprecated command -- use 'set escape' instead"; 3790 3791 static struct cmd cmdtab2[] = { 3792 { "help", helphelp, help, 0, 0 }, 3793 { "escape", escapehelp, setescape, 1, 0 }, 3794 { "crmod", crmodhelp, togcrmod, 1, 0 }, 3795 0 3796 }; 3797 3798 /* 3799 * Call routine with argc, argv set from args (terminated by 0). 3800 * VARARGS2 3801 */ 3802 static 3803 call(routine, args) 3804 int (*routine)(); 3805 char *args; 3806 { 3807 register char **argp; 3808 register int argc; 3809 3810 for (argc = 0, argp = &args; *argp++ != 0; argc++) 3811 ; 3812 return (*routine)(argc, &args); 3813 } 3814 3815 static char ** 3816 getnextcmd(name) 3817 char *name; 3818 { 3819 struct cmd *c = (struct cmd *) name; 3820 3821 return (char **) (c+1); 3822 } 3823 3824 static struct cmd * 3825 getcmd(name) 3826 char *name; 3827 { 3828 struct cmd *cm; 3829 3830 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) { 3831 return cm; 3832 } else { 3833 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd); 3834 } 3835 } 3836 3837 void 3838 command(top) 3839 int top; 3840 { 3841 register struct cmd *c; 3842 3843 setcommandmode(); 3844 if (!top) { 3845 putchar('\n'); 3846 } else { 3847 #if defined(unix) 3848 signal(SIGINT, SIG_DFL); 3849 signal(SIGQUIT, SIG_DFL); 3850 #endif /* defined(unix) */ 3851 } 3852 for (;;) { 3853 printf("%s> ", prompt); 3854 if (gets(line) == NULL) { 3855 if (feof(stdin) || ferror(stdin)) 3856 quit(); 3857 break; 3858 } 3859 if (line[0] == 0) 3860 break; 3861 makeargv(); 3862 c = getcmd(margv[0]); 3863 if (c == Ambiguous(struct cmd *)) { 3864 printf("?Ambiguous command\n"); 3865 continue; 3866 } 3867 if (c == 0) { 3868 printf("?Invalid command\n"); 3869 continue; 3870 } 3871 if (c->needconnect && !connected) { 3872 printf("?Need to be connected first.\n"); 3873 continue; 3874 } 3875 if ((*c->handler)(margc, margv)) { 3876 break; 3877 } 3878 } 3879 if (!top) { 3880 if (!connected) { 3881 longjmp(toplevel, 1); 3882 /*NOTREACHED*/ 3883 } 3884 if (shell_active == 0) { 3885 setconnmode(); 3886 } 3887 } 3888 } 3889 3890 /* 3891 * Help command. 3892 */ 3893 static 3894 help(argc, argv) 3895 int argc; 3896 char *argv[]; 3897 { 3898 register struct cmd *c; 3899 3900 if (argc == 1) { 3901 printf("Commands may be abbreviated. Commands are:\n\n"); 3902 for (c = cmdtab; c->name; c++) 3903 if (c->dohelp) { 3904 printf("%-*s\t%s\n", HELPINDENT, c->name, 3905 c->help); 3906 } 3907 return 0; 3908 } 3909 while (--argc > 0) { 3910 register char *arg; 3911 arg = *++argv; 3912 c = getcmd(arg); 3913 if (c == Ambiguous(struct cmd *)) 3914 printf("?Ambiguous help command %s\n", arg); 3915 else if (c == (struct cmd *)0) 3916 printf("?Invalid help command %s\n", arg); 3917 else 3918 printf("%s\n", c->help); 3919 } 3920 return 0; 3921 } 3922 3923 /* 3924 * main. Parse arguments, invoke the protocol or command parser. 3925 */ 3926 3927 3928 void 3929 main(argc, argv) 3930 int argc; 3931 char *argv[]; 3932 { 3933 tninit(); /* Clear out things */ 3934 3935 NetTrace = stdout; 3936 TerminalSaveState(); 3937 autoflush = TerminalAutoFlush(); 3938 3939 prompt = argv[0]; 3940 while ((argc > 1) && (argv[1][0] == '-')) { 3941 if (!strcmp(argv[1], "-d")) { 3942 debug = 1; 3943 } else if (!strcmp(argv[1], "-n")) { 3944 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */ 3945 NetTrace = fopen(argv[2], "w"); 3946 argv++; 3947 argc--; 3948 if (NetTrace == NULL) { 3949 NetTrace = stdout; 3950 } 3951 } 3952 } else { 3953 #if defined(TN3270) && defined(unix) 3954 if (!strcmp(argv[1], "-t")) { 3955 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */ 3956 transcom = tline; 3957 (void) strcpy(transcom, argv[1]); 3958 argv++; 3959 argc--; 3960 } 3961 } else if (!strcmp(argv[1], "-noasynch")) { 3962 noasynch = 1; 3963 } else 3964 #endif /* defined(TN3270) && defined(unix) */ 3965 if (argv[1][1] != '\0') { 3966 fprintf(stderr, "Unknown option *%s*.\n", argv[1]); 3967 } 3968 } 3969 argc--; 3970 argv++; 3971 } 3972 if (argc != 1) { 3973 if (setjmp(toplevel) != 0) 3974 Exit(0); 3975 tn(argc, argv); 3976 } 3977 setjmp(toplevel); 3978 for (;;) { 3979 #if !defined(TN3270) 3980 command(1); 3981 #else /* !defined(TN3270) */ 3982 if (!shell_active) { 3983 command(1); 3984 } else { 3985 #if defined(TN3270) 3986 shell_continue(); 3987 #endif /* defined(TN3270) */ 3988 } 3989 #endif /* !defined(TN3270) */ 3990 } 3991 } 3992