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