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