1 #ifndef lint 2 static char sccsid[] = "@(#)telnet.c 4.26 (Berkeley) 12/23/84"; 3 #endif 4 5 /* 6 * User telnet program. 7 */ 8 #include <sys/types.h> 9 #include <sys/socket.h> 10 #include <sys/ioctl.h> 11 12 #include <netinet/in.h> 13 14 #define TELOPTS 15 #include <arpa/telnet.h> 16 17 #include <stdio.h> 18 #include <ctype.h> 19 #include <errno.h> 20 #include <signal.h> 21 #include <setjmp.h> 22 #include <netdb.h> 23 24 #define strip(x) ((x)&0177) 25 26 char ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf; 27 char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 28 29 char hisopts[256]; 30 char myopts[256]; 31 32 char doopt[] = { IAC, DO, '%', 'c', 0 }; 33 char dont[] = { IAC, DONT, '%', 'c', 0 }; 34 char will[] = { IAC, WILL, '%', 'c', 0 }; 35 char wont[] = { IAC, WONT, '%', 'c', 0 }; 36 37 int connected; 38 int net; 39 int showoptions = 0; 40 int options; 41 int debug = 0; 42 int crmod = 0; 43 char *prompt; 44 char escape = CTRL(]); 45 46 char line[200]; 47 int margc; 48 char *margv[20]; 49 50 jmp_buf toplevel; 51 jmp_buf peerdied; 52 53 extern int errno; 54 55 int tn(), quit(), suspend(), bye(), help(); 56 int setescape(), status(), toggle(), setoptions(); 57 int setcrmod(), setdebug(); 58 59 #define HELPINDENT (sizeof ("connect")) 60 61 struct cmd { 62 char *name; /* command name */ 63 char *help; /* help string */ 64 int (*handler)(); /* routine which executes command */ 65 }; 66 67 char openhelp[] = "connect to a site"; 68 char closehelp[] = "close current connection"; 69 char quithelp[] = "exit telnet"; 70 char zhelp[] = "suspend telnet"; 71 char debughelp[] = "toggle debugging"; 72 char escapehelp[] = "set escape character"; 73 char statushelp[] = "print status information"; 74 char helphelp[] = "print help information"; 75 char optionshelp[] = "toggle viewing of options processing"; 76 char crmodhelp[] = "toggle mapping of received carriage returns"; 77 78 struct cmd cmdtab[] = { 79 { "open", openhelp, tn }, 80 { "close", closehelp, bye }, 81 { "quit", quithelp, quit }, 82 { "z", zhelp, suspend }, 83 { "escape", escapehelp, setescape }, 84 { "status", statushelp, status }, 85 { "options", optionshelp, setoptions }, 86 { "crmod", crmodhelp, setcrmod }, 87 { "debug", debughelp, setdebug }, 88 { "?", helphelp, help }, 89 0 90 }; 91 92 struct sockaddr_in sin; 93 94 int intr(), deadpeer(); 95 char *control(); 96 struct cmd *getcmd(); 97 struct servent *sp; 98 99 struct tchars otc; 100 struct ltchars oltc; 101 struct sgttyb ottyb; 102 103 main(argc, argv) 104 int argc; 105 char *argv[]; 106 { 107 sp = getservbyname("telnet", "tcp"); 108 if (sp == 0) { 109 fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); 110 exit(1); 111 } 112 ioctl(0, TIOCGETP, (char *)&ottyb); 113 ioctl(0, TIOCGETC, (char *)&otc); 114 ioctl(0, TIOCGLTC, (char *)&oltc); 115 setbuf(stdin, 0); 116 setbuf(stdout, 0); 117 prompt = argv[0]; 118 if (argc > 1 && !strcmp(argv[1], "-d")) { 119 debug = 1; 120 argv++; 121 argc--; 122 } 123 if (argc != 1) { 124 if (setjmp(toplevel) != 0) 125 exit(0); 126 tn(argc, argv); 127 } 128 setjmp(toplevel); 129 for (;;) 130 command(1); 131 } 132 133 char *hostname; 134 char hnamebuf[32]; 135 136 tn(argc, argv) 137 int argc; 138 char *argv[]; 139 { 140 register int c; 141 register struct hostent *host; 142 143 if (connected) { 144 printf("?Already connected to %s\n", hostname); 145 return; 146 } 147 if (argc < 2) { 148 strcpy(line, "Connect "); 149 printf("(to) "); 150 gets(&line[strlen(line)]); 151 makeargv(); 152 argc = margc; 153 argv = margv; 154 } 155 if (argc > 3) { 156 printf("usage: %s host-name [port]\n", argv[0]); 157 return; 158 } 159 host = gethostbyname(argv[1]); 160 if (host) { 161 sin.sin_family = host->h_addrtype; 162 bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length); 163 hostname = host->h_name; 164 } else { 165 sin.sin_family = AF_INET; 166 sin.sin_addr.s_addr = inet_addr(argv[1]); 167 if (sin.sin_addr.s_addr == -1) { 168 printf("%s: unknown host\n", argv[1]); 169 return; 170 } 171 strcpy(hnamebuf, argv[1]); 172 hostname = hnamebuf; 173 } 174 sin.sin_port = sp->s_port; 175 if (argc == 3) { 176 sin.sin_port = atoi(argv[2]); 177 if (sin.sin_port < 0) { 178 printf("%s: bad port number\n", argv[2]); 179 return; 180 } 181 sin.sin_port = htons(sin.sin_port); 182 } 183 net = socket(AF_INET, SOCK_STREAM, 0, 0); 184 if (net < 0) { 185 perror("telnet: socket"); 186 return; 187 } 188 if (debug && 189 setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0) 190 perror("setsockopt (SO_DEBUG)"); 191 signal(SIGINT, intr); 192 signal(SIGPIPE, deadpeer); 193 printf("Trying...\n"); 194 if (connect(net, (caddr_t)&sin, sizeof (sin), 0) < 0) { 195 perror("telnet: connect"); 196 signal(SIGINT, SIG_DFL); 197 return; 198 } 199 connected++; 200 call(status, "status", 0); 201 if (setjmp(peerdied) == 0) 202 telnet(net); 203 fprintf(stderr, "Connection closed by foreign host.\n"); 204 exit(1); 205 } 206 207 /* 208 * Print status about the connection. 209 */ 210 /*VARARGS*/ 211 status() 212 { 213 if (connected) 214 printf("Connected to %s.\n", hostname); 215 else 216 printf("No connection.\n"); 217 printf("Escape character is '%s'.\n", control(escape)); 218 fflush(stdout); 219 } 220 221 makeargv() 222 { 223 register char *cp; 224 register char **argp = margv; 225 226 margc = 0; 227 for (cp = line; *cp;) { 228 while (isspace(*cp)) 229 cp++; 230 if (*cp == '\0') 231 break; 232 *argp++ = cp; 233 margc += 1; 234 while (*cp != '\0' && !isspace(*cp)) 235 cp++; 236 if (*cp == '\0') 237 break; 238 *cp++ = '\0'; 239 } 240 *argp++ = 0; 241 } 242 243 /*VARARGS*/ 244 suspend() 245 { 246 register int save; 247 248 save = mode(0); 249 kill(0, SIGTSTP); 250 /* reget parameters in case they were changed */ 251 ioctl(0, TIOCGETP, (char *)&ottyb); 252 ioctl(0, TIOCGETC, (char *)&otc); 253 ioctl(0, TIOCGLTC, (char *)&oltc); 254 (void) mode(save); 255 } 256 257 /*VARARGS*/ 258 bye() 259 { 260 register char *op; 261 262 (void) mode(0); 263 if (connected) { 264 shutdown(net, 2); 265 printf("Connection closed.\n"); 266 close(net); 267 connected = 0; 268 /* reset his options */ 269 for (op = hisopts; op < &hisopts[256]; op++) 270 *op = 0; 271 } 272 } 273 274 /*VARARGS*/ 275 quit() 276 { 277 call(bye, "bye", 0); 278 exit(0); 279 } 280 281 /* 282 * Help command. 283 */ 284 help(argc, argv) 285 int argc; 286 char *argv[]; 287 { 288 register struct cmd *c; 289 290 if (argc == 1) { 291 printf("Commands may be abbreviated. Commands are:\n\n"); 292 for (c = cmdtab; c->name; c++) 293 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 294 return; 295 } 296 while (--argc > 0) { 297 register char *arg; 298 arg = *++argv; 299 c = getcmd(arg); 300 if (c == (struct cmd *)-1) 301 printf("?Ambiguous help command %s\n", arg); 302 else if (c == (struct cmd *)0) 303 printf("?Invalid help command %s\n", arg); 304 else 305 printf("%s\n", c->help); 306 } 307 } 308 309 /* 310 * Call routine with argc, argv set from args (terminated by 0). 311 * VARARGS2 312 */ 313 call(routine, args) 314 int (*routine)(); 315 int args; 316 { 317 register int *argp; 318 register int argc; 319 320 for (argc = 0, argp = &args; *argp++ != 0; argc++) 321 ; 322 (*routine)(argc, &args); 323 } 324 325 struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 326 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 327 328 mode(f) 329 register int f; 330 { 331 static int prevmode = 0; 332 struct tchars *tc; 333 struct ltchars *ltc; 334 struct sgttyb sb; 335 int onoff, old; 336 337 if (prevmode == f) 338 return (f); 339 old = prevmode; 340 prevmode = f; 341 sb = ottyb; 342 switch (f) { 343 344 case 0: 345 onoff = 0; 346 tc = &otc; 347 ltc = &oltc; 348 break; 349 350 case 1: 351 case 2: 352 sb.sg_flags |= CBREAK; 353 if (f == 1) 354 sb.sg_flags &= ~(ECHO|CRMOD); 355 else 356 sb.sg_flags |= ECHO|CRMOD; 357 sb.sg_erase = sb.sg_kill = -1; 358 tc = ¬c; 359 ltc = &noltc; 360 onoff = 1; 361 break; 362 363 default: 364 return; 365 } 366 ioctl(fileno(stdin), TIOCSLTC, (char *)ltc); 367 ioctl(fileno(stdin), TIOCSETC, (char *)tc); 368 ioctl(fileno(stdin), TIOCSETP, (char *)&sb); 369 ioctl(fileno(stdin), FIONBIO, &onoff); 370 ioctl(fileno(stdout), FIONBIO, &onoff); 371 return (old); 372 } 373 374 char sibuf[BUFSIZ], *sbp; 375 char tibuf[BUFSIZ], *tbp; 376 int scc, tcc; 377 378 /* 379 * Select from tty and network... 380 */ 381 telnet(s) 382 int s; 383 { 384 register int c; 385 int tin = fileno(stdin), tout = fileno(stdout); 386 int on = 1; 387 388 (void) mode(2); 389 ioctl(s, FIONBIO, &on); 390 for (;;) { 391 int ibits = 0, obits = 0; 392 393 if (nfrontp - nbackp) 394 obits |= (1 << s); 395 else 396 ibits |= (1 << tin); 397 if (tfrontp - tbackp) 398 obits |= (1 << tout); 399 else 400 ibits |= (1 << s); 401 if (scc < 0 && tcc < 0) 402 break; 403 select(16, &ibits, &obits, 0, 0); 404 if (ibits == 0 && obits == 0) { 405 sleep(5); 406 continue; 407 } 408 409 /* 410 * Something to read from the network... 411 */ 412 if (ibits & (1 << s)) { 413 scc = read(s, sibuf, sizeof (sibuf)); 414 if (scc < 0 && errno == EWOULDBLOCK) 415 scc = 0; 416 else { 417 if (scc <= 0) 418 break; 419 sbp = sibuf; 420 } 421 } 422 423 /* 424 * Something to read from the tty... 425 */ 426 if (ibits & (1 << tin)) { 427 tcc = read(tin, tibuf, sizeof (tibuf)); 428 if (tcc < 0 && errno == EWOULDBLOCK) 429 tcc = 0; 430 else { 431 if (tcc <= 0) 432 break; 433 tbp = tibuf; 434 } 435 } 436 437 while (tcc > 0) { 438 register int c; 439 440 if ((&netobuf[BUFSIZ] - nfrontp) < 2) 441 break; 442 c = *tbp++ & 0377, tcc--; 443 if (strip(c) == escape) { 444 command(0); 445 tcc = 0; 446 break; 447 } 448 if (c == IAC) 449 *nfrontp++ = c; 450 *nfrontp++ = c; 451 if (c == '\r') 452 *nfrontp++ = '\n'; 453 } 454 if ((obits & (1 << s)) && (nfrontp - nbackp) > 0) 455 netflush(s); 456 if (scc > 0) 457 telrcv(); 458 if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0) 459 ttyflush(tout); 460 } 461 (void) mode(0); 462 } 463 464 command(top) 465 int top; 466 { 467 register struct cmd *c; 468 int oldmode, wasopen; 469 470 oldmode = mode(0); 471 if (!top) 472 putchar('\n'); 473 else 474 signal(SIGINT, SIG_DFL); 475 for (;;) { 476 printf("%s> ", prompt); 477 if (gets(line) == 0) { 478 if (feof(stdin)) { 479 clearerr(stdin); 480 putchar('\n'); 481 } 482 break; 483 } 484 if (line[0] == 0) 485 break; 486 makeargv(); 487 c = getcmd(margv[0]); 488 if (c == (struct cmd *)-1) { 489 printf("?Ambiguous command\n"); 490 continue; 491 } 492 if (c == 0) { 493 printf("?Invalid command\n"); 494 continue; 495 } 496 (*c->handler)(margc, margv); 497 if (c->handler != help) 498 break; 499 } 500 if (!top) { 501 if (!connected) 502 longjmp(toplevel, 1); 503 (void) mode(oldmode); 504 } 505 } 506 507 /* 508 * Telnet receiver states for fsm 509 */ 510 #define TS_DATA 0 511 #define TS_IAC 1 512 #define TS_WILL 2 513 #define TS_WONT 3 514 #define TS_DO 4 515 #define TS_DONT 5 516 517 telrcv() 518 { 519 register int c; 520 static int state = TS_DATA; 521 522 while (scc > 0) { 523 c = *sbp++ & 0377, scc--; 524 switch (state) { 525 526 case TS_DATA: 527 if (c == IAC) { 528 state = TS_IAC; 529 continue; 530 } 531 *tfrontp++ = c; 532 /* 533 * This hack is needed since we can't set 534 * CRMOD on output only. Machines like MULTICS 535 * like to send \r without \n; since we must 536 * turn off CRMOD to get proper input, the mapping 537 * is done here (sigh). 538 */ 539 if (c == '\r' && crmod) 540 *tfrontp++ = '\n'; 541 continue; 542 543 case TS_IAC: 544 switch (c) { 545 546 case WILL: 547 state = TS_WILL; 548 continue; 549 550 case WONT: 551 state = TS_WONT; 552 continue; 553 554 case DO: 555 state = TS_DO; 556 continue; 557 558 case DONT: 559 state = TS_DONT; 560 continue; 561 562 case DM: 563 ioctl(fileno(stdout), TIOCFLUSH, 0); 564 break; 565 566 case NOP: 567 case GA: 568 break; 569 570 default: 571 break; 572 } 573 state = TS_DATA; 574 continue; 575 576 case TS_WILL: 577 printoption("RCVD", will, c, !hisopts[c]); 578 if (!hisopts[c]) 579 willoption(c); 580 state = TS_DATA; 581 continue; 582 583 case TS_WONT: 584 printoption("RCVD", wont, c, hisopts[c]); 585 if (hisopts[c]) 586 wontoption(c); 587 state = TS_DATA; 588 continue; 589 590 case TS_DO: 591 printoption("RCVD", doopt, c, !myopts[c]); 592 if (!myopts[c]) 593 dooption(c); 594 state = TS_DATA; 595 continue; 596 597 case TS_DONT: 598 printoption("RCVD", dont, c, myopts[c]); 599 if (myopts[c]) { 600 myopts[c] = 0; 601 sprintf(nfrontp, wont, c); 602 nfrontp += sizeof (wont) - 2; 603 printoption("SENT", wont, c); 604 } 605 state = TS_DATA; 606 continue; 607 } 608 } 609 } 610 611 willoption(option) 612 int option; 613 { 614 char *fmt; 615 616 switch (option) { 617 618 case TELOPT_ECHO: 619 (void) mode(1); 620 621 case TELOPT_SGA: 622 hisopts[option] = 1; 623 fmt = doopt; 624 break; 625 626 case TELOPT_TM: 627 fmt = dont; 628 break; 629 630 default: 631 fmt = dont; 632 break; 633 } 634 sprintf(nfrontp, fmt, option); 635 nfrontp += sizeof (dont) - 2; 636 printoption("SENT", fmt, option); 637 } 638 639 wontoption(option) 640 int option; 641 { 642 char *fmt; 643 644 switch (option) { 645 646 case TELOPT_ECHO: 647 (void) mode(2); 648 649 case TELOPT_SGA: 650 hisopts[option] = 0; 651 fmt = dont; 652 break; 653 654 default: 655 fmt = dont; 656 } 657 sprintf(nfrontp, fmt, option); 658 nfrontp += sizeof (doopt) - 2; 659 printoption("SENT", fmt, option); 660 } 661 662 dooption(option) 663 int option; 664 { 665 char *fmt; 666 667 switch (option) { 668 669 case TELOPT_TM: 670 fmt = wont; 671 break; 672 673 case TELOPT_ECHO: 674 (void) mode(2); 675 fmt = will; 676 hisopts[option] = 0; 677 break; 678 679 case TELOPT_SGA: 680 fmt = will; 681 break; 682 683 default: 684 fmt = wont; 685 break; 686 } 687 sprintf(nfrontp, fmt, option); 688 nfrontp += sizeof (doopt) - 2; 689 printoption("SENT", fmt, option); 690 } 691 692 /* 693 * Set the escape character. 694 */ 695 setescape(argc, argv) 696 int argc; 697 char *argv[]; 698 { 699 register char *arg; 700 char buf[50]; 701 702 if (argc > 2) 703 arg = argv[1]; 704 else { 705 printf("new escape character: "); 706 gets(buf); 707 arg = buf; 708 } 709 if (arg[0] != '\0') 710 escape = arg[0]; 711 printf("Escape character is '%s'.\n", control(escape)); 712 fflush(stdout); 713 } 714 715 /*VARARGS*/ 716 setoptions() 717 { 718 719 showoptions = !showoptions; 720 printf("%s show option processing.\n", showoptions ? "Will" : "Wont"); 721 fflush(stdout); 722 } 723 724 /*VARARGS*/ 725 setcrmod() 726 { 727 728 crmod = !crmod; 729 printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont"); 730 fflush(stdout); 731 } 732 733 /*VARARGS*/ 734 setdebug() 735 { 736 737 debug = debug ? 0 : 1; 738 printf("%s turn on socket level debugging.\n", 739 debug ? "Will" : "Wont"); 740 fflush(stdout); 741 if (net > 0 && 742 setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0) 743 perror("setsockopt (SO_DEBUG)"); 744 } 745 746 /* 747 * Construct a control character sequence 748 * for a special character. 749 */ 750 char * 751 control(c) 752 register int c; 753 { 754 static char buf[3]; 755 756 if (c == 0177) 757 return ("^?"); 758 if (c >= 040) { 759 buf[0] = c; 760 buf[1] = 0; 761 } else { 762 buf[0] = '^'; 763 buf[1] = '@'+c; 764 buf[2] = 0; 765 } 766 return (buf); 767 } 768 769 struct cmd * 770 getcmd(name) 771 register char *name; 772 { 773 register char *p, *q; 774 register struct cmd *c, *found; 775 register int nmatches, longest; 776 777 longest = 0; 778 nmatches = 0; 779 found = 0; 780 for (c = cmdtab; p = c->name; c++) { 781 for (q = name; *q == *p++; q++) 782 if (*q == 0) /* exact match? */ 783 return (c); 784 if (!*q) { /* the name was a prefix */ 785 if (q - name > longest) { 786 longest = q - name; 787 nmatches = 1; 788 found = c; 789 } else if (q - name == longest) 790 nmatches++; 791 } 792 } 793 if (nmatches > 1) 794 return ((struct cmd *)-1); 795 return (found); 796 } 797 798 deadpeer() 799 { 800 (void) mode(0); 801 longjmp(peerdied, -1); 802 } 803 804 intr() 805 { 806 (void) mode(0); 807 longjmp(toplevel, -1); 808 } 809 810 ttyflush(fd) 811 { 812 int n; 813 814 if ((n = tfrontp - tbackp) > 0) 815 n = write(fd, tbackp, n); 816 if (n < 0) 817 return; 818 tbackp += n; 819 if (tbackp == tfrontp) 820 tbackp = tfrontp = ttyobuf; 821 } 822 823 netflush(fd) 824 { 825 int n; 826 827 if ((n = nfrontp - nbackp) > 0) 828 n = write(fd, nbackp, n); 829 if (n < 0) { 830 if (errno != ENOBUFS && errno != EWOULDBLOCK) { 831 (void) mode(0); 832 perror(hostname); 833 close(fd); 834 longjmp(peerdied, -1); 835 /*NOTREACHED*/ 836 } 837 n = 0; 838 } 839 nbackp += n; 840 if (nbackp == nfrontp) 841 nbackp = nfrontp = netobuf; 842 } 843 844 /*VARARGS*/ 845 printoption(direction, fmt, option, what) 846 char *direction, *fmt; 847 int option, what; 848 { 849 if (!showoptions) 850 return; 851 printf("%s ", direction); 852 if (fmt == doopt) 853 fmt = "do"; 854 else if (fmt == dont) 855 fmt = "dont"; 856 else if (fmt == will) 857 fmt = "will"; 858 else if (fmt == wont) 859 fmt = "wont"; 860 else 861 fmt = "???"; 862 if (option < TELOPT_SUPDUP) 863 printf("%s %s", fmt, telopts[option]); 864 else 865 printf("%s %d", fmt, option); 866 if (*direction == '<') { 867 printf("\r\n"); 868 return; 869 } 870 printf(" (%s)\r\n", what ? "reply" : "don't reply"); 871 } 872