1: /* 2: * Copyright 1984, 1985 by the Regents of the University of 3: * 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: 23: #if defined(DOSCCS) && !defined(lint) 24: static char sccsid[] = "@(#)tn3270.c 2.8\t1/1/94"; 25: #endif 26: 27: /* 28: * User telnet program, specially modified for tn3270. 29: */ 30: #include <sys/types.h> 31: #include <sys/socket.h> 32: #include <sys/ioctl.h> 33: #include <sys/time.h> 34: 35: #include <netinet/in.h> 36: #include <arpa/inet.h> 37: 38: #define TELOPTS 39: #include <arpa/telnet.h> 40: 41: #include <stdio.h> 42: #include <ctype.h> 43: #include <errno.h> 44: #include <signal.h> 45: #include <setjmp.h> 46: #include <netdb.h> 47: 48: #define strip(x) ((x)&0177) 49: #define min(x,y) ((x<y)? x:y) 50: 51: #ifndef pdp11 52: #define IBUFSIZ 8*BUFSIZ 53: static char Ibuf[IBUFSIZ], *Ifrontp = Ibuf, *Ibackp = Ibuf; 54: #else 55: /* Move Ibuf onto stack to use last segment. */ 56: #define IBUFSIZ 3*BUFSIZ 57: static char *Ibuf, *Ifrontp, *Ibackp; 58: #endif 59: static char ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf; 60: static char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 61: 62: static char SbBuffer[100], *pSb = SbBuffer; 63: #define Sb_Option SbBuffer[0] 64: #define Sb_Command SbBuffer[1] 65: 66: 67: static char hisopts[256]; 68: static char myopts[256]; 69: 70: static char doopt[] = { IAC, DO, '%', 'c', 0 }; 71: static char dont[] = { IAC, DONT, '%', 'c', 0 }; 72: static char will[] = { IAC, WILL, '%', 'c', 0 }; 73: static char wont[] = { IAC, WONT, '%', 'c', 0 }; 74: static char sb_terminal[] = { IAC, SB, 75: TELOPT_TTYPE, TELQUAL_IS, 76: 'I', 'B', 'M', '-', '3', '2', '7', '7', '-', '2', 77: IAC, SE }; 78: 79: /* The following is a real, live, global. */ 80: 81: /* The point of HaveInput is to give a hint to the terminal output processor 82: * that some input from some source (network or terminal) has come in. 83: */ 84: 85: int HaveInput = 1; /* we have received input in the not too distant past */ 86: 87: 88: static int connected; 89: static int SentTerminalType = 0; /* returned sb_terminal to other? */ 90: static int In3270 = 0; /* we are in 3270 binary mode */ 91: static int ISend = 0; /* trying to send network data in */ 92: static int ForceMode = -1; /* for debugging */ 93: static int net; 94: static int showoptions = 0; 95: static int debug = 0; 96: static int crmod = 0; 97: static int printnet = 0; 98: static FILE *NetTrace; 99: static char *prompt; 100: static char escape = CTRL(]); 101: 102: static char line[200]; 103: static int margc; 104: static char *margv[20]; 105: 106: static jmp_buf toplevel; 107: static jmp_buf peerdied; 108: 109: extern int errno; 110: 111: int quit(), suspend(); 112: static int tn(), bye(), help(); 113: static int setescape(), status(), toggle(), setoptions(); 114: static int setcrmod(), setdebug(), SetPrintNet(); 115: 116: #define HELPINDENT (sizeof ("connect")) 117: 118: struct cmd { 119: char *name; /* command name */ 120: char *help; /* help string */ 121: int (*handler)(); /* routine which executes command */ 122: int dohelp; /* Should we give general help information? */ 123: }; 124: 125: static char openhelp[] = "connect to a site"; 126: static char closehelp[] = "close current connection"; 127: static char quithelp[] = "exit telnet"; 128: static char zhelp[] = "suspend telnet"; 129: static char debughelp[] = "toggle debugging"; 130: static char escapehelp[] = "set escape character"; 131: static char statushelp[] = "print status information"; 132: static char helphelp[] = "print help information"; 133: static char optionshelp[] = "toggle viewing of options processing"; 134: static char crmodhelp[] = "toggle mapping of received carriage returns"; 135: static char printnethelp[] = "print out raw data to/from net"; 136: 137: static struct cmd cmdtab[] = { 138: { "open", openhelp, tn, 1 }, 139: { "close", closehelp, bye, 1 }, 140: { "quit", quithelp, quit, 1 }, 141: { "z", zhelp, suspend, 1 }, 142: { "suspend", zhelp, suspend, 0 }, 143: { "escape", escapehelp, setescape, 0 }, 144: { "status", statushelp, status, 1 }, 145: { "options", optionshelp, setoptions, 0 }, 146: { "crmod", crmodhelp, setcrmod, 0 }, 147: { "debug", debughelp, setdebug, 0 }, 148: { "printnet", printnethelp, SetPrintNet, 0 }, 149: { "?", helphelp, help, 1 }, 150: { "help", helphelp, help, 0 }, 151: 0 152: }; 153: 154: static struct sockaddr_in sin; 155: 156: static int intr(), deadpeer(), inputAvailable(); 157: static char *control(); 158: static struct cmd *getcmd(); 159: static struct servent *sp; 160: 161: static struct tchars otc; 162: static struct ltchars oltc; 163: static struct sgttyb ottyb; 164: 165: main(argc, argv) 166: int argc; 167: char *argv[]; 168: { 169: #ifdef pdp11 170: char SIbuf[IBUFSIZ]; 171: 172: Ibuf = Ifrontp = Ibackp = SIbuf; 173: setbuf(stdout, NULL); 174: #endif 175: ioctl(0, TIOCGETP, (char *)&ottyb); 176: ioctl(0, TIOCGETC, (char *)&otc); 177: ioctl(0, TIOCGLTC, (char *)&oltc); 178: sp = getservbyname("telnet", "tcp"); 179: if (sp == 0) { 180: ExitString(stderr, "telnet: tcp/telnet: unknown service\n", 1); 181: } 182: NetTrace = stdout; 183: prompt = argv[0]; 184: if (argc > 1 && !strcmp(argv[1], "-d")) { 185: debug = SO_DEBUG, argv++, argc--; 186: } 187: if (argc > 1 && !strcmp(argv[1], "-n")) { 188: argv++; 189: argc--; 190: if (argc > 1) { /* get file name */ 191: NetTrace = fopen(argv[1], "w"); 192: argv++; 193: argc--; 194: if (NetTrace == NULL) { 195: NetTrace = stdout; 196: } 197: } 198: } 199: if (argc != 1) { 200: if (setjmp(toplevel) != 0) 201: Exit(0); 202: tn(argc, argv); 203: } 204: setjmp(toplevel); 205: for (;;) 206: command(1); 207: } 208: 209: static char *hostname; 210: static char hnamebuf[32]; 211: 212: static 213: tn(argc, argv) 214: int argc; 215: char *argv[]; 216: { 217: struct hostent *host; 218: char *strcpy(); 219: 220: if (connected) { 221: printf("?Already connected to %s\n", hostname); 222: return; 223: } 224: if (argc < 2) { 225: (void) strcpy(line, "Connect "); 226: printf("(to) "); 227: gets(&line[strlen(line)]); 228: makeargv(); 229: argc = margc; 230: argv = margv; 231: } 232: if (argc > 3) { 233: printf("usage: %s host-name [port]\n", argv[0]); 234: return; 235: } 236: host = gethostbyname(argv[1]); 237: if (host) { 238: sin.sin_family = host->h_addrtype; 239: bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length); 240: hostname = host->h_name; 241: } else { 242: sin.sin_family = AF_INET; 243: sin.sin_addr.s_addr = inet_addr(argv[1]); 244: if (sin.sin_addr.s_addr == -1) { 245: printf("%s: unknown host\n", argv[1]); 246: return; 247: } 248: (void) strcpy(hnamebuf, argv[1]); 249: hostname = hnamebuf; 250: } 251: sin.sin_port = sp->s_port; 252: if (argc == 3) { 253: sin.sin_port = atoi(argv[2]); 254: sin.sin_port = htons(sin.sin_port); 255: } 256: net = socket(AF_INET, SOCK_STREAM, 0); 257: if (net < 0) { 258: perror("telnet: socket"); 259: return; 260: } 261: if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 262: perror("setsockopt (SO_DEBUG)"); 263: signal(SIGINT, intr); 264: signal(SIGPIPE, deadpeer); 265: printf("Trying...\n"); 266: if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 267: perror("telnet: connect"); 268: signal(SIGINT, SIG_DFL); 269: return; 270: } 271: connected++; 272: call(status, "status", 0); 273: if (setjmp(peerdied) == 0) 274: telnet(); 275: if (In3270) { 276: Stop3270(1); 277: } 278: ExitString(stderr, "Connection closed by foreign host.\n", 1); 279: } 280: 281: /* 282: * Print status about the connection. 283: */ 284: /*VARARGS*/ 285: static 286: status() 287: { 288: if (connected) 289: printf("Connected to %s.\n", hostname); 290: else 291: printf("No connection.\n"); 292: /*printf("Escape character is '%s'.\n", control(escape));*/ 293: fflush(stdout); 294: } 295: 296: static 297: makeargv() 298: { 299: register char *cp; 300: register char **argp = margv; 301: 302: margc = 0; 303: for (cp = line; *cp;) { 304: while (isspace(*cp)) 305: cp++; 306: if (*cp == '\0') 307: break; 308: *argp++ = cp; 309: margc += 1; 310: while (*cp != '\0' && !isspace(*cp)) 311: cp++; 312: if (*cp == '\0') 313: break; 314: *cp++ = '\0'; 315: } 316: *argp++ = 0; 317: } 318: 319: /*VARARGS*/ 320: suspend() 321: { 322: register int save; 323: 324: save = mode(0); 325: kill(0, SIGTSTP); 326: /* reget parameters in case they were changed */ 327: ioctl(0, TIOCGETP, (char *)&ottyb); 328: ioctl(0, TIOCGETC, (char *)&otc); 329: ioctl(0, TIOCGLTC, (char *)&oltc); 330: (void) mode(save); 331: } 332: 333: /*VARARGS*/ 334: static 335: bye() 336: { 337: register char *op; 338: 339: (void) mode(0); 340: if (connected) { 341: shutdown(net, 2); 342: printf("Connection closed.\n"); 343: close(net); 344: connected = 0; 345: /* reset his options */ 346: for (op = hisopts; op < &hisopts[256]; op++) 347: *op = 0; 348: } 349: } 350: 351: /*VARARGS*/ 352: quit() 353: { 354: call(bye, "bye", 0); 355: Exit(0); 356: } 357: 358: /* 359: * Help command. 360: */ 361: static 362: help(argc, argv) 363: int argc; 364: char *argv[]; 365: { 366: register struct cmd *c; 367: 368: if (argc == 1) { 369: printf("Commands may be abbreviated. Commands are:\n\n"); 370: for (c = cmdtab; c->name; c++) 371: if (c->dohelp) { 372: printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 373: } 374: return; 375: } 376: while (--argc > 0) { 377: register char *arg; 378: arg = *++argv; 379: c = getcmd(arg); 380: if (c == (struct cmd *)-1) 381: printf("?Ambiguous help command %s\n", arg); 382: else if (c == (struct cmd *)0) 383: printf("?Invalid help command %s\n", arg); 384: else 385: printf("%s\n", c->help); 386: } 387: } 388: 389: /* 390: * Call routine with argc, argv set from args (terminated by 0). 391: * VARARGS2 392: */ 393: static 394: call(routine, args) 395: int (*routine)(); 396: int args; 397: { 398: register int *argp; 399: register int argc; 400: 401: for (argc = 0, argp = &args; *argp++ != 0; argc++) 402: ; 403: (*routine)(argc, &args); 404: } 405: 406: static struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 407: static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 408: 409: mode(f) 410: register int f; 411: { 412: static int prevmode = 0; 413: struct tchars *tc, tc3; 414: struct ltchars *ltc; 415: struct sgttyb sb; 416: int onoff, old; 417: 418: if (prevmode == f) 419: return (f); 420: old = prevmode; 421: prevmode = f; 422: sb = ottyb; 423: if (ForceMode != -1) { 424: f = ForceMode; 425: ForceMode = -1; 426: } 427: switch (f) { 428: 429: case 0: 430: onoff = 0; 431: tc = &otc; 432: ltc = &oltc; 433: break; 434: 435: case 1: /* the rawest */ 436: case 2: /* allows for local echoing, newline mapping */ 437: case 3: /* like 1, but with XON/XOFF */ 438: 439: sb.sg_flags |= CBREAK; 440: if ((f == 1) || (f == 3)) { 441: sb.sg_flags &= ~(ECHO|CRMOD); 442: } else { 443: sb.sg_flags |= ECHO|CRMOD; 444: } 445: sb.sg_erase = sb.sg_kill = -1; 446: if (f == 3) { 447: tc = &tc3; 448: tc3 = notc; 449: /* get XON, XOFF characters */ 450: tc3.t_startc = otc.t_startc; 451: tc3.t_stopc = otc.t_stopc; 452: } else { 453: tc = ¬c; 454: } 455: ltc = &noltc; 456: onoff = 1; 457: break; 458: 459: default: 460: return(old); 461: } 462: ioctl(fileno(stdin), TIOCSLTC, (char *)ltc); 463: ioctl(fileno(stdin), TIOCSETC, (char *)tc); 464: ioctl(fileno(stdin), TIOCSETP, (char *)&sb); 465: ioctl(fileno(stdin), FIONBIO, (char *)&onoff); 466: ioctl(fileno(stdout), FIONBIO, (char *)&onoff); 467: ioctl(fileno(stdin), FIOASYNC, (char *)&onoff); 468: return (old); 469: } 470: 471: static char sibuf[BUFSIZ], *sbp; 472: static char tibuf[BUFSIZ], *tbp; 473: static int scc, tcc; 474: static int tin, tout; /* file descriptors */ 475: 476: /* 477: * Select from tty and network... 478: */ 479: static 480: telnet() 481: { 482: int on = 1; 483: int negativePid = -getpid(); 484: int schedValue; 485: 486: (void) mode(2); 487: ioctl(net, FIONBIO, (char *)&on); 488: ioctl(net, FIOASYNC, (char *)&on); /* hear about input */ 489: ioctl(net, SIOCSPGRP, (char *)&negativePid); /* set my pid */ 490: tin = fileno(stdin); 491: tout = fileno(stdout); 492: 493: for (;;) { 494: while (schedValue = Scheduler(0)) { 495: if (schedValue == -1) { 496: (void) mode(0); 497: return; 498: } 499: } 500: /* If there is data waiting to go out to terminal, don't 501: * schedule any more data for the terminal. 502: */ 503: if (tfrontp-tbackp || !In3270) { 504: schedValue = 1; 505: } else { 506: schedValue = DoTerminalOutput(); 507: } 508: if (schedValue) { 509: if (Scheduler(1) == -1) { 510: (void) mode(0); 511: return; 512: } 513: } 514: } 515: } 516: 517: 518: /* Loop around once. */ 519: 520: static 521: Scheduler(block) 522: int block; /* should we block in the select? */ 523: { 524: register int c; 525: fd_set ibits, obits; 526: /* One wants to be a bit careful about setting returnValue 527: * to one, since a one implies we did some useful work, 528: * and therefore probably won't be called to block next 529: * time. 530: */ 531: int returnValue = 0; 532: static struct timeval TimeValue = {0}; 533: 534: FD_ZERO(&ibits); 535: FD_ZERO(&obits); 536: 537: if (!In3270) { 538: if (nfrontp - nbackp) 539: FD_SET(net, &obits); 540: else if (tcc == 0) { 541: FD_SET(tin, &ibits); 542: } 543: if (tfrontp - tbackp) 544: FD_SET(tout, &obits); 545: else if (!ISend) 546: FD_SET(net, &ibits); 547: } else { 548: if (nfrontp - nbackp) { /* something for network? */ 549: FD_SET(net, &obits); /* yes - wait for space */ 550: } 551: if (tcc == 0) { /* any pending tty input? */ 552: FD_SET(tin, &ibits); /* no, look for new input */ 553: } 554: if (tfrontp-tbackp) { /* any pending tty output? */ 555: FD_SET(tout, &obits); /* yes - wait for space */ 556: } 557: if (!ISend) { /* any pending net input? */ 558: FD_SET(net, &ibits); /* no, look for new input */ 559: } 560: } 561: if (scc < 0 && tcc < 0) { 562: return(-1); 563: } 564: if (HaveInput) { /* Reprime SIGIO handler if appropriate */ 565: HaveInput = 0; 566: signal(SIGIO, inputAvailable); 567: } 568: select(16, &ibits, &obits, (fd_set *) 0, 569: (block)? (struct timeval *)0:&TimeValue); 570: 571: #define FD_NEXT(p) (ffs((p)->fds_bits[0])-1) /* Until there's a better way */ 572: 573: if (FD_NEXT(&ibits) < 0 && FD_NEXT(&obits) < 0 && block) { 574: /* I don't like this, does it ever happen? */ 575: printf("sleep(5) from tn3270, after select\n"); 576: sleep(5); 577: return(0); 578: } 579: 580: /* 581: * Something to read from the network... 582: */ 583: if (FD_ISSET(net, &ibits)) { 584: scc = read(net, sibuf, sizeof (sibuf)); 585: if (scc < 0 && errno == EWOULDBLOCK) 586: scc = 0; 587: else { 588: if (scc <= 0) 589: return(-1); 590: sbp = sibuf; 591: if (printnet) { 592: Dump('<', sbp, scc); 593: } 594: returnValue = 1; /* did something useful */ 595: } 596: } 597: 598: /* 599: * Something to read from the tty... 600: */ 601: if (FD_ISSET(tin, &ibits)) { 602: tcc = read(tin, tibuf, sizeof tibuf); 603: if (tcc < 0 && errno == EWOULDBLOCK) 604: tcc = 0; 605: else { 606: if (tcc <= 0) 607: return(-1); 608: tbp = tibuf; 609: returnValue = 1; /* did something useful */ 610: } 611: } 612: 613: if (tcc > 0) { 614: if (In3270) { 615: c = DataFromTerminal(tbp, tcc); 616: if (c) { 617: returnValue = 1; /* did something useful */ 618: } 619: tcc -= c; 620: tbp += c; 621: } else { 622: returnValue = 1; /* did something useful */ 623: while (tcc > 0) { 624: if ((&netobuf[BUFSIZ] - nfrontp) < 2) 625: break; 626: c = *tbp++ & 0377, tcc--; 627: if (strip(c) == escape) { 628: command(0); 629: tcc = 0; 630: break; 631: } 632: if (c == IAC) 633: *nfrontp++ = c; 634: *nfrontp++ = c; 635: } 636: } 637: } 638: if (FD_ISSET(net, &obits) && (c = (int) (nfrontp - nbackp)) > 0) { 639: netflush(); 640: if (c != (int) (nfrontp-nbackp)) { 641: returnValue = 1; 642: } 643: } 644: if (scc > 0) { 645: if (Ifrontp+scc >= Ibuf+IBUFSIZ) { 646: if (Ibackp != Ibuf) { /* do some copying */ 647: bcopy(Ibackp, Ibuf, Ifrontp-Ibackp); 648: Ifrontp -= (Ibackp-Ibuf); 649: Ibackp = Ibuf; 650: } 651: } 652: if (Ifrontp+scc < Ibuf+IBUFSIZ) { 653: returnValue = 1; /* doing something useful */ 654: telrcv(); 655: } /* Else - we may never recover */ 656: } 657: if (FD_ISSET(tout, &obits) && (c = (int) (tfrontp - tbackp)) > 0) { 658: ttyflush(); 659: if (c != (int) (tfrontp-tbackp)) { 660: returnValue = 1; 661: } 662: } 663: if (In3270 && (c = (int) (Ifrontp-Ibackp))) { 664: Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, ISend); 665: if (c != (int) (Ifrontp-Ibackp)) { 666: returnValue = 1; 667: } 668: if (Ibackp == Ifrontp) { 669: Ibackp = Ifrontp = Ibuf; 670: ISend = 0; /* take data from network */ 671: } 672: } 673: return(returnValue); /* good return */ 674: } 675: 676: command(top) 677: int top; 678: { 679: register struct cmd *c; 680: int oldmode; 681: 682: oldmode = mode(0); 683: if (!top) 684: putchar('\n'); 685: else 686: signal(SIGINT, SIG_DFL); 687: for (;;) { 688: printf("%s> ", prompt); 689: if (gets(line) == 0) { 690: if (feof(stdin)) { 691: clearerr(stdin); 692: putchar('\n'); 693: } 694: break; 695: } 696: if (line[0] == 0) 697: break; 698: makeargv(); 699: c = getcmd(margv[0]); 700: if (c == (struct cmd *)-1) { 701: printf("?Ambiguous command\n"); 702: continue; 703: } 704: if (c == 0) { 705: printf("?Invalid command\n"); 706: continue; 707: } 708: (*c->handler)(margc, margv); 709: if (c->handler != help) 710: break; 711: } 712: if (!top) { 713: if (!connected) 714: longjmp(toplevel, 1); 715: (void) mode(oldmode); 716: } 717: } 718: 719: /* 720: * Telnet receiver states for fsm 721: */ 722: #define TS_DATA 0 723: #define TS_IAC 1 724: #define TS_WILL 2 725: #define TS_WONT 3 726: #define TS_DO 4 727: #define TS_DONT 5 728: #define TS_SB 6 /* in sub-negotiation */ 729: #define TS_SE 7 /* coming out of sub-negotiation */ 730: 731: #define SB_ACCUM(c) {*pSb = c; /* accumulate character */ \ 732: if (pSb >= SbBuffer+sizeof (SbBuffer)) { \ 733: /* can't accept any more */ \ 734: pSb = SbBuffer; \ 735: } \ 736: pSb++;} 737: 738: static 739: telrcv() 740: { 741: register int c; 742: register char *Sbp; 743: register int Scc; 744: static int state = TS_DATA; 745: 746: while (scc > 0) { 747: c = *sbp++ & 0377, scc--; 748: switch (state) { 749: 750: case TS_DATA: 751: if (c == IAC) { 752: state = TS_IAC; 753: continue; 754: } 755: /* We optimize this loop, since it is 756: * where we spend 99% of this routine. 757: */ 758: if (In3270) { 759: *Ifrontp++ = c; 760: Sbp = sbp; 761: Scc = scc; 762: while (Scc > 0) { 763: c = *Sbp++ & 0377, Scc--; 764: if (c == IAC) { 765: state = TS_IAC; 766: break; 767: } 768: *Ifrontp++ = c; 769: } 770: sbp = Sbp; 771: scc = Scc; 772: } else { 773: *tfrontp++ = c; 774: /* 775: * This hack is needed since we can't set 776: * CRMOD on output only. Machines like MULTICS 777: * like to send \r without \n; since we must 778: * turn off CRMOD to get proper input, the mapping 779: * is done here (sigh). 780: */ 781: if (c == '\r' && crmod && !In3270) 782: *tfrontp++ = '\n'; 783: } 784: continue; 785: 786: 787: case TS_IAC: 788: switch (c) { 789: 790: case WILL: 791: state = TS_WILL; 792: continue; 793: 794: case WONT: 795: state = TS_WONT; 796: continue; 797: 798: case DO: 799: state = TS_DO; 800: continue; 801: 802: case DONT: 803: state = TS_DONT; 804: continue; 805: 806: case DM: 807: outputPurge(); 808: break; 809: 810: case NOP: 811: case GA: 812: break; 813: 814: case SB: 815: state = TS_SB; 816: pSb = SbBuffer; /* where to collect */ 817: continue; 818: 819: case EOR: 820: if (In3270) { 821: Ibackp += DataFromNetwork(Ibackp, 822: Ifrontp-Ibackp, 1); 823: if (Ibackp == Ifrontp) { 824: Ibackp = Ifrontp = Ibuf; 825: ISend = 0; /* should have been! */ 826: } else { 827: ISend = 1; 828: } 829: } 830: break; 831: 832: case IAC: 833: if (In3270) { 834: *Ifrontp++ = IAC; 835: } else { 836: *tfrontp++ = IAC; 837: } 838: break; 839: 840: default: 841: break; 842: } 843: state = TS_DATA; 844: continue; 845: 846: case TS_WILL: 847: printoption("RCVD", will, c, !hisopts[c]); 848: if (!hisopts[c]) 849: willoption(c); 850: state = TS_DATA; 851: continue; 852: 853: case TS_WONT: 854: printoption("RCVD", wont, c, hisopts[c]); 855: if (hisopts[c]) 856: wontoption(c); 857: state = TS_DATA; 858: continue; 859: 860: case TS_DO: 861: printoption("RCVD", doopt, c, !myopts[c]); 862: if (!myopts[c]) 863: dooption(c); 864: state = TS_DATA; 865: continue; 866: 867: case TS_DONT: 868: printoption("RCVD", dont, c, myopts[c]); 869: if (myopts[c]) { 870: myopts[c] = 0; 871: if (c == TELOPT_BINARY) { 872: SetIn3270(); 873: } 874: sprintf(nfrontp, wont, c); 875: nfrontp += sizeof (wont) - 2; 876: printoption("SENT", wont, c); 877: } 878: state = TS_DATA; 879: continue; 880: case TS_SB: 881: if (c == IAC) { 882: state = TS_SE; 883: continue; 884: } 885: SB_ACCUM(c); 886: continue; 887: case TS_SE: 888: if (c != SE) { 889: if (c != IAC) { 890: SB_ACCUM(IAC); 891: } 892: SB_ACCUM(c); 893: state = TS_SB; 894: } else { 895: /* this is the end of the sub negotiation */ 896: /* we only allow a termtype, send, sub */ 897: if ((Sb_Option != TELOPT_TTYPE) || 898: (Sb_Command != TELQUAL_SEND)) { 899: /* what to do? XXX */ 900: } else { 901: /* send our type */ 902: SentTerminalType = 1; 903: SetIn3270(); 904: bcopy(sb_terminal, nfrontp, sizeof sb_terminal); 905: nfrontp += sizeof sb_terminal; 906: printoption("SENT", sb_terminal, 907: TELOPT_TTYPE); 908: } 909: state = TS_DATA; 910: } 911: } 912: } 913: } 914: 915: static 916: willoption(option) 917: int option; 918: { 919: char *fmt; 920: 921: switch (option) { 922: 923: case TELOPT_ECHO: 924: (void) mode(1); 925: 926: case TELOPT_BINARY: 927: hisopts[option] = 1; 928: SetIn3270(); 929: fmt = doopt; 930: break; 931: 932: case TELOPT_EOR: 933: case TELOPT_SGA: 934: hisopts[option] = 1; 935: fmt = doopt; 936: break; 937: 938: case TELOPT_TM: 939: fmt = dont; 940: break; 941: 942: default: 943: fmt = dont; 944: break; 945: } 946: sprintf(nfrontp, fmt, option); 947: nfrontp += sizeof (dont) - 2; 948: printoption("SENT", fmt, option); 949: } 950: 951: static 952: wontoption(option) 953: int option; 954: { 955: char *fmt; 956: 957: switch (option) { 958: 959: case TELOPT_BINARY: 960: hisopts[option] = 0; 961: SetIn3270(); 962: fmt = doopt; 963: break; 964: 965: case TELOPT_ECHO: 966: (void) mode(2); 967: 968: case TELOPT_SGA: 969: hisopts[option] = 0; 970: fmt = dont; 971: break; 972: 973: default: 974: fmt = dont; 975: } 976: sprintf(nfrontp, fmt, option); 977: nfrontp += sizeof (doopt) - 2; 978: printoption("SENT", fmt, option); 979: } 980: 981: static 982: dooption(option) 983: int option; 984: { 985: char *fmt; 986: 987: switch (option) { 988: 989: case TELOPT_TTYPE: 990: case TELOPT_BINARY: 991: myopts[option] = 1; 992: SetIn3270(); 993: fmt = will; 994: break; 995: 996: case TELOPT_TM: 997: fmt = wont; 998: break; 999: 1000: case TELOPT_ECHO: 1001: (void) mode(2); 1002: fmt = will; 1003: hisopts[option] = 0; 1004: break; 1005: 1006: case TELOPT_EOR: 1007: case TELOPT_SGA: 1008: fmt = will; 1009: break; 1010: 1011: default: 1012: fmt = wont; 1013: break; 1014: } 1015: sprintf(nfrontp, fmt, option); 1016: nfrontp += (sizeof dont)-2; 1017: printoption("SENT", fmt, option); 1018: } 1019: 1020: static 1021: SetIn3270() 1022: { 1023: if (SentTerminalType && myopts[TELOPT_BINARY] && hisopts[TELOPT_BINARY]) { 1024: if (!In3270) { 1025: In3270 = 1; 1026: OptInit(); /* initialize mappings */ 1027: /* initialize terminal key mapping */ 1028: (void) DataFromTerminal(ttyobuf, 0); 1029: (void) mode(3); 1030: } 1031: } else { 1032: if (In3270) { 1033: Stop3270(1); 1034: In3270 = 0; 1035: (void) mode(2); 1036: } 1037: } 1038: } 1039: 1040: /* 1041: * Set the escape character. 1042: */ 1043: static 1044: setescape(argc, argv) 1045: int argc; 1046: char *argv[]; 1047: { 1048: register char *arg; 1049: char buf[50]; 1050: 1051: if (argc > 2) 1052: arg = argv[1]; 1053: else { 1054: printf("new escape character: "); 1055: gets(buf); 1056: arg = buf; 1057: } 1058: if (arg[0] != '\0') 1059: escape = arg[0]; 1060: printf("Escape character is '%s'.\n", control(escape)); 1061: fflush(stdout); 1062: } 1063: 1064: /*VARARGS*/ 1065: static 1066: setoptions() 1067: { 1068: 1069: showoptions = !showoptions; 1070: printf("%s show option processing.\n", showoptions ? "Will" : "Wont"); 1071: fflush(stdout); 1072: } 1073: 1074: /*VARARGS*/ 1075: static 1076: setcrmod() 1077: { 1078: 1079: crmod = !crmod; 1080: printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont"); 1081: fflush(stdout); 1082: } 1083: 1084: /*VARARGS*/ 1085: static 1086: setdebug() 1087: { 1088: 1089: debug = !debug; 1090: printf("%s turn on socket level debugging.\n", 1091: debug ? "Will" : "Wont"); 1092: fflush(stdout); 1093: if (debug && net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 1094: perror("setsockopt (SO_DEBUG)"); 1095: } 1096: 1097: /*VARARGS*/ 1098: static 1099: SetPrintNet() 1100: { 1101: 1102: printnet = !printnet; 1103: printf("%s turn on printing of raw network traffic.\n", 1104: printnet ? "Will" : "Wont"); 1105: } 1106: 1107: /* 1108: * Construct a control character sequence 1109: * for a special character. 1110: */ 1111: static char * 1112: control(c) 1113: register int c; 1114: { 1115: static char buf[3]; 1116: 1117: if (c == 0177) 1118: return ("^?"); 1119: if (c >= 040) { 1120: buf[0] = c; 1121: buf[1] = 0; 1122: } else { 1123: buf[0] = '^'; 1124: buf[1] = '@'+c; 1125: buf[2] = 0; 1126: } 1127: return (buf); 1128: } 1129: 1130: static struct cmd * 1131: getcmd(name) 1132: register char *name; 1133: { 1134: register char *p, *q; 1135: register struct cmd *c, *found; 1136: register int nmatches, longest; 1137: 1138: longest = 0; 1139: nmatches = 0; 1140: found = 0; 1141: for (c = cmdtab; p = c->name; c++) { 1142: for (q = name; *q == *p++; q++) 1143: if (*q == 0) /* exact match? */ 1144: return (c); 1145: if (!*q) { /* the name was a prefix */ 1146: if (q - name > longest) { 1147: longest = q - name; 1148: nmatches = 1; 1149: found = c; 1150: } else if (q - name == longest) 1151: nmatches++; 1152: } 1153: } 1154: if (nmatches > 1) 1155: return ((struct cmd *)-1); 1156: return (found); 1157: } 1158: 1159: static 1160: deadpeer() 1161: { 1162: (void) mode(0); 1163: longjmp(peerdied, -1); 1164: } 1165: 1166: static 1167: intr() 1168: { 1169: (void) mode(0); 1170: longjmp(toplevel, -1); 1171: } 1172: 1173: static 1174: inputAvailable() 1175: { 1176: HaveInput = 1; 1177: } 1178: 1179: /* outputPurge() - get rid of all output destined for terminal. */ 1180: outputPurge() 1181: { 1182: ioctl(fileno(stdout), TIOCFLUSH, (char *)0); 1183: tbackp = tfrontp = ttyobuf; 1184: } 1185: 1186: ttyflush() 1187: { 1188: int n; 1189: 1190: if ((n = tfrontp - tbackp) > 0) 1191: n = write(tout, tbackp, n); 1192: if (n < 0) 1193: return; 1194: tbackp += n; 1195: if (tbackp == tfrontp) 1196: tbackp = tfrontp = ttyobuf; 1197: } 1198: 1199: /* TtyChars() - returns the number of characters in the TTY buffer */ 1200: TtyChars() 1201: { 1202: return(tfrontp-tbackp); 1203: } 1204: 1205: netflush() 1206: { 1207: int n; 1208: 1209: if ((n = nfrontp - nbackp) > 0) 1210: n = write(net, nbackp, n); 1211: if (n < 0) { 1212: if (errno != ENOBUFS && errno != EWOULDBLOCK) { 1213: (void) mode(0); 1214: perror(hostname); 1215: close(net); 1216: longjmp(peerdied, -1); 1217: /*NOTREACHED*/ 1218: } 1219: n = 0; 1220: } 1221: if (printnet) { 1222: Dump('>', nbackp, n); 1223: } 1224: nbackp += n; 1225: if (nbackp == nfrontp) 1226: nbackp = nfrontp = netobuf; 1227: } 1228: 1229: /* DataToNetwork - queue up some data to go to network. When last byte is 1230: queued, we add on an IAC EOR sequence (so, don't call us until you 1231: want that done...) 1232: */ 1233: 1234: int 1235: DataToNetwork(buffer, count) 1236: register char *buffer; /* where the data is */ 1237: register int count; /* how much to send */ 1238: { 1239: register int c; 1240: int origCount; 1241: 1242: origCount = count; 1243: 1244: while (count) { 1245: if ((&netobuf[sizeof netobuf] - nfrontp) < 6) { 1246: netflush(); 1247: if ((&netobuf[sizeof netobuf] - nfrontp) < 6) { 1248: break; 1249: } 1250: } 1251: c = *buffer++; 1252: count--; 1253: if (c == IAC) { 1254: *nfrontp++ = IAC; 1255: *nfrontp++ = IAC; 1256: } else { 1257: *nfrontp++ = c; 1258: } 1259: } 1260: 1261: if (!count) { 1262: *nfrontp++ = IAC; 1263: *nfrontp++ = EOR; 1264: netflush(); /* try to move along as quickly as ... */ 1265: } 1266: return(origCount - count); 1267: } 1268: 1269: /* DataToTerminal - queue up some data to go to terminal. */ 1270: 1271: int 1272: DataToTerminal(buffer, count) 1273: register char *buffer; /* where the data is */ 1274: register int count; /* how much to send */ 1275: { 1276: int origCount; 1277: fd_set o; 1278: 1279: origCount = count; 1280: 1281: while (count) { 1282: if (tfrontp >= &ttyobuf[sizeof ttyobuf]) { 1283: ttyflush(); 1284: while (tfrontp >= &ttyobuf[sizeof ttyobuf]) { 1285: FD_ZERO(&o); 1286: FD_SET(tout, &o); 1287: (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, 1288: (struct timeval *) 0); 1289: ttyflush(); 1290: } 1291: } 1292: *tfrontp++ = *buffer++; 1293: count--; 1294: } 1295: return(origCount - count); 1296: } 1297: 1298: /* EmptyTerminal - called to make sure that the terminal buffer is empty. 1299: * Note that we consider the buffer to run all the 1300: * way to the kernel (thus the select). 1301: */ 1302: 1303: void 1304: EmptyTerminal() 1305: { 1306: fd_set o; 1307: 1308: FD_ZERO(&o); 1309: FD_SET(tout, &o); 1310: 1311: if (tfrontp == tbackp) { 1312: (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, 1313: (struct timeval *) 0); /* wait for TTLOWAT */ 1314: } else { 1315: while (tfrontp != tbackp) { 1316: ttyflush(); 1317: (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, 1318: (struct timeval *) 0); /* wait for TTLOWAT */ 1319: } 1320: } 1321: } 1322: 1323: 1324: 1325: /* StringToTerminal - output a null terminated string to the terminal */ 1326: 1327: int 1328: StringToTerminal(s) 1329: char *s; 1330: { 1331: int count; 1332: 1333: count = strlen(s); 1334: if (count) { 1335: (void) DataToTerminal(s, count); /* we know it always goes... */ 1336: } 1337: } 1338: 1339: 1340: /* _putchar - output a single character to the terminal. This name is so that 1341: * curses(3x) can call us to send out data. 1342: */ 1343: 1344: _putchar(c) 1345: char c; 1346: { 1347: if (tfrontp >= &ttyobuf[sizeof ttyobuf]) { 1348: (void) DataToTerminal(&c, 1); 1349: } else { 1350: *tfrontp++ = c; /* optimize if possible. */ 1351: } 1352: } 1353: 1354: static 1355: SetForExit() 1356: { 1357: (void) mode(2); /* switch modes to flush output */ 1358: (void) mode(0); 1359: fflush(stdout); 1360: fflush(stderr); 1361: if (In3270) { 1362: Stop3270(0); 1363: } 1364: (void) mode(2); /* make sure we go back to mode 0 */ 1365: (void) mode(0); 1366: } 1367: 1368: static 1369: Exit(returnCode) 1370: int returnCode; 1371: { 1372: SetForExit(); 1373: exit(returnCode); 1374: } 1375: 1376: ExitString(file, string, returnCode) 1377: FILE *file; 1378: char *string; 1379: int returnCode; 1380: { 1381: SetForExit(); 1382: fwrite(string, 1, strlen(string), file); 1383: exit(returnCode); 1384: } 1385: 1386: ExitPerror(string, returnCode) 1387: char *string; 1388: int returnCode; 1389: { 1390: SetForExit(); 1391: perror(string); 1392: exit(returnCode); 1393: } 1394: 1395: 1396: static 1397: Dump(direction, buffer, length) 1398: char direction; 1399: char *buffer; 1400: int length; 1401: { 1402: # define BYTES_PER_LINE 32 1403: char *pThis; 1404: int offset; 1405: 1406: offset = 0; 1407: 1408: while (length) { 1409: /* print one line */ 1410: fprintf(NetTrace, "%c 0x%x\t", direction, offset); 1411: pThis = buffer; 1412: buffer = buffer+min(length, BYTES_PER_LINE); 1413: while (pThis < buffer) { 1414: fprintf(NetTrace, "%.2x", (*pThis)&0xff); 1415: pThis++; 1416: } 1417: fprintf(NetTrace, "\n"); 1418: length -= BYTES_PER_LINE; 1419: offset += BYTES_PER_LINE; 1420: if (length < 0) { 1421: return; 1422: } 1423: /* find next unique line */ 1424: } 1425: } 1426: 1427: 1428: 1429: /*VARARGS*/ 1430: static 1431: printoption(direction, fmt, option, what) 1432: char *direction, *fmt; 1433: int option, what; 1434: { 1435: if (!showoptions) 1436: return; 1437: printf("%s ", direction); 1438: if (fmt == doopt) 1439: fmt = "do"; 1440: else if (fmt == dont) 1441: fmt = "dont"; 1442: else if (fmt == will) 1443: fmt = "will"; 1444: else if (fmt == wont) 1445: fmt = "wont"; 1446: else if (fmt == sb_terminal) 1447: fmt = "will (terminal)"; 1448: else 1449: fmt = "???"; 1450: if (option < TELOPT_SUPDUP) 1451: printf("%s %s", fmt, telopts[option]); 1452: else 1453: printf("%s %d", fmt, option); 1454: if (*direction == '<') { 1455: printf("\r\n"); 1456: return; 1457: } 1458: printf(" (%s)\r\n", what ? "reply" : "don't reply"); 1459: }