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