1 static char sccsid[] = "@(#)telnet.c 4.5 (Berkeley) 03/23/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 if (showoptions) 329 printoption("<--", doopt, TELOPT_SGA); 330 sprintf(nfrontp, doopt, TELOPT_SGA); 331 nfrontp += sizeof(doopt) - 2; 332 if (showoptions) 333 printoption("<--", will, TELOPT_SGA); 334 sprintf(nfrontp, will, TELOPT_SGA); 335 nfrontp += sizeof(doopt) - 2; 336 ioctl(s, FIONBIO, &on); 337 for (;;) { 338 int ibits = 0, obits = 0; 339 340 if (nfrontp - nbackp) 341 obits |= (1 << s); 342 else 343 ibits |= (1 << tin); 344 if (tfrontp - tbackp) 345 obits |= (1 << tout); 346 else 347 ibits |= (1 << s); 348 if (scc < 0 && tcc < 0) 349 break; 350 select(32, &ibits, &obits, INFINITY); 351 if (ibits == 0 && obits == 0) { 352 sleep(5); 353 continue; 354 } 355 356 /* 357 * Something to read from the network... 358 */ 359 if (ibits & (1 << s)) { 360 scc = read(s, sibuf, sizeof(sibuf)); 361 if (scc < 0 && errno == EWOULDBLOCK) 362 scc = 0; 363 else { 364 if (scc <= 0) 365 break; 366 sbp = sibuf; 367 } 368 } 369 370 /* 371 * Something to read from the tty... 372 */ 373 if (ibits & (1 << tin)) { 374 tcc = read(tin, tibuf, sizeof(tibuf)); 375 if (tcc < 0 && errno == EWOULDBLOCK) 376 tcc = 0; 377 else { 378 if (tcc <= 0) 379 break; 380 tbp = tibuf; 381 } 382 } 383 384 while (tcc > 0) { 385 register int c; 386 387 if ((&netobuf[BUFSIZ] - nfrontp) < 2) 388 break; 389 c = *tbp++ & 0377, tcc--; 390 if (strip(c) == escape) { 391 command(0); 392 tcc = 0; 393 break; 394 } 395 *nfrontp++ = c; 396 } 397 if ((obits & (1 << s)) && (nfrontp - nbackp) > 0) 398 netflush(s); 399 if (scc > 0) 400 telrcv(); 401 if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0) 402 ttyflush(tout); 403 } 404 mode(0); 405 } 406 407 command(top) 408 int top; 409 { 410 register struct cmd *c; 411 int oldmode, wasopen; 412 413 oldmode = mode(0); 414 if (!top) 415 putchar('\n'); 416 else 417 sigset(SIGINT, SIG_DFL); 418 for (;;) { 419 printf("%s> ", prompt); 420 if (gets(line) == 0) 421 break; 422 if (line[0] == 0) 423 break; 424 makeargv(); 425 c = getcmd(margv[0]); 426 if (c == (struct cmd *)-1) { 427 printf("?Ambiguous command\n"); 428 continue; 429 } 430 if (c == 0) { 431 printf("?Invalid command\n"); 432 continue; 433 } 434 (*c->handler)(margc, margv); 435 if (c->handler != help) 436 break; 437 } 438 if (!top) { 439 if (!connected) 440 longjmp(toplevel, 1); 441 mode(oldmode); 442 } 443 } 444 445 /* 446 * Telnet receiver states for fsm 447 */ 448 #define TS_DATA 0 449 #define TS_IAC 1 450 #define TS_WILL 2 451 #define TS_WONT 3 452 #define TS_DO 4 453 #define TS_DONT 5 454 455 telrcv() 456 { 457 register int c; 458 static int state = TS_DATA; 459 460 while (scc > 0) { 461 c = *sbp++ & 0377, scc--; 462 switch (state) { 463 464 case TS_DATA: 465 if (c == IAC) 466 state = TS_IAC; 467 else 468 *tfrontp++ = c; 469 continue; 470 471 case TS_IAC: 472 switch (c) { 473 474 case WILL: 475 state = TS_WILL; 476 continue; 477 478 case WONT: 479 state = TS_WONT; 480 continue; 481 482 case DO: 483 state = TS_DO; 484 continue; 485 486 case DONT: 487 state = TS_DONT; 488 continue; 489 490 case DM: 491 ioctl(fileno(stdout), TIOCFLUSH, 0); 492 break; 493 494 case NOP: 495 case GA: 496 break; 497 498 default: 499 break; 500 } 501 state = TS_DATA; 502 continue; 503 504 case TS_WILL: 505 if (showoptions) 506 printoption("-->", will, c, !hisopts[c]); 507 if (!hisopts[c]) 508 willoption(c); 509 state = TS_DATA; 510 continue; 511 512 case TS_WONT: 513 if (showoptions) 514 printoption("-->", wont, c, hisopts[c]); 515 if (hisopts[c]) 516 wontoption(c); 517 state = TS_DATA; 518 continue; 519 520 case TS_DO: 521 if (showoptions) 522 printoption("-->", doopt, c, !myopts[c]); 523 if (!myopts[c]) 524 dooption(c); 525 state = TS_DATA; 526 continue; 527 528 case TS_DONT: 529 if (showoptions) 530 printoption("-->", dont, c, myopts[c]); 531 if (myopts[c]) { 532 myopts[c] = 0; 533 sprintf(nfrontp, wont, c); 534 nfrontp += sizeof(wont) - 2; 535 if (showoptions) 536 printoption("<--", wont, c); 537 } 538 state = TS_DATA; 539 continue; 540 } 541 } 542 } 543 544 willoption(option) 545 int option; 546 { 547 char *fmt; 548 549 switch (option) { 550 551 case TELOPT_ECHO: 552 mode(1); 553 554 case TELOPT_SGA: 555 hisopts[option] = 1; 556 fmt = doopt; 557 break; 558 559 case TELOPT_TM: 560 fmt = dont; 561 break; 562 563 default: 564 fmt = dont; 565 break; 566 } 567 sprintf(nfrontp, fmt, option); 568 nfrontp += sizeof(dont) - 2; 569 if (showoptions) 570 printoption("<--", fmt, option); 571 } 572 573 wontoption(option) 574 int option; 575 { 576 char *fmt; 577 578 switch (option) { 579 580 case TELOPT_ECHO: 581 mode(2); 582 583 case TELOPT_SGA: 584 hisopts[option] = 0; 585 fmt = dont; 586 break; 587 588 default: 589 fmt = dont; 590 } 591 sprintf(nfrontp, fmt, option); 592 nfrontp += sizeof(doopt) - 2; 593 if (showoptions) 594 printoption("<--", fmt, option); 595 } 596 597 dooption(option) 598 int option; 599 { 600 char *fmt; 601 602 switch (option) { 603 604 case TELOPT_TM: 605 fmt = wont; 606 break; 607 608 case TELOPT_SGA: 609 fmt = will; 610 break; 611 612 default: 613 fmt = wont; 614 break; 615 } 616 sprintf(nfrontp, fmt, option); 617 nfrontp += sizeof(doopt) - 2; 618 if (showoptions) 619 printoption("<--", fmt, option); 620 } 621 622 /* 623 * Set the escape character. 624 */ 625 setescape(argc, argv) 626 int argc; 627 char *argv[]; 628 { 629 register char *arg; 630 char buf[50]; 631 632 if (argc > 2) 633 arg = argv[1]; 634 else { 635 printf("new escape character: "); 636 gets(buf); 637 arg = buf; 638 } 639 if (arg[0] != '\0') 640 escape = arg[0]; 641 printf("Escape character is '%s'.\n", control(escape)); 642 } 643 644 /*VARARGS*/ 645 setoptions() 646 { 647 showoptions = !showoptions; 648 printf("%s show option processing.\n", showoptions ? "Will" : "Wont"); 649 } 650 651 /* 652 * Construct a control character sequence 653 * for a special character. 654 */ 655 char * 656 control(c) 657 register int c; 658 { 659 static char buf[3]; 660 661 if (c == 0177) 662 return ("^?"); 663 if (c >= 040) { 664 buf[0] = c; 665 buf[1] = 0; 666 } else { 667 buf[0] = '^'; 668 buf[1] = '@'+c; 669 buf[2] = 0; 670 } 671 return (buf); 672 } 673 674 struct cmd * 675 getcmd(name) 676 register char *name; 677 { 678 register char *p, *q; 679 register struct cmd *c, *found; 680 register int nmatches, longest; 681 682 longest = 0; 683 nmatches = 0; 684 found = 0; 685 for (c = cmdtab; p = c->name; c++) { 686 for (q = name; *q == *p++; q++) 687 if (*q == 0) /* exact match? */ 688 return (c); 689 if (!*q) { /* the name was a prefix */ 690 if (q - name > longest) { 691 longest = q - name; 692 nmatches = 1; 693 found = c; 694 } else if (q - name == longest) 695 nmatches++; 696 } 697 } 698 if (nmatches > 1) 699 return ((struct cmd *)-1); 700 return (found); 701 } 702 703 deadpeer() 704 { 705 sigset(SIGPIPE, deadpeer); 706 mode(0); 707 longjmp(peerdied, -1); 708 } 709 710 intr() 711 { 712 sigset(SIGINT, intr); 713 mode(0); 714 longjmp(toplevel, -1); 715 } 716 717 ttyflush(fd) 718 { 719 int n; 720 721 if ((n = tfrontp - tbackp) > 0) 722 n = write(fd, tbackp, n); 723 if (n < 0 && errno == EWOULDBLOCK) 724 n = 0; 725 tbackp += n; 726 if (tbackp == tfrontp) 727 tbackp = tfrontp = ttyobuf; 728 } 729 730 netflush(fd) 731 { 732 int n; 733 734 if ((n = nfrontp - nbackp) > 0) 735 n = write(fd, nbackp, n); 736 if (n < 0 && errno == EWOULDBLOCK) 737 n = 0; 738 nbackp += n; 739 if (nbackp == nfrontp) 740 nbackp = nfrontp = netobuf; 741 } 742 743 /*VARARGS*/ 744 printoption(direction, fmt, option, what) 745 char *direction, *fmt; 746 int option, what; 747 { 748 printf("%s ", direction); 749 if (fmt == doopt) 750 fmt = "do"; 751 else if (fmt == dont) 752 fmt = "dont"; 753 else if (fmt == will) 754 fmt = "will"; 755 else if (fmt == wont) 756 fmt = "wont"; 757 else 758 fmt = "???"; 759 if (option < TELOPT_SUPDUP) 760 printf("%s %s", fmt, telopts[option]); 761 else 762 printf("%s %d", fmt, option); 763 if (*direction == '<') { 764 printf("\r\n"); 765 return; 766 } 767 printf(" (%s)\r\n", what ? "reply" : "don't reply"); 768 } 769