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