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