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