1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #if defined(DOSCCS) && !defined(lint) 8: char copyright[] = 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: 12: static char sccsid[] = "@(#)telnet.c 5.16.1 (2.11BSD GTE) 1/1/94"; 13: #endif 14: 15: /* 16: * User telnet program. 17: * 18: * Many of the FUNCTIONAL changes in this newest version of telnet 19: * were suggested by Dave Borman of Cray Research, Inc. 20: */ 21: 22: #include <sys/types.h> 23: #include <sys/socket.h> 24: #include <sys/ioctl.h> 25: #include <sys/time.h> 26: 27: #include <netinet/in.h> 28: 29: #define TELOPTS 30: #include <arpa/telnet.h> 31: #include <arpa/inet.h> 32: 33: #include <stdio.h> 34: #include <ctype.h> 35: #include <errno.h> 36: #include <signal.h> 37: #include <setjmp.h> 38: #include <netdb.h> 39: #include <strings.h> 40: 41: 42: 43: #ifndef FD_SETSIZE 44: /* 45: * The following is defined just in case someone should want to run 46: * this telnet on a 4.2 system. 47: * 48: */ 49: 50: #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) 51: #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) 52: #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) 53: #define FD_ZERO(p) ((p)->fds_bits[0] = 0) 54: 55: #endif 56: 57: #define strip(x) ((x)&0x7f) 58: 59: char ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf; 60: #define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } } 61: #define TTYLOC() (tfrontp) 62: #define TTYMAX() (ttyobuf+sizeof ttyobuf-1) 63: #define TTYMIN() (netobuf) 64: #define TTYBYTES() (tfrontp-tbackp) 65: #define TTYROOM() (TTYMAX()-TTYLOC()+1) 66: 67: char netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 68: #define NETADD(c) { *nfrontp++ = c; } 69: #define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); } 70: #define NETLOC() (nfrontp) 71: #define NETMAX() (netobuf+sizeof netobuf-1) 72: #define NETBYTES() (nfrontp-nbackp) 73: #define NETROOM() (NETMAX()-NETLOC()+1) 74: char *neturg = 0; /* one past last byte of urgent data */ 75: 76: char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */ 77: #define SB_CLEAR() subpointer = subbuffer; 78: #define SB_TERM() subend = subpointer; 79: #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 80: *subpointer++ = (c); \ 81: } 82: 83: char hisopts[256]; 84: char myopts[256]; 85: 86: char doopt[] = { IAC, DO, '%', 'c', 0 }; 87: char dont[] = { IAC, DONT, '%', 'c', 0 }; 88: char will[] = { IAC, WILL, '%', 'c', 0 }; 89: char wont[] = { IAC, WONT, '%', 'c', 0 }; 90: 91: struct cmd { 92: char *name; /* command name */ 93: char *help; /* help string */ 94: int (*handler)(); /* routine which executes command */ 95: int dohelp; /* Should we give general help information? */ 96: int needconnect; /* Do we need to be connected to execute? */ 97: }; 98: 99: int connected; 100: int net; 101: int tout; 102: int showoptions = 0; 103: int debug = 0; 104: int crmod = 0; 105: int netdata = 0; 106: static FILE *NetTrace; 107: int telnetport = 1; 108: 109: 110: char *prompt; 111: char escape = CTRL(]); 112: char echoc = CTRL(E); 113: 114: int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 115: int flushout = 0; /* flush output */ 116: int autoflush = 0; /* flush output when interrupting? */ 117: int autosynch = 0; /* send interrupt characters with SYNCH? */ 118: int localchars = 0; /* we recognize interrupt/quit */ 119: int donelclchars = 0; /* the user has set "localchars" */ 120: int dontlecho = 0; /* do we suppress local echoing right now? */ 121: 122: char line[200]; 123: int margc; 124: char *margv[20]; 125: 126: jmp_buf toplevel; 127: jmp_buf peerdied; 128: 129: extern int errno; 130: 131: 132: struct sockaddr_in sin; 133: 134: struct cmd *getcmd(); 135: struct servent *sp; 136: 137: struct tchars otc, ntc; 138: struct ltchars oltc, nltc; 139: struct sgttyb ottyb, nttyb; 140: int globalmode = 0; 141: int flushline = 1; 142: 143: char *hostname; 144: char hnamebuf[32]; 145: 146: /* 147: * The following are some clocks used to decide how to interpret 148: * the relationship between various variables. 149: */ 150: 151: struct { 152: int 153: system, /* what the current time is */ 154: echotoggle, /* last time user entered echo character */ 155: modenegotiated, /* last time operating mode negotiated */ 156: didnetreceive, /* last time we read data from network */ 157: gotDM; /* when did we last see a data mark */ 158: } clocks; 159: 160: #define settimer(x) clocks.x = clocks.system++ 161: 162: /* 163: * Various utility routines. 164: */ 165: 166: char *ambiguous; /* special return value */ 167: #define Ambiguous(t) ((t)&ambiguous) 168: 169: 170: char ** 171: genget(name, table, next) 172: char *name; /* name to match */ 173: char **table; /* name entry in table */ 174: char **(*next)(); /* routine to return next entry in table */ 175: { 176: register char *p, *q; 177: register char **c, **found; 178: register int nmatches, longest; 179: 180: longest = 0; 181: nmatches = 0; 182: found = 0; 183: for (c = table; p = *c; c = (*next)(c)) { 184: for (q = name; *q == *p++; q++) 185: if (*q == 0) /* exact match? */ 186: return (c); 187: if (!*q) { /* the name was a prefix */ 188: if (q - name > longest) { 189: longest = q - name; 190: nmatches = 1; 191: found = c; 192: } else if (q - name == longest) 193: nmatches++; 194: } 195: } 196: if (nmatches > 1) 197: return Ambiguous(char **); 198: return (found); 199: } 200: 201: /* 202: * Make a character string into a number. 203: * 204: * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). 205: */ 206: 207: special(s) 208: register char *s; 209: { 210: register char c; 211: char b; 212: 213: switch (*s) { 214: case '^': 215: b = *++s; 216: if (b == '?') { 217: c = b | 0x40; /* DEL */ 218: } else { 219: c = b & 0x1f; 220: } 221: break; 222: default: 223: c = *s; 224: break; 225: } 226: return c; 227: } 228: 229: /* 230: * Construct a control character sequence 231: * for a special character. 232: */ 233: char * 234: control(c) 235: register int c; 236: { 237: static char buf[3]; 238: 239: if (c == 0x7f) 240: return ("^?"); 241: if (c == '\377') { 242: return "off"; 243: } 244: if (c >= 0x20) { 245: buf[0] = c; 246: buf[1] = 0; 247: } else { 248: buf[0] = '^'; 249: buf[1] = '@'+c; 250: buf[2] = 0; 251: } 252: return (buf); 253: } 254: 255: 256: /* 257: * upcase() 258: * 259: * Upcase (in place) the argument. 260: */ 261: 262: void 263: upcase(argument) 264: register char *argument; 265: { 266: register int c; 267: 268: while (c = *argument) { 269: if (islower(c)) { 270: *argument = toupper(c); 271: } 272: argument++; 273: } 274: } 275: 276: /* 277: * Check to see if any out-of-band data exists on a socket (for 278: * Telnet "synch" processing). 279: */ 280: 281: int 282: stilloob(s) 283: int s; /* socket number */ 284: { 285: static struct timeval timeout = { 0 }; 286: fd_set excepts; 287: int value; 288: 289: do { 290: FD_ZERO(&excepts); 291: FD_SET(s, &excepts); 292: value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 293: } while ((value == -1) && (errno = EINTR)); 294: 295: if (value < 0) { 296: perror("select"); 297: quit(); 298: } 299: if (FD_ISSET(s, &excepts)) { 300: return 1; 301: } else { 302: return 0; 303: } 304: } 305: 306: 307: /* 308: * netflush 309: * Send as much data as possible to the network, 310: * handling requests for urgent data. 311: */ 312: 313: 314: netflush(fd) 315: { 316: int n; 317: 318: if ((n = nfrontp - nbackp) > 0) { 319: if (!neturg) { 320: n = write(fd, nbackp, n); /* normal write */ 321: } else { 322: n = neturg - nbackp; 323: /* 324: * In 4.2 (and 4.3) systems, there is some question about 325: * what byte in a sendOOB operation is the "OOB" data. 326: * To make ourselves compatible, we only send ONE byte 327: * out of band, the one WE THINK should be OOB (though 328: * we really have more the TCP philosophy of urgent data 329: * rather than the Unix philosophy of OOB data). 330: */ 331: if (n > 1) { 332: n = send(fd, nbackp, n-1, 0); /* send URGENT all by itself */ 333: } else { 334: n = send(fd, nbackp, n, MSG_OOB); /* URGENT data */ 335: } 336: } 337: } 338: if (n < 0) { 339: if (errno != ENOBUFS && errno != EWOULDBLOCK) { 340: setcommandmode(); 341: perror(hostname); 342: close(fd); 343: neturg = 0; 344: longjmp(peerdied, -1); 345: /*NOTREACHED*/ 346: } 347: n = 0; 348: } 349: if (netdata && n) { 350: Dump('>', nbackp, n); 351: } 352: nbackp += n; 353: if (nbackp >= neturg) { 354: neturg = 0; 355: } 356: if (nbackp == nfrontp) { 357: nbackp = nfrontp = netobuf; 358: } 359: } 360: 361: /* 362: * nextitem() 363: * 364: * Return the address of the next "item" in the TELNET data 365: * stream. This will be the address of the next character if 366: * the current address is a user data character, or it will 367: * be the address of the character following the TELNET command 368: * if the current address is a TELNET IAC ("I Am a Command") 369: * character. 370: */ 371: 372: char * 373: nextitem(current) 374: char *current; 375: { 376: if ((*current&0xff) != IAC) { 377: return current+1; 378: } 379: switch (*(current+1)&0xff) { 380: case DO: 381: case DONT: 382: case WILL: 383: case WONT: 384: return current+3; 385: case SB: /* loop forever looking for the SE */ 386: { 387: register char *look = current+2; 388: 389: for (;;) { 390: if ((*look++&0xff) == IAC) { 391: if ((*look++&0xff) == SE) { 392: return look; 393: } 394: } 395: } 396: } 397: default: 398: return current+2; 399: } 400: } 401: /* 402: * netclear() 403: * 404: * We are about to do a TELNET SYNCH operation. Clear 405: * the path to the network. 406: * 407: * Things are a bit tricky since we may have sent the first 408: * byte or so of a previous TELNET command into the network. 409: * So, we have to scan the network buffer from the beginning 410: * until we are up to where we want to be. 411: * 412: * A side effect of what we do, just to keep things 413: * simple, is to clear the urgent data pointer. The principal 414: * caller should be setting the urgent data pointer AFTER calling 415: * us in any case. 416: */ 417: 418: netclear() 419: { 420: register char *thisitem, *next; 421: char *good; 422: #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 423: ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 424: 425: thisitem = netobuf; 426: 427: while ((next = nextitem(thisitem)) <= nbackp) { 428: thisitem = next; 429: } 430: 431: /* Now, thisitem is first before/at boundary. */ 432: 433: good = netobuf; /* where the good bytes go */ 434: 435: while (nfrontp > thisitem) { 436: if (wewant(thisitem)) { 437: int length; 438: 439: next = thisitem; 440: do { 441: next = nextitem(next); 442: } while (wewant(next) && (nfrontp > next)); 443: length = next-thisitem; 444: bcopy(thisitem, good, length); 445: good += length; 446: thisitem = next; 447: } else { 448: thisitem = nextitem(thisitem); 449: } 450: } 451: 452: nbackp = netobuf; 453: nfrontp = good; /* next byte to be sent */ 454: neturg = 0; 455: } 456: 457: /* 458: * Send as much data as possible to the terminal. 459: */ 460: 461: 462: ttyflush() 463: { 464: int n; 465: 466: if ((n = tfrontp - tbackp) > 0) { 467: if (!(SYNCHing||flushout)) { 468: n = write(tout, tbackp, n); 469: } else { 470: ioctl(fileno(stdout), TIOCFLUSH, (char *) 0); 471: /* we leave 'n' alone! */ 472: } 473: } 474: if (n < 0) { 475: return; 476: } 477: tbackp += n; 478: if (tbackp == tfrontp) { 479: tbackp = tfrontp = ttyobuf; 480: } 481: } 482: 483: /* 484: * Various signal handling routines. 485: */ 486: 487: deadpeer() 488: { 489: setcommandmode(); 490: longjmp(peerdied, -1); 491: } 492: 493: intr() 494: { 495: if (localchars) { 496: intp(); 497: return; 498: } 499: setcommandmode(); 500: longjmp(toplevel, -1); 501: } 502: 503: intr2() 504: { 505: if (localchars) { 506: sendbrk(); 507: return; 508: } 509: } 510: 511: doescape() 512: { 513: command(0); 514: } 515: 516: /* 517: * The following are routines used to print out debugging information. 518: */ 519: 520: 521: static 522: Dump(direction, buffer, length) 523: char direction; 524: char *buffer; 525: int length; 526: { 527: # define BYTES_PER_LINE 32 528: # define min(x,y) ((x<y)? x:y) 529: char *pThis; 530: int offset; 531: 532: offset = 0; 533: 534: while (length) { 535: /* print one line */ 536: fprintf(NetTrace, "%c 0x%x\t", direction, offset); 537: pThis = buffer; 538: buffer = buffer+min(length, BYTES_PER_LINE); 539: while (pThis < buffer) { 540: fprintf(NetTrace, "%02x", (*pThis)&0xff); 541: pThis++; 542: } 543: fprintf(NetTrace, "\n"); 544: length -= BYTES_PER_LINE; 545: offset += BYTES_PER_LINE; 546: if (length < 0) { 547: return; 548: } 549: /* find next unique line */ 550: } 551: } 552: 553: 554: /*VARARGS*/ 555: printoption(direction, fmt, option, what) 556: char *direction, *fmt; 557: int option, what; 558: { 559: if (!showoptions) 560: return; 561: printf("%s ", direction+1); 562: if (fmt == doopt) 563: fmt = "do"; 564: else if (fmt == dont) 565: fmt = "dont"; 566: else if (fmt == will) 567: fmt = "will"; 568: else if (fmt == wont) 569: fmt = "wont"; 570: else 571: fmt = "???"; 572: if (option < (sizeof telopts/sizeof telopts[0])) 573: printf("%s %s", fmt, telopts[option]); 574: else 575: printf("%s %d", fmt, option); 576: if (*direction == '<') { 577: printf("\r\n"); 578: return; 579: } 580: printf(" (%s)\r\n", what ? "reply" : "don't reply"); 581: } 582: 583: /* 584: * Mode - set up terminal to a specific mode. 585: */ 586: 587: 588: mode(f) 589: register int f; 590: { 591: static int prevmode = 0; 592: struct tchars *tc; 593: struct ltchars *ltc; 594: struct sgttyb sb; 595: int onoff, old; 596: struct tchars notc2; 597: struct ltchars noltc2; 598: static struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 599: static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 600: 601: globalmode = f; 602: if (prevmode == f) 603: return; 604: old = prevmode; 605: prevmode = f; 606: sb = nttyb; 607: switch (f) { 608: 609: case 0: 610: onoff = 0; 611: tc = &otc; 612: ltc = &oltc; 613: break; 614: 615: case 1: /* remote character processing, remote echo */ 616: case 2: /* remote character processing, local echo */ 617: sb.sg_flags |= CBREAK; 618: if (f == 1) 619: sb.sg_flags &= ~(ECHO|CRMOD); 620: else 621: sb.sg_flags |= ECHO|CRMOD; 622: sb.sg_erase = sb.sg_kill = -1; 623: tc = ¬c; 624: /* 625: * If user hasn't specified one way or the other, 626: * then default to not trapping signals. 627: */ 628: if (!donelclchars) { 629: localchars = 0; 630: } 631: if (localchars) { 632: notc2 = notc; 633: notc2.t_intrc = ntc.t_intrc; 634: notc2.t_quitc = ntc.t_quitc; 635: tc = ¬c2; 636: } else 637: tc = ¬c; 638: ltc = &noltc; 639: onoff = 1; 640: break; 641: case 3: /* local character processing, remote echo */ 642: case 4: /* local character processing, local echo */ 643: case 5: /* local character processing, no echo */ 644: sb.sg_flags &= ~CBREAK; 645: sb.sg_flags |= CRMOD; 646: if (f == 4) 647: sb.sg_flags |= ECHO; 648: else 649: sb.sg_flags &= ~ECHO; 650: notc2 = ntc; 651: tc = ¬c2; 652: noltc2 = oltc; 653: ltc = &noltc2; 654: /* 655: * If user hasn't specified one way or the other, 656: * then default to trapping signals. 657: */ 658: if (!donelclchars) { 659: localchars = 1; 660: } 661: if (localchars) { 662: notc2.t_brkc = nltc.t_flushc; 663: noltc2.t_flushc = -1; 664: } else { 665: notc2.t_intrc = notc2.t_quitc = -1; 666: } 667: noltc2.t_suspc = escape; 668: noltc2.t_dsuspc = -1; 669: onoff = 1; 670: break; 671: 672: default: 673: return; 674: } 675: ioctl(fileno(stdin), TIOCSLTC, (char *)ltc); 676: ioctl(fileno(stdin), TIOCSETC, (char *)tc); 677: ioctl(fileno(stdin), TIOCSETP, (char *)&sb); 678: ioctl(fileno(stdin), FIONBIO, (char *)&onoff); 679: ioctl(fileno(stdout), FIONBIO, (char *)&onoff); 680: if (f >= 3) 681: signal(SIGTSTP, doescape); 682: else if (old >= 3) { 683: signal(SIGTSTP, SIG_DFL); 684: sigsetmask(sigblock(0L) & ~(1L<<(SIGTSTP-1))); 685: } 686: } 687: 688: /* 689: * These routines decides on what the mode should be (based on the values 690: * of various global variables). 691: */ 692: 693: char *modedescriptions[] = { 694: "telnet command mode", /* 0 */ 695: "character-at-a-time mode", /* 1 */ 696: "character-at-a-time mode (local echo)", /* 2 */ 697: "line-by-line mode (remote echo)", /* 3 */ 698: "line-by-line mode", /* 4 */ 699: "line-by-line mode (local echoing suppressed)", /* 5 */ 700: }; 701: 702: getconnmode() 703: { 704: static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 }; 705: int modeindex = 0; 706: 707: if (hisopts[TELOPT_ECHO]) { 708: modeindex += 2; 709: } 710: if (hisopts[TELOPT_SGA]) { 711: modeindex += 4; 712: } 713: if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) { 714: modeindex += 1; 715: } 716: return newmode[modeindex]; 717: } 718: 719: setconnmode() 720: { 721: mode(getconnmode()); 722: } 723: 724: 725: setcommandmode() 726: { 727: mode(0); 728: } 729: 730: char sibuf[BUFSIZ], *sbp; 731: char tibuf[BUFSIZ], *tbp; 732: int scc, tcc; 733: 734: 735: /* 736: * Select from tty and network... 737: */ 738: telnet() 739: { 740: register int c; 741: int tin = fileno(stdin); 742: int on = 1; 743: fd_set ibits, obits, xbits; 744: 745: tout = fileno(stdout); 746: setconnmode(); 747: scc = 0; 748: tcc = 0; 749: FD_ZERO(&ibits); 750: FD_ZERO(&obits); 751: FD_ZERO(&xbits); 752: 753: ioctl(net, FIONBIO, (char *)&on); 754: #if defined(SO_OOBINLINE) 755: setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 756: #endif /* defined(SO_OOBINLINE) */ 757: if (telnetport) { 758: if (!hisopts[TELOPT_SGA]) { 759: willoption(TELOPT_SGA, 0); 760: } 761: if (!myopts[TELOPT_TTYPE]) { 762: dooption(TELOPT_TTYPE, 0); 763: } 764: } 765: for (;;) { 766: if (scc < 0 && tcc < 0) { 767: break; 768: } 769: 770: if (((globalmode < 4) || flushline) && NETBYTES()) { 771: FD_SET(net, &obits); 772: } else { 773: FD_SET(tin, &ibits); 774: } 775: if (TTYBYTES()) { 776: FD_SET(tout, &obits); 777: } else { 778: FD_SET(net, &ibits); 779: } 780: if (!SYNCHing) { 781: FD_SET(net, &xbits); 782: } 783: if ((c = select(16, &ibits, &obits, &xbits, 784: (struct timeval *)0)) < 1) { 785: if (c == -1) { 786: /* 787: * we can get EINTR if we are in line mode, 788: * and the user does an escape (TSTP), or 789: * some other signal generator. 790: */ 791: if (errno == EINTR) { 792: continue; 793: } 794: } 795: sleep(5); 796: continue; 797: } 798: 799: /* 800: * Any urgent data? 801: */ 802: if (FD_ISSET(net, &xbits)) { 803: FD_CLR(net, &xbits); 804: SYNCHing = 1; 805: ttyflush(); /* flush already enqueued data */ 806: } 807: 808: /* 809: * Something to read from the network... 810: */ 811: if (FD_ISSET(net, &ibits)) { 812: int canread; 813: 814: FD_CLR(net, &ibits); 815: if (scc == 0) { 816: sbp = sibuf; 817: } 818: canread = sibuf + sizeof sibuf - sbp; 819: #if !defined(SO_OOBINLINE) 820: /* 821: * In 4.2 (and some early 4.3) systems, the 822: * OOB indication and data handling in the kernel 823: * is such that if two separate TCP Urgent requests 824: * come in, one byte of TCP data will be overlaid. 825: * This is fatal for Telnet, but we try to live 826: * with it. 827: * 828: * In addition, in 4.2 (and...), a special protocol 829: * is needed to pick up the TCP Urgent data in 830: * the correct sequence. 831: * 832: * What we do is: if we think we are in urgent 833: * mode, we look to see if we are "at the mark". 834: * If we are, we do an OOB receive. If we run 835: * this twice, we will do the OOB receive twice, 836: * but the second will fail, since the second 837: * time we were "at the mark", but there wasn't 838: * any data there (the kernel doesn't reset 839: * "at the mark" until we do a normal read). 840: * Once we've read the OOB data, we go ahead 841: * and do normal reads. 842: * 843: * There is also another problem, which is that 844: * since the OOB byte we read doesn't put us 845: * out of OOB state, and since that byte is most 846: * likely the TELNET DM (data mark), we would 847: * stay in the TELNET SYNCH (SYNCHing) state. 848: * So, clocks to the rescue. If we've "just" 849: * received a DM, then we test for the 850: * presence of OOB data when the receive OOB 851: * fails (and AFTER we did the normal mode read 852: * to clear "at the mark"). 853: */ 854: if (SYNCHing) { 855: int atmark; 856: 857: ioctl(net, SIOCATMARK, (char *)&atmark); 858: if (atmark) { 859: c = recv(net, sibuf, canread, MSG_OOB); 860: if ((c == -1) && (errno == EINVAL)) { 861: c = read(net, sibuf, canread); 862: if (clocks.didnetreceive < clocks.gotDM) { 863: SYNCHing = stilloob(net); 864: } 865: } 866: } else { 867: c = read(net, sibuf, canread); 868: } 869: } else { 870: c = read(net, sibuf, canread); 871: } 872: settimer(didnetreceive); 873: #else /* !defined(SO_OOBINLINE) */ 874: c = read(net, sbp, canread); 875: #endif /* !defined(SO_OOBINLINE) */ 876: if (c < 0 && errno == EWOULDBLOCK) { 877: c = 0; 878: } else if (c <= 0) { 879: break; 880: } 881: if (netdata) { 882: Dump('<', sbp, c); 883: } 884: scc += c; 885: } 886: 887: /* 888: * Something to read from the tty... 889: */ 890: if (FD_ISSET(tin, &ibits)) { 891: FD_CLR(tin, &ibits); 892: if (tcc == 0) { 893: tbp = tibuf; /* nothing left, reset */ 894: } 895: c = read(tin, tbp, tibuf+sizeof tibuf - tbp); 896: if (c < 0 && errno == EWOULDBLOCK) { 897: c = 0; 898: } else { 899: /* EOF detection for line mode!!!! */ 900: if (c == 0 && globalmode >= 3) { 901: /* must be an EOF... */ 902: *tbp = ntc.t_eofc; 903: c = 1; 904: } 905: if (c <= 0) { 906: tcc = c; 907: break; 908: } 909: } 910: tcc += c; 911: } 912: 913: while (tcc > 0) { 914: register int sc; 915: 916: if (NETROOM() < 2) { 917: flushline = 1; 918: break; 919: } 920: c = *tbp++ & 0xff, sc = strip(c), tcc--; 921: if (sc == escape) { 922: command(0); 923: tcc = 0; 924: flushline = 1; 925: break; 926: } else if ((globalmode >= 4) && (sc == echoc)) { 927: if (tcc > 0 && strip(*tbp) == echoc) { 928: tbp++; 929: tcc--; 930: } else { 931: dontlecho = !dontlecho; 932: settimer(echotoggle); 933: setconnmode(); 934: tcc = 0; 935: flushline = 1; 936: break; 937: } 938: } 939: if (localchars) { 940: if (sc == ntc.t_intrc) { 941: intp(); 942: break; 943: } else if (sc == ntc.t_quitc) { 944: sendbrk(); 945: break; 946: } else if (sc == nltc.t_flushc) { 947: NET2ADD(IAC, AO); 948: if (autoflush) { 949: doflush(); 950: } 951: break; 952: } else if (globalmode > 2) { 953: ; 954: } else if (sc == nttyb.sg_kill) { 955: NET2ADD(IAC, EL); 956: break; 957: } else if (sc == nttyb.sg_erase) { 958: NET2ADD(IAC, EC); 959: break; 960: } 961: } 962: switch (c) { 963: case '\n': 964: /* 965: * If we are in CRMOD mode (\r ==> \n) 966: * on our local machine, then probably 967: * a newline (unix) is CRLF (TELNET). 968: */ 969: if (globalmode >= 3) { 970: NETADD('\r'); 971: } 972: NETADD('\n'); 973: flushline = 1; 974: break; 975: case '\r': 976: NET2ADD('\r', '\0'); 977: flushline = 1; 978: break; 979: case IAC: 980: NET2ADD(IAC, IAC); 981: break; 982: default: 983: NETADD(c); 984: break; 985: } 986: } 987: if (((globalmode < 4) || flushline) && 988: FD_ISSET(net, &obits) && (NETBYTES() > 0)) { 989: FD_CLR(net, &obits); 990: netflush(net); 991: } 992: if (scc > 0) 993: telrcv(); 994: if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) { 995: FD_CLR(tout, &obits); 996: ttyflush(); 997: } 998: } 999: setcommandmode(); 1000: } 1001: 1002: /* 1003: * Telnet receiver states for fsm 1004: */ 1005: #define TS_DATA 0 1006: #define TS_IAC 1 1007: #define TS_WILL 2 1008: #define TS_WONT 3 1009: #define TS_DO 4 1010: #define TS_DONT 5 1011: #define TS_CR 6 1012: #define TS_SB 7 /* sub-option collection */ 1013: #define TS_SE 8 /* looking for sub-option end */ 1014: 1015: telrcv() 1016: { 1017: register int c; 1018: static int state = TS_DATA; 1019: 1020: while ((scc > 0) && (TTYROOM() > 2)) { 1021: c = *sbp++ & 0xff, scc--; 1022: switch (state) { 1023: 1024: case TS_CR: 1025: state = TS_DATA; 1026: if (c == '\0') { 1027: break; /* Ignore \0 after CR */ 1028: } else if (c == '\n') { 1029: if (hisopts[TELOPT_ECHO] && !crmod) { 1030: TTYADD(c); 1031: } 1032: break; 1033: } 1034: /* Else, fall through */ 1035: 1036: case TS_DATA: 1037: if (c == IAC) { 1038: state = TS_IAC; 1039: continue; 1040: } 1041: /* 1042: * The 'crmod' hack (see following) is needed 1043: * since we can't * set CRMOD on output only. 1044: * Machines like MULTICS like to send \r without 1045: * \n; since we must turn off CRMOD to get proper 1046: * input, the mapping is done here (sigh). 1047: */ 1048: if (c == '\r') { 1049: if (scc > 0) { 1050: c = *sbp&0xff; 1051: if (c == 0) { 1052: sbp++, scc--; 1053: /* a "true" CR */ 1054: TTYADD('\r'); 1055: } else if (!hisopts[TELOPT_ECHO] && 1056: (c == '\n')) { 1057: sbp++, scc--; 1058: TTYADD('\n'); 1059: } else { 1060: TTYADD('\r'); 1061: if (crmod) { 1062: TTYADD('\n'); 1063: } 1064: } 1065: } else { 1066: state = TS_CR; 1067: TTYADD('\r'); 1068: if (crmod) { 1069: TTYADD('\n'); 1070: } 1071: } 1072: } else { 1073: TTYADD(c); 1074: } 1075: continue; 1076: 1077: case TS_IAC: 1078: switch (c) { 1079: 1080: case WILL: 1081: state = TS_WILL; 1082: continue; 1083: 1084: case WONT: 1085: state = TS_WONT; 1086: continue; 1087: 1088: case DO: 1089: state = TS_DO; 1090: continue; 1091: 1092: case DONT: 1093: state = TS_DONT; 1094: continue; 1095: 1096: case DM: 1097: /* 1098: * We may have missed an urgent notification, 1099: * so make sure we flush whatever is in the 1100: * buffer currently. 1101: */ 1102: SYNCHing = 1; 1103: ttyflush(); 1104: SYNCHing = stilloob(net); 1105: settimer(gotDM); 1106: break; 1107: 1108: case NOP: 1109: case GA: 1110: break; 1111: 1112: case SB: 1113: SB_CLEAR(); 1114: state = TS_SB; 1115: continue; 1116: 1117: default: 1118: break; 1119: } 1120: state = TS_DATA; 1121: continue; 1122: 1123: case TS_WILL: 1124: printoption(">RCVD", will, c, !hisopts[c]); 1125: if (c == TELOPT_TM) { 1126: if (flushout) { 1127: flushout = 0; 1128: } 1129: } else if (!hisopts[c]) { 1130: willoption(c, 1); 1131: } 1132: state = TS_DATA; 1133: continue; 1134: 1135: case TS_WONT: 1136: printoption(">RCVD", wont, c, hisopts[c]); 1137: if (c == TELOPT_TM) { 1138: if (flushout) { 1139: flushout = 0; 1140: } 1141: } else if (hisopts[c]) { 1142: wontoption(c, 1); 1143: } 1144: state = TS_DATA; 1145: continue; 1146: 1147: case TS_DO: 1148: printoption(">RCVD", doopt, c, !myopts[c]); 1149: if (!myopts[c]) 1150: dooption(c); 1151: state = TS_DATA; 1152: continue; 1153: 1154: case TS_DONT: 1155: printoption(">RCVD", dont, c, myopts[c]); 1156: if (myopts[c]) { 1157: myopts[c] = 0; 1158: sprintf(nfrontp, wont, c); 1159: nfrontp += sizeof (wont) - 2; 1160: flushline = 1; 1161: setconnmode(); /* set new tty mode (maybe) */ 1162: printoption(">SENT", wont, c); 1163: } 1164: state = TS_DATA; 1165: continue; 1166: case TS_SB: 1167: if (c == IAC) { 1168: state = TS_SE; 1169: } else { 1170: SB_ACCUM(c); 1171: } 1172: continue; 1173: 1174: case TS_SE: 1175: if (c != SE) { 1176: if (c != IAC) { 1177: SB_ACCUM(IAC); 1178: } 1179: SB_ACCUM(c); 1180: state = TS_SB; 1181: } else { 1182: SB_TERM(); 1183: suboption(); /* handle sub-option */ 1184: state = TS_DATA; 1185: } 1186: } 1187: } 1188: } 1189: 1190: willoption(option, reply) 1191: int option, reply; 1192: { 1193: char *fmt; 1194: 1195: switch (option) { 1196: 1197: case TELOPT_ECHO: 1198: case TELOPT_SGA: 1199: settimer(modenegotiated); 1200: hisopts[option] = 1; 1201: fmt = doopt; 1202: setconnmode(); /* possibly set new tty mode */ 1203: break; 1204: 1205: case TELOPT_TM: 1206: return; /* Never reply to TM will's/wont's */ 1207: 1208: default: 1209: fmt = dont; 1210: break; 1211: } 1212: sprintf(nfrontp, fmt, option); 1213: nfrontp += sizeof (dont) - 2; 1214: if (reply) 1215: printoption(">SENT", fmt, option); 1216: else 1217: printoption("<SENT", fmt, option); 1218: } 1219: 1220: wontoption(option, reply) 1221: int option, reply; 1222: { 1223: char *fmt; 1224: 1225: switch (option) { 1226: 1227: case TELOPT_ECHO: 1228: case TELOPT_SGA: 1229: settimer(modenegotiated); 1230: hisopts[option] = 0; 1231: fmt = dont; 1232: setconnmode(); /* Set new tty mode */ 1233: break; 1234: 1235: case TELOPT_TM: 1236: return; /* Never reply to TM will's/wont's */ 1237: 1238: default: 1239: fmt = dont; 1240: } 1241: sprintf(nfrontp, fmt, option); 1242: nfrontp += sizeof (doopt) - 2; 1243: if (reply) 1244: printoption(">SENT", fmt, option); 1245: else 1246: printoption("<SENT", fmt, option); 1247: } 1248: 1249: dooption(option) 1250: int option; 1251: { 1252: char *fmt; 1253: 1254: switch (option) { 1255: 1256: case TELOPT_TM: 1257: fmt = will; 1258: break; 1259: 1260: case TELOPT_TTYPE: /* terminal type option */ 1261: case TELOPT_SGA: /* no big deal */ 1262: fmt = will; 1263: myopts[option] = 1; 1264: break; 1265: 1266: case TELOPT_ECHO: /* We're never going to echo... */ 1267: default: 1268: fmt = wont; 1269: break; 1270: } 1271: sprintf(nfrontp, fmt, option); 1272: nfrontp += sizeof (doopt) - 2; 1273: printoption(">SENT", fmt, option); 1274: } 1275: 1276: /* 1277: * suboption() 1278: * 1279: * Look at the sub-option buffer, and try to be helpful to the other 1280: * side. 1281: * 1282: * Currently we recognize: 1283: * 1284: * Terminal type, send request. 1285: */ 1286: 1287: suboption() 1288: { 1289: switch (subbuffer[0]&0xff) { 1290: case TELOPT_TTYPE: 1291: if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 1292: ; 1293: } else { 1294: char *name; 1295: char namebuf[41]; 1296: char *getenv(); 1297: int len; 1298: 1299: name = getenv("TERM"); 1300: if ((name == 0) || ((len = strlen(name)) > 40)) { 1301: name = "UNKNOWN"; 1302: } 1303: if ((len + 4+2) < NETROOM()) { 1304: strcpy(namebuf, name); 1305: upcase(namebuf); 1306: sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 1307: TELQUAL_IS, namebuf, IAC, SE); 1308: nfrontp += 4+strlen(namebuf)+2; 1309: } 1310: } 1311: 1312: default: 1313: break; 1314: } 1315: } 1316: 1317: /* 1318: * The following are data structures and routines for 1319: * the "send" command. 1320: * 1321: */ 1322: 1323: struct sendlist { 1324: char *name; /* How user refers to it (case independent) */ 1325: int what; /* Character to be sent (<0 ==> special) */ 1326: char *help; /* Help information (0 ==> no help) */ 1327: int (*routine)(); /* Routine to perform (for special ops) */ 1328: }; 1329: 1330: /*ARGSUSED*/ 1331: dosynch(s) 1332: struct sendlist *s; 1333: { 1334: netclear(); /* clear the path to the network */ 1335: NET2ADD(IAC, DM); 1336: neturg = NETLOC()-1; /* Some systems are off by one XXX */ 1337: } 1338: 1339: doflush() 1340: { 1341: NET2ADD(IAC, DO); 1342: NETADD(TELOPT_TM); 1343: flushline = 1; 1344: flushout = 1; 1345: ttyflush(); 1346: /* do printoption AFTER flush, otherwise the output gets tossed... */ 1347: printoption("<SENT", doopt, TELOPT_TM); 1348: } 1349: 1350: intp() 1351: { 1352: NET2ADD(IAC, IP); 1353: if (autoflush) { 1354: doflush(); 1355: } 1356: if (autosynch) { 1357: dosynch(); 1358: } 1359: } 1360: 1361: sendbrk() 1362: { 1363: NET2ADD(IAC, BREAK); 1364: if (autoflush) { 1365: doflush(); 1366: } 1367: if (autosynch) { 1368: dosynch(); 1369: } 1370: } 1371: 1372: 1373: #define SENDQUESTION -1 1374: #define SENDESCAPE -3 1375: 1376: struct sendlist Sendlist[] = { 1377: { "ao", AO, "Send Telnet Abort output" }, 1378: { "ayt", AYT, "Send Telnet 'Are You There'" }, 1379: { "brk", BREAK, "Send Telnet Break" }, 1380: { "ec", EC, "Send Telnet Erase Character" }, 1381: { "el", EL, "Send Telnet Erase Line" }, 1382: { "escape", SENDESCAPE, "Send current escape character" }, 1383: { "ga", GA, "Send Telnet 'Go Ahead' sequence" }, 1384: { "ip", IP, "Send Telnet Interrupt Process" }, 1385: { "nop", NOP, "Send Telnet 'No operation'" }, 1386: { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch }, 1387: { "?", SENDQUESTION, "Display send options" }, 1388: { 0 } 1389: }; 1390: 1391: struct sendlist Sendlist2[] = { /* some synonyms */ 1392: { "break", BREAK, 0 }, 1393: 1394: { "intp", IP, 0 }, 1395: { "interrupt", IP, 0 }, 1396: { "intr", IP, 0 }, 1397: 1398: { "help", SENDQUESTION, 0 }, 1399: 1400: { 0 } 1401: }; 1402: 1403: char ** 1404: getnextsend(name) 1405: char *name; 1406: { 1407: struct sendlist *c = (struct sendlist *) name; 1408: 1409: return (char **) (c+1); 1410: } 1411: 1412: struct sendlist * 1413: getsend(name) 1414: char *name; 1415: { 1416: struct sendlist *sl; 1417: 1418: if (sl = (struct sendlist *) 1419: genget(name, (char **) Sendlist, getnextsend)) { 1420: return sl; 1421: } else { 1422: return (struct sendlist *) 1423: genget(name, (char **) Sendlist2, getnextsend); 1424: } 1425: } 1426: 1427: sendcmd(argc, argv) 1428: int argc; 1429: char **argv; 1430: { 1431: int what; /* what we are sending this time */ 1432: int count; /* how many bytes we are going to need to send */ 1433: int hadsynch; /* are we going to process a "synch"? */ 1434: int i; 1435: int question = 0; /* was at least one argument a question */ 1436: struct sendlist *s; /* pointer to current command */ 1437: 1438: if (argc < 2) { 1439: printf("need at least one argument for 'send' command\n"); 1440: printf("'send ?' for help\n"); 1441: return 0; 1442: } 1443: /* 1444: * First, validate all the send arguments. 1445: * In addition, we see how much space we are going to need, and 1446: * whether or not we will be doing a "SYNCH" operation (which 1447: * flushes the network queue). 1448: */ 1449: count = 0; 1450: hadsynch = 0; 1451: for (i = 1; i < argc; i++) { 1452: s = getsend(argv[i]); 1453: if (s == 0) { 1454: printf("Unknown send argument '%s'\n'send ?' for help.\n", 1455: argv[i]); 1456: return 0; 1457: } else if (s == Ambiguous(struct sendlist *)) { 1458: printf("Ambiguous send argument '%s'\n'send ?' for help.\n", 1459: argv[i]); 1460: return 0; 1461: } 1462: switch (s->what) { 1463: case SENDQUESTION: 1464: break; 1465: case SENDESCAPE: 1466: count += 1; 1467: break; 1468: case SYNCH: 1469: hadsynch = 1; 1470: count += 2; 1471: break; 1472: default: 1473: count += 2; 1474: break; 1475: } 1476: } 1477: /* Now, do we have enough room? */ 1478: if (NETROOM() < count) { 1479: printf("There is not enough room in the buffer TO the network\n"); 1480: printf("to process your request. Nothing will be done.\n"); 1481: printf("('send synch' will throw away most data in the network\n"); 1482: printf("buffer, if this might help.)\n"); 1483: return 0; 1484: } 1485: /* OK, they are all OK, now go through again and actually send */ 1486: for (i = 1; i < argc; i++) { 1487: if (!(s = getsend(argv[i]))) { 1488: fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); 1489: quit(); 1490: /*NOTREACHED*/ 1491: } 1492: if (s->routine) { 1493: (*s->routine)(s); 1494: } else { 1495: switch (what = s->what) { 1496: case SYNCH: 1497: dosynch(); 1498: break; 1499: case SENDQUESTION: 1500: for (s = Sendlist; s->name; s++) { 1501: if (s->help) { 1502: printf(s->name); 1503: if (s->help) { 1504: printf("\t%s", s->help); 1505: } 1506: printf("\n"); 1507: } 1508: } 1509: question = 1; 1510: break; 1511: case SENDESCAPE: 1512: NETADD(escape); 1513: break; 1514: default: 1515: NET2ADD(IAC, what); 1516: break; 1517: } 1518: } 1519: } 1520: return !question; 1521: } 1522: 1523: /* 1524: * The following are the routines and data structures referred 1525: * to by the arguments to the "toggle" command. 1526: */ 1527: 1528: lclchars() 1529: { 1530: donelclchars = 1; 1531: return 1; 1532: } 1533: 1534: togdebug() 1535: { 1536: #ifndef NOT43 1537: if (net > 0 && 1538: setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug)) 1539: < 0) { 1540: perror("setsockopt (SO_DEBUG)"); 1541: } 1542: #else NOT43 1543: if (debug) { 1544: if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 1545: perror("setsockopt (SO_DEBUG)"); 1546: } else 1547: printf("Cannot turn off socket debugging\n"); 1548: #endif NOT43 1549: return 1; 1550: } 1551: 1552: 1553: 1554: int togglehelp(); 1555: 1556: struct togglelist { 1557: char *name; /* name of toggle */ 1558: char *help; /* help message */ 1559: int (*handler)(); /* routine to do actual setting */ 1560: int dohelp; /* should we display help information */ 1561: int *variable; 1562: char *actionexplanation; 1563: }; 1564: 1565: struct togglelist Togglelist[] = { 1566: { "autoflush", 1567: "toggle flushing of output when sending interrupt characters", 1568: 0, 1569: 1, 1570: &autoflush, 1571: "flush output when sending interrupt characters" }, 1572: { "autosynch", 1573: "toggle automatic sending of interrupt characters in urgent mode", 1574: 0, 1575: 1, 1576: &autosynch, 1577: "send interrupt characters in urgent mode" }, 1578: { "crmod", 1579: "toggle mapping of received carriage returns", 1580: 0, 1581: 1, 1582: &crmod, 1583: "map carriage return on output" }, 1584: { "localchars", 1585: "toggle local recognition of certain control characters", 1586: lclchars, 1587: 1, 1588: &localchars, 1589: "recognize certain control characters" }, 1590: { " ", "", 0, 1 }, /* empty line */ 1591: { "debug", 1592: "(debugging) toggle debugging", 1593: togdebug, 1594: 1, 1595: &debug, 1596: "turn on socket level debugging" }, 1597: { "netdata", 1598: "(debugging) toggle printing of hexadecimal network data", 1599: 0, 1600: 1, 1601: &netdata, 1602: "print hexadecimal representation of network traffic" }, 1603: { "options", 1604: "(debugging) toggle viewing of options processing", 1605: 0, 1606: 1, 1607: &showoptions, 1608: "show option processing" }, 1609: { " ", "", 0, 1 }, /* empty line */ 1610: { "?", 1611: "display help information", 1612: togglehelp, 1613: 1 }, 1614: { "help", 1615: "display help information", 1616: togglehelp, 1617: 0 }, 1618: { 0 } 1619: }; 1620: 1621: togglehelp() 1622: { 1623: struct togglelist *c; 1624: 1625: for (c = Togglelist; c->name; c++) { 1626: if (c->dohelp) { 1627: printf("%s\t%s\n", c->name, c->help); 1628: } 1629: } 1630: return 0; 1631: } 1632: 1633: char ** 1634: getnexttoggle(name) 1635: char *name; 1636: { 1637: struct togglelist *c = (struct togglelist *) name; 1638: 1639: return (char **) (c+1); 1640: } 1641: 1642: struct togglelist * 1643: gettoggle(name) 1644: char *name; 1645: { 1646: return (struct togglelist *) 1647: genget(name, (char **) Togglelist, getnexttoggle); 1648: } 1649: 1650: toggle(argc, argv) 1651: int argc; 1652: char *argv[]; 1653: { 1654: int retval = 1; 1655: char *name; 1656: struct togglelist *c; 1657: 1658: if (argc < 2) { 1659: fprintf(stderr, 1660: "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); 1661: return 0; 1662: } 1663: argc--; 1664: argv++; 1665: while (argc--) { 1666: name = *argv++; 1667: c = gettoggle(name); 1668: if (c == Ambiguous(struct togglelist *)) { 1669: fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", 1670: name); 1671: return 0; 1672: } else if (c == 0) { 1673: fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", 1674: name); 1675: return 0; 1676: } else { 1677: if (c->variable) { 1678: *c->variable = !*c->variable; /* invert it */ 1679: printf("%s %s.\n", *c->variable? "Will" : "Won't", 1680: c->actionexplanation); 1681: } 1682: if (c->handler) { 1683: retval &= (*c->handler)(c); 1684: } 1685: } 1686: } 1687: return retval; 1688: } 1689: 1690: /* 1691: * The following perform the "set" command. 1692: */ 1693: 1694: struct setlist { 1695: char *name; /* name */ 1696: char *help; /* help information */ 1697: char *charp; /* where it is located at */ 1698: }; 1699: 1700: struct setlist Setlist[] = { 1701: { "echo", "character to toggle local echoing on/off", &echoc }, 1702: { "escape", "character to escape back to telnet command mode", &escape }, 1703: { " ", "" }, 1704: { " ", "The following need 'localchars' to be toggled true", 0 }, 1705: { "erase", "character to cause an Erase Character", &nttyb.sg_erase }, 1706: { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc }, 1707: { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc }, 1708: { "kill", "character to cause an Erase Line", &nttyb.sg_kill }, 1709: { "quit", "character to cause a Break", &ntc.t_quitc }, 1710: { "eof", "character to cause an EOF ", &ntc.t_eofc }, 1711: { 0 } 1712: }; 1713: 1714: char ** 1715: getnextset(name) 1716: char *name; 1717: { 1718: struct setlist *c = (struct setlist *)name; 1719: 1720: return (char **) (c+1); 1721: } 1722: 1723: struct setlist * 1724: getset(name) 1725: char *name; 1726: { 1727: return (struct setlist *) genget(name, (char **) Setlist, getnextset); 1728: } 1729: 1730: setcmd(argc, argv) 1731: int argc; 1732: char *argv[]; 1733: { 1734: int value; 1735: struct setlist *ct; 1736: 1737: /* XXX back we go... sigh */ 1738: if (argc != 3) { 1739: if ((argc == 2) && 1740: ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) { 1741: for (ct = Setlist; ct->name; ct++) { 1742: printf("%s\t%s\n", ct->name, ct->help); 1743: } 1744: printf("?\tdisplay help information\n"); 1745: } else { 1746: printf("Format is 'set Name Value'\n'set ?' for help.\n"); 1747: } 1748: return 0; 1749: } 1750: 1751: ct = getset(argv[1]); 1752: if (ct == 0) { 1753: fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", 1754: argv[1]); 1755: return 0; 1756: } else if (ct == Ambiguous(struct setlist *)) { 1757: fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", 1758: argv[1]); 1759: return 0; 1760: } else { 1761: if (strcmp("off", argv[2])) { 1762: value = special(argv[2]); 1763: } else { 1764: value = -1; 1765: } 1766: *(ct->charp) = value; 1767: printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); 1768: } 1769: return 1; 1770: } 1771: 1772: /* 1773: * The following are the data structures and routines for the 1774: * 'mode' command. 1775: */ 1776: 1777: dolinemode() 1778: { 1779: if (hisopts[TELOPT_SGA]) { 1780: wontoption(TELOPT_SGA, 0); 1781: } 1782: if (hisopts[TELOPT_ECHO]) { 1783: wontoption(TELOPT_ECHO, 0); 1784: } 1785: } 1786: 1787: docharmode() 1788: { 1789: if (!hisopts[TELOPT_SGA]) { 1790: willoption(TELOPT_SGA, 0); 1791: } 1792: if (!hisopts[TELOPT_ECHO]) { 1793: willoption(TELOPT_ECHO, 0); 1794: } 1795: } 1796: 1797: struct cmd Modelist[] = { 1798: { "character", "character-at-a-time mode", docharmode, 1, 1 }, 1799: { "line", "line-by-line mode", dolinemode, 1, 1 }, 1800: { 0 }, 1801: }; 1802: 1803: char ** 1804: getnextmode(name) 1805: char *name; 1806: { 1807: struct cmd *c = (struct cmd *) name; 1808: 1809: return (char **) (c+1); 1810: } 1811: 1812: struct cmd * 1813: getmodecmd(name) 1814: char *name; 1815: { 1816: return (struct cmd *) genget(name, (char **) Modelist, getnextmode); 1817: } 1818: 1819: modecmd(argc, argv) 1820: int argc; 1821: char *argv[]; 1822: { 1823: struct cmd *mt; 1824: 1825: if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) { 1826: printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); 1827: for (mt = Modelist; mt->name; mt++) { 1828: printf("%s\t%s\n", mt->name, mt->help); 1829: } 1830: return 0; 1831: } 1832: mt = getmodecmd(argv[1]); 1833: if (mt == 0) { 1834: fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); 1835: return 0; 1836: } else if (mt == Ambiguous(struct cmd *)) { 1837: fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); 1838: return 0; 1839: } else { 1840: (*mt->handler)(); 1841: } 1842: return 1; 1843: } 1844: 1845: /* 1846: * The following data structures and routines implement the 1847: * "display" command. 1848: */ 1849: 1850: display(argc, argv) 1851: int argc; 1852: char *argv[]; 1853: { 1854: #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ 1855: if (*tl->variable) { \ 1856: printf("will"); \ 1857: } else { \ 1858: printf("won't"); \ 1859: } \ 1860: printf(" %s.\n", tl->actionexplanation); \ 1861: } 1862: 1863: #define doset(sl) if (sl->name && *sl->name != ' ') { \ 1864: printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \ 1865: } 1866: 1867: struct togglelist *tl; 1868: struct setlist *sl; 1869: 1870: if (argc == 1) { 1871: for (tl = Togglelist; tl->name; tl++) { 1872: dotog(tl); 1873: } 1874: printf("\n"); 1875: for (sl = Setlist; sl->name; sl++) { 1876: doset(sl); 1877: } 1878: } else { 1879: int i; 1880: 1881: for (i = 1; i < argc; i++) { 1882: sl = getset(argv[i]); 1883: tl = gettoggle(argv[i]); 1884: if ((sl == Ambiguous(struct setlist *)) || 1885: (tl == Ambiguous(struct togglelist *))) { 1886: printf("?Ambiguous argument '%s'.\n", argv[i]); 1887: return 0; 1888: } else if (!sl && !tl) { 1889: printf("?Unknown argument '%s'.\n", argv[i]); 1890: return 0; 1891: } else { 1892: if (tl) { 1893: dotog(tl); 1894: } 1895: if (sl) { 1896: doset(sl); 1897: } 1898: } 1899: } 1900: } 1901: return 1; 1902: #undef doset(sl) 1903: #undef dotog(tl) 1904: } 1905: 1906: /* 1907: * The following are the data structures, and many of the routines, 1908: * relating to command processing. 1909: */ 1910: 1911: /* 1912: * Set the escape character. 1913: */ 1914: setescape(argc, argv) 1915: int argc; 1916: char *argv[]; 1917: { 1918: register char *arg; 1919: char buf[50]; 1920: 1921: printf( 1922: "Deprecated usage - please use 'set escape%s%s' in the future.\n", 1923: (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); 1924: if (argc > 2) 1925: arg = argv[1]; 1926: else { 1927: printf("new escape character: "); 1928: gets(buf); 1929: arg = buf; 1930: } 1931: if (arg[0] != '\0') 1932: escape = arg[0]; 1933: printf("Escape character is '%s'.\n", control(escape)); 1934: fflush(stdout); 1935: return 1; 1936: } 1937: 1938: /*VARARGS*/ 1939: togcrmod() 1940: { 1941: crmod = !crmod; 1942: printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); 1943: printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); 1944: fflush(stdout); 1945: return 1; 1946: } 1947: 1948: /*VARARGS*/ 1949: suspend() 1950: { 1951: setcommandmode(); 1952: kill(0, SIGTSTP); 1953: /* reget parameters in case they were changed */ 1954: ioctl(0, TIOCGETP, (char *)&ottyb); 1955: ioctl(0, TIOCGETC, (char *)&otc); 1956: ioctl(0, TIOCGLTC, (char *)&oltc); 1957: return 1; 1958: } 1959: 1960: /*VARARGS*/ 1961: bye() 1962: { 1963: register char *op; 1964: 1965: if (connected) { 1966: shutdown(net, 2); 1967: printf("Connection closed.\n"); 1968: close(net); 1969: connected = 0; 1970: /* reset his options */ 1971: for (op = hisopts; op < &hisopts[256]; op++) 1972: *op = 0; 1973: /* reset our options */ 1974: bzero(myopts, 256); 1975: } 1976: return 0; 1977: } 1978: 1979: /*VARARGS*/ 1980: quit() 1981: { 1982: (void) call(bye, "bye", 0); 1983: exit(0); 1984: /*NOTREACHED*/ 1985: } 1986: 1987: /* 1988: * Print status about the connection. 1989: */ 1990: /*ARGSUSED*/ 1991: status(argc, argv) 1992: int argc; 1993: char *argv[]; 1994: { 1995: if (connected) { 1996: printf("Connected to %s.\n", hostname); 1997: if (argc < 2) { 1998: printf("Operating in %s.\n", modedescriptions[getconnmode()]); 1999: if (localchars) { 2000: printf("Catching signals locally.\n"); 2001: } 2002: } 2003: } else { 2004: printf("No connection.\n"); 2005: } 2006: printf("Escape character is '%s'.\n", control(escape)); 2007: fflush(stdout); 2008: return 1; 2009: } 2010: 2011: tn(argc, argv) 2012: int argc; 2013: char *argv[]; 2014: { 2015: register struct hostent *host = 0; 2016: 2017: if (connected) { 2018: printf("?Already connected to %s\n", hostname); 2019: return 0; 2020: } 2021: if (argc < 2) { 2022: (void) strcpy(line, "Connect "); 2023: printf("(to) "); 2024: gets(&line[strlen(line)]); 2025: makeargv(); 2026: argc = margc; 2027: argv = margv; 2028: } 2029: if (argc > 3) { 2030: printf("usage: %s host-name [port]\n", argv[0]); 2031: return 0; 2032: } 2033: sin.sin_addr.s_addr = inet_addr(argv[1]); 2034: if (sin.sin_addr.s_addr != -1) { 2035: sin.sin_family = AF_INET; 2036: (void) strcpy(hnamebuf, argv[1]); 2037: hostname = hnamebuf; 2038: } else { 2039: host = gethostbyname(argv[1]); 2040: if (host) { 2041: sin.sin_family = host->h_addrtype; 2042: #ifndef NOT43 2043: bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr, 2044: host->h_length); 2045: #else NOT43 2046: bcopy(host->h_addr, (caddr_t)&sin.sin_addr, 2047: host->h_length); 2048: #endif NOT43 2049: hostname = host->h_name; 2050: } else { 2051: printf("%s: unknown host\n", argv[1]); 2052: return 0; 2053: } 2054: } 2055: sin.sin_port = sp->s_port; 2056: if (argc == 3) { 2057: sin.sin_port = atoi(argv[2]); 2058: if (sin.sin_port == 0) { 2059: sp = getservbyname(argv[2], "tcp"); 2060: if (sp) 2061: sin.sin_port = sp->s_port; 2062: else { 2063: printf("%s: bad port number\n", argv[2]); 2064: return 0; 2065: } 2066: } else { 2067: sin.sin_port = atoi(argv[2]); 2068: sin.sin_port = htons(sin.sin_port); 2069: } 2070: telnetport = 0; 2071: } else { 2072: telnetport = 1; 2073: } 2074: signal(SIGINT, intr); 2075: signal(SIGQUIT, intr2); 2076: signal(SIGPIPE, deadpeer); 2077: printf("Trying...\n"); 2078: do { 2079: net = socket(AF_INET, SOCK_STREAM, 0); 2080: if (net < 0) { 2081: perror("telnet: socket"); 2082: return 0; 2083: } 2084: #ifndef NOT43 2085: if (debug && 2086: setsockopt(net, SOL_SOCKET, SO_DEBUG, 2087: (char *)&debug, sizeof(debug)) < 0) 2088: #else NOT43 2089: if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 2090: #endif NOT43 2091: perror("setsockopt (SO_DEBUG)"); 2092: 2093: if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 2094: #ifndef NOT43 2095: if (host && host->h_addr_list[1]) { 2096: int oerrno = errno; 2097: 2098: fprintf(stderr, 2099: "telnet: connect to address %s: ", 2100: inet_ntoa(sin.sin_addr)); 2101: errno = oerrno; 2102: perror((char *)0); 2103: host->h_addr_list++; 2104: bcopy(host->h_addr_list[0], 2105: (caddr_t)&sin.sin_addr, host->h_length); 2106: fprintf(stderr, "Trying %s...\n", 2107: inet_ntoa(sin.sin_addr)); 2108: (void) close(net); 2109: continue; 2110: } 2111: #endif NOT43 2112: perror("telnet: connect"); 2113: signal(SIGINT, SIG_DFL); 2114: signal(SIGQUIT, SIG_DFL); 2115: return 0; 2116: } 2117: connected++; 2118: } while (connected == 0); 2119: call(status, "status", "notmuch", 0); 2120: if (setjmp(peerdied) == 0) 2121: telnet(); 2122: fprintf(stderr, "Connection closed by foreign host.\n"); 2123: exit(1); 2124: /*NOTREACHED*/ 2125: } 2126: 2127: 2128: #define HELPINDENT (sizeof ("connect")) 2129: 2130: char openhelp[] = "connect to a site"; 2131: char closehelp[] = "close current connection"; 2132: char quithelp[] = "exit telnet"; 2133: char zhelp[] = "suspend telnet"; 2134: char statushelp[] = "print status information"; 2135: char helphelp[] = "print help information"; 2136: char sendhelp[] = "transmit special characters ('send ?' for more)"; 2137: char sethelp[] = "set operating parameters ('set ?' for more)"; 2138: char togglestring[] ="toggle operating parameters ('toggle ?' for more)"; 2139: char displayhelp[] = "display operating parameters"; 2140: char modehelp[] = 2141: "try to enter line-by-line or character-at-a-time mode"; 2142: 2143: int help(); 2144: 2145: struct cmd cmdtab[] = { 2146: { "close", closehelp, bye, 1, 1 }, 2147: { "display", displayhelp, display, 1, 0 }, 2148: { "mode", modehelp, modecmd, 1, 1 }, 2149: { "open", openhelp, tn, 1, 0 }, 2150: { "quit", quithelp, quit, 1, 0 }, 2151: { "send", sendhelp, sendcmd, 1, 1 }, 2152: { "set", sethelp, setcmd, 1, 0 }, 2153: { "status", statushelp, status, 1, 0 }, 2154: { "toggle", togglestring, toggle, 1, 0 }, 2155: { "z", zhelp, suspend, 1, 0 }, 2156: { "?", helphelp, help, 1, 0 }, 2157: 0 2158: }; 2159: 2160: char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; 2161: char escapehelp[] = "deprecated command -- use 'set escape' instead"; 2162: 2163: struct cmd cmdtab2[] = { 2164: { "help", helphelp, help, 0, 0 }, 2165: { "escape", escapehelp, setescape, 1, 0 }, 2166: { "crmod", crmodhelp, togcrmod, 1, 0 }, 2167: 0 2168: }; 2169: 2170: /* 2171: * Help command. 2172: */ 2173: help(argc, argv) 2174: int argc; 2175: char *argv[]; 2176: { 2177: register struct cmd *c; 2178: 2179: if (argc == 1) { 2180: printf("Commands may be abbreviated. Commands are:\n\n"); 2181: for (c = cmdtab; c->name; c++) 2182: if (c->dohelp) { 2183: printf("%-*s\t%s\n", HELPINDENT, c->name, 2184: c->help); 2185: } 2186: return 0; 2187: } 2188: while (--argc > 0) { 2189: register char *arg; 2190: arg = *++argv; 2191: c = getcmd(arg); 2192: if (c == Ambiguous(struct cmd *)) 2193: printf("?Ambiguous help command %s\n", arg); 2194: else if (c == (struct cmd *)0) 2195: printf("?Invalid help command %s\n", arg); 2196: else 2197: printf("%s\n", c->help); 2198: } 2199: return 0; 2200: } 2201: /* 2202: * Call routine with argc, argv set from args (terminated by 0). 2203: * VARARGS2 2204: */ 2205: call(routine, args) 2206: int (*routine)(); 2207: char *args; 2208: { 2209: register char **argp; 2210: register int argc; 2211: 2212: for (argc = 0, argp = &args; *argp++ != 0; argc++) 2213: ; 2214: return (*routine)(argc, &args); 2215: } 2216: 2217: makeargv() 2218: { 2219: register char *cp; 2220: register char **argp = margv; 2221: 2222: margc = 0; 2223: for (cp = line; *cp;) { 2224: while (isspace(*cp)) 2225: cp++; 2226: if (*cp == '\0') 2227: break; 2228: *argp++ = cp; 2229: margc += 1; 2230: while (*cp != '\0' && !isspace(*cp)) 2231: cp++; 2232: if (*cp == '\0') 2233: break; 2234: *cp++ = '\0'; 2235: } 2236: *argp++ = 0; 2237: } 2238: 2239: char ** 2240: getnextcmd(name) 2241: char *name; 2242: { 2243: struct cmd *c = (struct cmd *) name; 2244: 2245: return (char **) (c+1); 2246: } 2247: 2248: struct cmd * 2249: getcmd(name) 2250: char *name; 2251: { 2252: struct cmd *cm; 2253: 2254: if (cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) { 2255: return cm; 2256: } else { 2257: return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd); 2258: } 2259: } 2260: 2261: command(top) 2262: int top; 2263: { 2264: register struct cmd *c; 2265: 2266: setcommandmode(); 2267: if (!top) { 2268: putchar('\n'); 2269: } else { 2270: signal(SIGINT, SIG_DFL); 2271: signal(SIGQUIT, SIG_DFL); 2272: } 2273: for (;;) { 2274: printf("%s> ", prompt); 2275: if (gets(line) == 0) { 2276: if (feof(stdin)) 2277: quit(); 2278: break; 2279: } 2280: if (line[0] == 0) 2281: break; 2282: makeargv(); 2283: c = getcmd(margv[0]); 2284: if (c == Ambiguous(struct cmd *)) { 2285: printf("?Ambiguous command\n"); 2286: continue; 2287: } 2288: if (c == 0) { 2289: printf("?Invalid command\n"); 2290: continue; 2291: } 2292: if (c->needconnect && !connected) { 2293: printf("?Need to be connected first.\n"); 2294: continue; 2295: } 2296: if ((*c->handler)(margc, margv)) { 2297: break; 2298: } 2299: } 2300: if (!top) { 2301: if (!connected) { 2302: longjmp(toplevel, 1); 2303: /*NOTREACHED*/ 2304: } 2305: setconnmode(); 2306: } 2307: } 2308: 2309: /* 2310: * main. Parse arguments, invoke the protocol or command parser. 2311: */ 2312: 2313: 2314: main(argc, argv) 2315: int argc; 2316: char *argv[]; 2317: { 2318: sp = getservbyname("telnet", "tcp"); 2319: if (sp == 0) { 2320: fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); 2321: exit(1); 2322: } 2323: NetTrace = stdout; 2324: ioctl(0, TIOCGETP, (char *)&ottyb); 2325: ioctl(0, TIOCGETC, (char *)&otc); 2326: ioctl(0, TIOCGLTC, (char *)&oltc); 2327: #if defined(LNOFLSH) 2328: ioctl(0, TIOCLGET, (char *)&autoflush); 2329: autoflush = !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */ 2330: #else /* LNOFLSH */ 2331: autoflush = 1; 2332: #endif /* LNOFLSH */ 2333: ntc = otc; 2334: nltc = oltc; 2335: nttyb = ottyb; 2336: setbuf(stdin, (char *)0); 2337: setbuf(stdout, (char *)0); 2338: prompt = argv[0]; 2339: if (argc > 1 && !strcmp(argv[1], "-d")) { 2340: debug = 1; 2341: argv++; 2342: argc--; 2343: } 2344: if (argc > 1 && !strcmp(argv[1], "-n")) { 2345: argv++; 2346: argc--; 2347: if (argc > 1) { /* get file name */ 2348: NetTrace = fopen(argv[1], "w"); 2349: argv++; 2350: argc--; 2351: if (NetTrace == NULL) { 2352: NetTrace = stdout; 2353: } 2354: } 2355: } 2356: if (argc != 1) { 2357: if (setjmp(toplevel) != 0) 2358: exit(0); 2359: tn(argc, argv); 2360: } 2361: setjmp(toplevel); 2362: for (;;) 2363: command(1); 2364: }