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