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