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