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