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