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