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