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