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