1 #ifndef lint 2 static char sccsid[] = "@(#)telnetd.c 4.23 (Berkeley) 83/07/02"; 3 #endif 4 5 /* 6 * Stripped-down telnet server. 7 */ 8 #include <sys/types.h> 9 #include <sys/socket.h> 10 #include <sys/wait.h> 11 12 #include <netinet/in.h> 13 14 #include <arpa/telnet.h> 15 16 #include <stdio.h> 17 #include <signal.h> 18 #include <errno.h> 19 #include <sgtty.h> 20 #include <netdb.h> 21 #include <getty.h> 22 23 #define BELL '\07' 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 /* 34 * I/O data buffers, pointers, and counters. 35 */ 36 char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 37 char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 38 char netibuf[BUFSIZ], *netip = netibuf; 39 char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 40 int pcc, ncc; 41 42 int pty, net; 43 int inter; 44 int reapchild(); 45 extern int errno; 46 char line[] = "/dev/ptyp0"; 47 48 struct sockaddr_in sin = { AF_INET }; 49 50 main(argc, argv) 51 char *argv[]; 52 { 53 int s, pid, options; 54 struct servent *sp; 55 56 sp = getservbyname("telnet", "tcp"); 57 if (sp == 0) { 58 fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 59 exit(1); 60 } 61 sin.sin_port = sp->s_port; 62 argc--, argv++; 63 if (argc > 0 && !strcmp(*argv, "-d")) { 64 options |= SO_DEBUG; 65 argc--, argv++; 66 } 67 if (argc > 0) { 68 sin.sin_port = atoi(*argv); 69 if (sin.sin_port <= 0) { 70 fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 71 exit(1); 72 } 73 sin.sin_port = htons((u_short)sin.sin_port); 74 } 75 #ifndef DEBUG 76 if (fork()) 77 exit(0); 78 for (s = 0; s < 10; s++) 79 (void) close(s); 80 (void) open("/", 0); 81 (void) dup2(0, 1); 82 (void) dup2(0, 2); 83 { int tt = open("/dev/tty", 2); 84 if (tt > 0) { 85 ioctl(tt, TIOCNOTTY, 0); 86 close(tt); 87 } 88 } 89 #endif 90 again: 91 s = socket(AF_INET, SOCK_STREAM, 0, 0); 92 if (s < 0) { 93 perror("telnetd: socket");; 94 sleep(5); 95 goto again; 96 } 97 if (options & SO_DEBUG) 98 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 99 perror("telnetd: setsockopt (SO_DEBUG)"); 100 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 101 perror("telnetd: setsockopt (SO_KEEPALIVE)"); 102 while (bind(s, (caddr_t)&sin, sizeof (sin), 0) < 0) { 103 perror("telnetd: bind"); 104 sleep(5); 105 } 106 signal(SIGCHLD, reapchild); 107 listen(s, 10); 108 for (;;) { 109 struct sockaddr_in from; 110 int s2, fromlen = sizeof (from); 111 112 s2 = accept(s, (caddr_t)&from, &fromlen); 113 if (s2 < 0) { 114 if (errno == EINTR) 115 continue; 116 perror("telnetd: accept"); 117 sleep(1); 118 continue; 119 } 120 if ((pid = fork()) < 0) 121 printf("Out of processes\n"); 122 else if (pid == 0) { 123 signal(SIGCHLD, SIG_IGN); 124 doit(s2, &from); 125 } 126 close(s2); 127 } 128 /*NOTREACHED*/ 129 } 130 131 reapchild() 132 { 133 union wait status; 134 135 while (wait3(&status, WNOHANG, 0) > 0) 136 ; 137 } 138 139 int cleanup(); 140 141 /* 142 * Get a pty, scan input lines. 143 */ 144 doit(f, who) 145 int f; 146 struct sockaddr_in *who; 147 { 148 char *cp = line, *host, *ntoa(); 149 int i, p, cc, t; 150 struct sgttyb b; 151 struct hostent *hp; 152 153 for (i = 0; i < 16; i++) { 154 cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 155 p = open(cp, 2); 156 if (p > 0) 157 goto gotpty; 158 } 159 fatal(f, "All network ports in use"); 160 /*NOTREACHED*/ 161 gotpty: 162 dup2(f, 0); 163 cp[strlen("/dev/")] = 't'; 164 t = open("/dev/tty", 2); 165 if (t >= 0) { 166 ioctl(t, TIOCNOTTY, 0); 167 close(t); 168 } 169 t = open(cp, 2); 170 if (t < 0) 171 fatalperror(f, cp, errno); 172 ioctl(t, TIOCGETP, &b); 173 b.sg_flags = CRMOD|XTABS|ANYP; 174 ioctl(t, TIOCSETP, &b); 175 ioctl(p, TIOCGETP, &b); 176 b.sg_flags &= ~ECHO; 177 ioctl(p, TIOCSETP, &b); 178 hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 179 who->sin_family); 180 if (hp) 181 host = hp->h_name; 182 else 183 host = ntoa(who->sin_addr); 184 if ((i = fork()) < 0) 185 fatalperror(f, "fork", errno); 186 if (i) 187 telnet(f, p); 188 close(f); 189 close(p); 190 dup2(t, 0); 191 dup2(t, 1); 192 dup2(t, 2); 193 close(t); 194 execl("/bin/login", "login", "-h", host, 0); 195 fatalperror(f, "/bin/login", errno); 196 /*NOTREACHED*/ 197 } 198 199 fatal(f, msg) 200 int f; 201 char *msg; 202 { 203 char buf[BUFSIZ]; 204 205 (void) sprintf(buf, "telnetd: %s.\n", msg); 206 (void) write(f, buf, strlen(buf)); 207 exit(1); 208 } 209 210 fatalperror(f, msg, errno) 211 int f; 212 char *msg; 213 int errno; 214 { 215 char buf[BUFSIZ]; 216 extern char *sys_errlist[]; 217 218 (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); 219 fatal(f, buf); 220 } 221 222 /* 223 * Main loop. Select from pty and network, and 224 * hand data to telnet receiver finite state machine. 225 */ 226 telnet(f, p) 227 { 228 int on = 1; 229 char hostname[32]; 230 231 net = f, pty = p; 232 ioctl(f, FIONBIO, &on); 233 ioctl(p, FIONBIO, &on); 234 signal(SIGTSTP, SIG_IGN); 235 signal(SIGCHLD, cleanup); 236 237 /* 238 * Request to do remote echo. 239 */ 240 dooption(TELOPT_ECHO); 241 myopts[TELOPT_ECHO] = 1; 242 /* 243 * Show banner that getty never gave. 244 */ 245 gethostname(hostname, sizeof (hostname)); 246 sprintf(nfrontp, BANNER, hostname, ""); 247 nfrontp += strlen(nfrontp); 248 for (;;) { 249 int ibits = 0, obits = 0; 250 register int c; 251 252 /* 253 * Never look for input if there's still 254 * stuff in the corresponding output buffer 255 */ 256 if (nfrontp - nbackp) 257 obits |= (1 << f); 258 else 259 ibits |= (1 << p); 260 if (pfrontp - pbackp) 261 obits |= (1 << p); 262 else 263 ibits |= (1 << f); 264 if (ncc < 0 && pcc < 0) 265 break; 266 select(16, &ibits, &obits, 0, 0); 267 if (ibits == 0 && obits == 0) { 268 sleep(5); 269 continue; 270 } 271 272 /* 273 * Something to read from the network... 274 */ 275 if (ibits & (1 << f)) { 276 ncc = read(f, netibuf, BUFSIZ); 277 if (ncc < 0 && errno == EWOULDBLOCK) 278 ncc = 0; 279 else { 280 if (ncc <= 0) 281 break; 282 netip = netibuf; 283 } 284 } 285 286 /* 287 * Something to read from the pty... 288 */ 289 if (ibits & (1 << p)) { 290 pcc = read(p, ptyibuf, BUFSIZ); 291 if (pcc < 0 && errno == EWOULDBLOCK) 292 pcc = 0; 293 else { 294 if (pcc <= 0) 295 break; 296 ptyip = ptyibuf; 297 } 298 } 299 300 while (pcc > 0) { 301 if ((&netobuf[BUFSIZ] - nfrontp) < 2) 302 break; 303 c = *ptyip++ & 0377, pcc--; 304 if (c == IAC) 305 *nfrontp++ = c; 306 *nfrontp++ = c; 307 } 308 if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 309 netflush(); 310 if (ncc > 0) 311 telrcv(); 312 if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 313 ptyflush(); 314 } 315 cleanup(); 316 } 317 318 /* 319 * State for recv fsm 320 */ 321 #define TS_DATA 0 /* base state */ 322 #define TS_IAC 1 /* look for double IAC's */ 323 #define TS_CR 2 /* CR-LF ->'s CR */ 324 #define TS_BEGINNEG 3 /* throw away begin's... */ 325 #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 326 #define TS_WILL 5 /* will option negotiation */ 327 #define TS_WONT 6 /* wont " */ 328 #define TS_DO 7 /* do " */ 329 #define TS_DONT 8 /* dont " */ 330 331 telrcv() 332 { 333 register int c; 334 static int state = TS_DATA; 335 struct sgttyb b; 336 337 while (ncc > 0) { 338 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 339 return; 340 c = *netip++ & 0377, ncc--; 341 switch (state) { 342 343 case TS_DATA: 344 if (c == IAC) { 345 state = TS_IAC; 346 break; 347 } 348 if (inter > 0) 349 break; 350 *pfrontp++ = c; 351 if (!myopts[TELOPT_BINARY] && c == '\r') 352 state = TS_CR; 353 break; 354 355 case TS_CR: 356 if (c && c != '\n') 357 *pfrontp++ = c; 358 state = TS_DATA; 359 break; 360 361 case TS_IAC: 362 switch (c) { 363 364 /* 365 * Send the process on the pty side an 366 * interrupt. Do this with a NULL or 367 * interrupt char; depending on the tty mode. 368 */ 369 case BREAK: 370 case IP: 371 interrupt(); 372 break; 373 374 /* 375 * Are You There? 376 */ 377 case AYT: 378 *pfrontp++ = BELL; 379 break; 380 381 /* 382 * Erase Character and 383 * Erase Line 384 */ 385 case EC: 386 case EL: 387 ptyflush(); /* half-hearted */ 388 ioctl(pty, TIOCGETP, &b); 389 *pfrontp++ = (c == EC) ? 390 b.sg_erase : b.sg_kill; 391 break; 392 393 /* 394 * Check for urgent data... 395 */ 396 case DM: 397 break; 398 399 /* 400 * Begin option subnegotiation... 401 */ 402 case SB: 403 state = TS_BEGINNEG; 404 continue; 405 406 case WILL: 407 case WONT: 408 case DO: 409 case DONT: 410 state = TS_WILL + (c - WILL); 411 continue; 412 413 case IAC: 414 *pfrontp++ = c; 415 break; 416 } 417 state = TS_DATA; 418 break; 419 420 case TS_BEGINNEG: 421 if (c == IAC) 422 state = TS_ENDNEG; 423 break; 424 425 case TS_ENDNEG: 426 state = c == SE ? TS_DATA : TS_BEGINNEG; 427 break; 428 429 case TS_WILL: 430 if (!hisopts[c]) 431 willoption(c); 432 state = TS_DATA; 433 continue; 434 435 case TS_WONT: 436 if (hisopts[c]) 437 wontoption(c); 438 state = TS_DATA; 439 continue; 440 441 case TS_DO: 442 if (!myopts[c]) 443 dooption(c); 444 state = TS_DATA; 445 continue; 446 447 case TS_DONT: 448 if (myopts[c]) { 449 myopts[c] = 0; 450 sprintf(nfrontp, wont, c); 451 nfrontp += sizeof (wont) - 2; 452 } 453 state = TS_DATA; 454 continue; 455 456 default: 457 printf("telnetd: panic state=%d\n", state); 458 exit(1); 459 } 460 } 461 } 462 463 willoption(option) 464 int option; 465 { 466 char *fmt; 467 468 switch (option) { 469 470 case TELOPT_BINARY: 471 mode(RAW, 0); 472 goto common; 473 474 case TELOPT_ECHO: 475 mode(0, ECHO|CRMOD); 476 /*FALL THRU*/ 477 478 case TELOPT_SGA: 479 common: 480 hisopts[option] = 1; 481 fmt = doopt; 482 break; 483 484 case TELOPT_TM: 485 fmt = dont; 486 break; 487 488 default: 489 fmt = dont; 490 break; 491 } 492 sprintf(nfrontp, fmt, option); 493 nfrontp += sizeof (dont) - 2; 494 } 495 496 wontoption(option) 497 int option; 498 { 499 char *fmt; 500 501 switch (option) { 502 503 case TELOPT_ECHO: 504 mode(ECHO|CRMOD, 0); 505 goto common; 506 507 case TELOPT_BINARY: 508 mode(0, RAW); 509 /*FALL THRU*/ 510 511 case TELOPT_SGA: 512 common: 513 hisopts[option] = 0; 514 fmt = dont; 515 break; 516 517 default: 518 fmt = dont; 519 } 520 sprintf(nfrontp, fmt, option); 521 nfrontp += sizeof (doopt) - 2; 522 } 523 524 dooption(option) 525 int option; 526 { 527 char *fmt; 528 529 switch (option) { 530 531 case TELOPT_TM: 532 fmt = wont; 533 break; 534 535 case TELOPT_ECHO: 536 mode(ECHO|CRMOD, 0); 537 goto common; 538 539 case TELOPT_BINARY: 540 mode(RAW, 0); 541 /*FALL THRU*/ 542 543 case TELOPT_SGA: 544 common: 545 fmt = will; 546 break; 547 548 default: 549 fmt = wont; 550 break; 551 } 552 sprintf(nfrontp, fmt, option); 553 nfrontp += sizeof (doopt) - 2; 554 } 555 556 mode(on, off) 557 int on, off; 558 { 559 struct sgttyb b; 560 561 ptyflush(); 562 ioctl(pty, TIOCGETP, &b); 563 b.sg_flags |= on; 564 b.sg_flags &= ~off; 565 ioctl(pty, TIOCSETP, &b); 566 } 567 568 /* 569 * Send interrupt to process on other side of pty. 570 * If it is in raw mode, just write NULL; 571 * otherwise, write intr char. 572 */ 573 interrupt() 574 { 575 struct sgttyb b; 576 struct tchars tchars; 577 578 ptyflush(); /* half-hearted */ 579 ioctl(pty, TIOCGETP, &b); 580 if (b.sg_flags & RAW) { 581 *pfrontp++ = '\0'; 582 return; 583 } 584 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 585 '\177' : tchars.t_intrc; 586 } 587 588 ptyflush() 589 { 590 int n; 591 592 if ((n = pfrontp - pbackp) > 0) 593 n = write(pty, pbackp, n); 594 if (n < 0) 595 return; 596 pbackp += n; 597 if (pbackp == pfrontp) 598 pbackp = pfrontp = ptyobuf; 599 } 600 601 netflush() 602 { 603 int n; 604 605 if ((n = nfrontp - nbackp) > 0) 606 n = write(net, nbackp, n); 607 if (n < 0) { 608 if (errno == EWOULDBLOCK) 609 return; 610 /* should blow this guy away... */ 611 return; 612 } 613 nbackp += n; 614 if (nbackp == nfrontp) 615 nbackp = nfrontp = netobuf; 616 } 617 618 cleanup() 619 { 620 621 rmut(); 622 vhangup(); /* XXX */ 623 shutdown(net, 2); 624 kill(0, SIGKILL); 625 exit(1); 626 } 627 628 #include <utmp.h> 629 630 struct utmp wtmp; 631 char wtmpf[] = "/usr/adm/wtmp"; 632 char utmp[] = "/etc/utmp"; 633 #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 634 #define SCMPN(a, b) strncmp(a, b, sizeof (a)) 635 636 rmut() 637 { 638 register f; 639 int found = 0; 640 641 f = open(utmp, 2); 642 if (f >= 0) { 643 while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) { 644 if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0) 645 continue; 646 lseek(f, -(long)sizeof (wtmp), 1); 647 SCPYN(wtmp.ut_name, ""); 648 SCPYN(wtmp.ut_host, ""); 649 time(&wtmp.ut_time); 650 write(f, (char *)&wtmp, sizeof (wtmp)); 651 found++; 652 } 653 close(f); 654 } 655 if (found) { 656 f = open(wtmpf, 1); 657 if (f >= 0) { 658 SCPYN(wtmp.ut_line, line+5); 659 SCPYN(wtmp.ut_name, ""); 660 SCPYN(wtmp.ut_host, ""); 661 time(&wtmp.ut_time); 662 lseek(f, (long)0, 2); 663 write(f, (char *)&wtmp, sizeof (wtmp)); 664 close(f); 665 } 666 } 667 chmod(line, 0666); 668 chown(line, 0, 0); 669 line[strlen("/dev/")] = 'p'; 670 chmod(line, 0666); 671 chown(line, 0, 0); 672 } 673 674 /* 675 * Convert network-format internet address 676 * to base 256 d.d.d.d representation. 677 */ 678 char * 679 ntoa(in) 680 struct in_addr in; 681 { 682 static char b[18]; 683 register char *p; 684 685 p = (char *)∈ 686 #define UC(b) (((int)b)&0xff) 687 sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 688 return (b); 689 } 690