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