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