|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1983,1986 Regents of the University of California. ! 3: * All rights reserved. The Berkeley software License Agreement ! 4: * specifies the terms and conditions for redistribution. ! 5: */ ! 6: ! 7: #ifndef lint ! 8: char copyright[] = ! 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ ! 10: All rights reserved.\n"; ! 11: #endif not lint ! 12: ! 13: #ifndef lint ! 14: static char sccsid[] = "@(#)telnetd.c 5.18 (Berkeley) 5/12/86"; ! 15: #endif not lint ! 16: ! 17: /* ! 18: * Telnet server. ! 19: */ ! 20: #include <sys/param.h> ! 21: #include <sys/socket.h> ! 22: #include <sys/wait.h> ! 23: #include <sys/file.h> ! 24: #include <sys/stat.h> ! 25: #include <sys/time.h> ! 26: ! 27: #include <netinet/in.h> ! 28: ! 29: #include <arpa/telnet.h> ! 30: ! 31: #include <stdio.h> ! 32: #include <signal.h> ! 33: #include <errno.h> ! 34: #include <sgtty.h> ! 35: #include <netdb.h> ! 36: #include <syslog.h> ! 37: #include <ctype.h> ! 38: ! 39: #define OPT_NO 0 /* won't do this option */ ! 40: #define OPT_YES 1 /* will do this option */ ! 41: #define OPT_YES_BUT_ALWAYS_LOOK 2 ! 42: #define OPT_NO_BUT_ALWAYS_LOOK 3 ! 43: char hisopts[256]; ! 44: char myopts[256]; ! 45: ! 46: char doopt[] = { IAC, DO, '%', 'c', 0 }; ! 47: char dont[] = { IAC, DONT, '%', 'c', 0 }; ! 48: char will[] = { IAC, WILL, '%', 'c', 0 }; ! 49: char wont[] = { IAC, WONT, '%', 'c', 0 }; ! 50: ! 51: /* ! 52: * I/O data buffers, pointers, and counters. ! 53: */ ! 54: char ptyibuf[BUFSIZ], *ptyip = ptyibuf; ! 55: ! 56: char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; ! 57: ! 58: char netibuf[BUFSIZ], *netip = netibuf; ! 59: #define NIACCUM(c) { *netip++ = c; \ ! 60: ncc++; \ ! 61: } ! 62: ! 63: char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; ! 64: char *neturg = 0; /* one past last bye of urgent data */ ! 65: /* the remote system seems to NOT be an old 4.2 */ ! 66: int not42 = 1; ! 67: ! 68: ! 69: char BANNER1[] = "\r\n\r\n4.3 BSD UNIX (", ! 70: BANNER2[] = ")\r\n\r\0\r\n\r\0"; ! 71: ! 72: /* buffer for sub-options */ ! 73: char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; ! 74: #define SB_CLEAR() subpointer = subbuffer; ! 75: #define SB_TERM() { subend = subpointer; SB_CLEAR(); } ! 76: #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ ! 77: *subpointer++ = (c); \ ! 78: } ! 79: #define SB_GET() ((*subpointer++)&0xff) ! 80: #define SB_EOF() (subpointer >= subend) ! 81: ! 82: int pcc, ncc; ! 83: ! 84: int pty, net; ! 85: int inter; ! 86: extern char **environ; ! 87: extern int errno; ! 88: char *line; ! 89: int SYNCHing = 0; /* we are in TELNET SYNCH mode */ ! 90: /* ! 91: * The following are some clocks used to decide how to interpret ! 92: * the relationship between various variables. ! 93: */ ! 94: ! 95: struct { ! 96: int ! 97: system, /* what the current time is */ ! 98: echotoggle, /* last time user entered echo character */ ! 99: modenegotiated, /* last time operating mode negotiated */ ! 100: didnetreceive, /* last time we read data from network */ ! 101: ttypeopt, /* ttype will/won't received */ ! 102: ttypesubopt, /* ttype subopt is received */ ! 103: getterminal, /* time started to get terminal information */ ! 104: gotDM; /* when did we last see a data mark */ ! 105: } clocks; ! 106: ! 107: #define settimer(x) (clocks.x = ++clocks.system) ! 108: #define sequenceIs(x,y) (clocks.x < clocks.y) ! 109: ! 110: main(argc, argv) ! 111: char *argv[]; ! 112: { ! 113: struct sockaddr_in from; ! 114: int on = 1, fromlen; ! 115: ! 116: #if defined(DEBUG) ! 117: { ! 118: int s, ns, foo; ! 119: struct servent *sp; ! 120: static struct sockaddr_in sin = { AF_INET }; ! 121: ! 122: sp = getservbyname("telnet", "tcp"); ! 123: if (sp == 0) { ! 124: fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); ! 125: exit(1); ! 126: } ! 127: sin.sin_port = sp->s_port; ! 128: argc--, argv++; ! 129: if (argc > 0) { ! 130: sin.sin_port = atoi(*argv); ! 131: sin.sin_port = htons((u_short)sin.sin_port); ! 132: } ! 133: ! 134: s = socket(AF_INET, SOCK_STREAM, 0); ! 135: if (s < 0) { ! 136: perror("telnetd: socket");; ! 137: exit(1); ! 138: } ! 139: if (bind(s, &sin, sizeof sin) < 0) { ! 140: perror("bind"); ! 141: exit(1); ! 142: } ! 143: if (listen(s, 1) < 0) { ! 144: perror("listen"); ! 145: exit(1); ! 146: } ! 147: foo = sizeof sin; ! 148: ns = accept(s, &sin, &foo); ! 149: if (ns < 0) { ! 150: perror("accept"); ! 151: exit(1); ! 152: } ! 153: dup2(ns, 0); ! 154: close(s); ! 155: } ! 156: #endif /* defined(DEBUG) */ ! 157: openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); ! 158: fromlen = sizeof (from); ! 159: if (getpeername(0, &from, &fromlen) < 0) { ! 160: fprintf(stderr, "%s: ", argv[0]); ! 161: perror("getpeername"); ! 162: _exit(1); ! 163: } ! 164: if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { ! 165: syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); ! 166: } ! 167: doit(0, &from); ! 168: } ! 169: ! 170: char *terminaltype = 0; ! 171: char *envinit[2]; ! 172: int cleanup(); ! 173: ! 174: /* ! 175: * ttloop ! 176: * ! 177: * A small subroutine to flush the network output buffer, get some data ! 178: * from the network, and pass it through the telnet state machine. We ! 179: * also flush the pty input buffer (by dropping its data) if it becomes ! 180: * too full. ! 181: */ ! 182: ! 183: void ! 184: ttloop() ! 185: { ! 186: if (nfrontp-nbackp) { ! 187: netflush(); ! 188: } ! 189: ncc = read(net, netibuf, sizeof netibuf); ! 190: if (ncc < 0) { ! 191: syslog(LOG_INFO, "ttloop: read: %m\n"); ! 192: exit(1); ! 193: } else if (ncc == 0) { ! 194: syslog(LOG_INFO, "ttloop: peer died: %m\n"); ! 195: exit(1); ! 196: } ! 197: netip = netibuf; ! 198: telrcv(); /* state machine */ ! 199: if (ncc > 0) { ! 200: pfrontp = pbackp = ptyobuf; ! 201: telrcv(); ! 202: } ! 203: } ! 204: ! 205: /* ! 206: * getterminaltype ! 207: * ! 208: * Ask the other end to send along its terminal type. ! 209: * Output is the variable terminaltype filled in. ! 210: */ ! 211: ! 212: void ! 213: getterminaltype() ! 214: { ! 215: static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; ! 216: ! 217: settimer(getterminal); ! 218: bcopy(sbuf, nfrontp, sizeof sbuf); ! 219: nfrontp += sizeof sbuf; ! 220: hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; ! 221: while (sequenceIs(ttypeopt, getterminal)) { ! 222: ttloop(); ! 223: } ! 224: if (hisopts[TELOPT_TTYPE] == OPT_YES) { ! 225: static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; ! 226: ! 227: bcopy(sbbuf, nfrontp, sizeof sbbuf); ! 228: nfrontp += sizeof sbbuf; ! 229: while (sequenceIs(ttypesubopt, getterminal)) { ! 230: ttloop(); ! 231: } ! 232: } ! 233: } ! 234: ! 235: /* ! 236: * Get a pty, scan input lines. ! 237: */ ! 238: doit(f, who) ! 239: int f; ! 240: struct sockaddr_in *who; ! 241: { ! 242: char *host, *inet_ntoa(); ! 243: int i, p, t; ! 244: struct sgttyb b; ! 245: struct hostent *hp; ! 246: int c; ! 247: ! 248: for (c = 'p'; c <= 's'; c++) { ! 249: struct stat stb; ! 250: ! 251: line = "/dev/ptyXX"; ! 252: line[strlen("/dev/pty")] = c; ! 253: line[strlen("/dev/ptyp")] = '0'; ! 254: if (stat(line, &stb) < 0) ! 255: break; ! 256: for (i = 0; i < 16; i++) { ! 257: line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; ! 258: p = open(line, 2); ! 259: if (p > 0) ! 260: goto gotpty; ! 261: } ! 262: } ! 263: fatal(f, "All network ports in use"); ! 264: /*NOTREACHED*/ ! 265: gotpty: ! 266: dup2(f, 0); ! 267: line[strlen("/dev/")] = 't'; ! 268: t = open("/dev/tty", O_RDWR); ! 269: if (t >= 0) { ! 270: ioctl(t, TIOCNOTTY, 0); ! 271: close(t); ! 272: } ! 273: t = open(line, O_RDWR); ! 274: if (t < 0) ! 275: fatalperror(f, line, errno); ! 276: ioctl(t, TIOCGETP, &b); ! 277: b.sg_flags = CRMOD|XTABS|ANYP; ! 278: ioctl(t, TIOCSETP, &b); ! 279: ioctl(p, TIOCGETP, &b); ! 280: b.sg_flags &= ~ECHO; ! 281: ioctl(p, TIOCSETP, &b); ! 282: hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), ! 283: who->sin_family); ! 284: if (hp) ! 285: host = hp->h_name; ! 286: else ! 287: host = inet_ntoa(who->sin_addr); ! 288: ! 289: net = f; ! 290: pty = p; ! 291: ! 292: /* ! 293: * get terminal type. ! 294: */ ! 295: getterminaltype(); ! 296: ! 297: if ((i = fork()) < 0) ! 298: fatalperror(f, "fork", errno); ! 299: if (i) ! 300: telnet(f, p); ! 301: close(f); ! 302: close(p); ! 303: dup2(t, 0); ! 304: dup2(t, 1); ! 305: dup2(t, 2); ! 306: close(t); ! 307: envinit[0] = terminaltype; ! 308: envinit[1] = 0; ! 309: environ = envinit; ! 310: /* ! 311: * -h : pass on name of host. ! 312: * WARNING: -h is accepted by login if and only if ! 313: * getuid() == 0. ! 314: * -p : don't clobber the environment (so terminal type stays set). ! 315: */ ! 316: execl("/bin/login", "login", "-h", host, ! 317: terminaltype ? "-p" : 0, 0); ! 318: fatalperror(f, "/bin/login", errno); ! 319: /*NOTREACHED*/ ! 320: } ! 321: ! 322: fatal(f, msg) ! 323: int f; ! 324: char *msg; ! 325: { ! 326: char buf[BUFSIZ]; ! 327: ! 328: (void) sprintf(buf, "telnetd: %s.\r\n", msg); ! 329: (void) write(f, buf, strlen(buf)); ! 330: exit(1); ! 331: } ! 332: ! 333: fatalperror(f, msg, errno) ! 334: int f; ! 335: char *msg; ! 336: int errno; ! 337: { ! 338: char buf[BUFSIZ]; ! 339: extern char *sys_errlist[]; ! 340: ! 341: (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); ! 342: fatal(f, buf); ! 343: } ! 344: ! 345: ! 346: /* ! 347: * Check a descriptor to see if out of band data exists on it. ! 348: */ ! 349: ! 350: ! 351: stilloob(s) ! 352: int s; /* socket number */ ! 353: { ! 354: static struct timeval timeout = { 0 }; ! 355: fd_set excepts; ! 356: int value; ! 357: ! 358: do { ! 359: FD_ZERO(&excepts); ! 360: FD_SET(s, &excepts); ! 361: value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); ! 362: } while ((value == -1) && (errno == EINTR)); ! 363: ! 364: if (value < 0) { ! 365: fatalperror(pty, "select", errno); ! 366: } ! 367: if (FD_ISSET(s, &excepts)) { ! 368: return 1; ! 369: } else { ! 370: return 0; ! 371: } ! 372: } ! 373: ! 374: /* ! 375: * Main loop. Select from pty and network, and ! 376: * hand data to telnet receiver finite state machine. ! 377: */ ! 378: telnet(f, p) ! 379: { ! 380: int on = 1; ! 381: char hostname[MAXHOSTNAMELEN]; ! 382: ! 383: ioctl(f, FIONBIO, &on); ! 384: ioctl(p, FIONBIO, &on); ! 385: #if defined(SO_OOBINLINE) ! 386: setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); ! 387: #endif /* defined(SO_OOBINLINE) */ ! 388: signal(SIGTSTP, SIG_IGN); ! 389: signal(SIGCHLD, cleanup); ! 390: setpgrp(0, 0); ! 391: ! 392: /* ! 393: * Request to do remote echo and to suppress go ahead. ! 394: */ ! 395: if (!myopts[TELOPT_ECHO]) { ! 396: dooption(TELOPT_ECHO); ! 397: } ! 398: if (!myopts[TELOPT_SGA]) { ! 399: dooption(TELOPT_SGA); ! 400: } ! 401: /* ! 402: * Is the client side a 4.2 (NOT 4.3) system? We need to know this ! 403: * because 4.2 clients are unable to deal with TCP urgent data. ! 404: * ! 405: * To find out, we send out a "DO ECHO". If the remote system ! 406: * answers "WILL ECHO" it is probably a 4.2 client, and we note ! 407: * that fact ("WILL ECHO" ==> that the client will echo what ! 408: * WE, the server, sends it; it does NOT mean that the client will ! 409: * echo the terminal input). ! 410: */ ! 411: sprintf(nfrontp, doopt, TELOPT_ECHO); ! 412: nfrontp += sizeof doopt-2; ! 413: hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; ! 414: ! 415: /* ! 416: * Show banner that getty never gave. ! 417: * ! 418: * The banner includes some null's (for TELNET CR disambiguation), ! 419: * so we have to be somewhat complicated. ! 420: */ ! 421: ! 422: gethostname(hostname, sizeof (hostname)); ! 423: ! 424: bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); ! 425: nfrontp += sizeof BANNER1 - 1; ! 426: bcopy(hostname, nfrontp, strlen(hostname)); ! 427: nfrontp += strlen(hostname); ! 428: bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); ! 429: nfrontp += sizeof BANNER2 - 1; ! 430: ! 431: /* ! 432: * Call telrcv() once to pick up anything received during ! 433: * terminal type negotiation. ! 434: */ ! 435: telrcv(); ! 436: ! 437: for (;;) { ! 438: fd_set ibits, obits, xbits; ! 439: register int c; ! 440: ! 441: if (ncc < 0 && pcc < 0) ! 442: break; ! 443: ! 444: FD_ZERO(&ibits); ! 445: FD_ZERO(&obits); ! 446: FD_ZERO(&xbits); ! 447: /* ! 448: * Never look for input if there's still ! 449: * stuff in the corresponding output buffer ! 450: */ ! 451: if (nfrontp - nbackp || pcc > 0) { ! 452: FD_SET(f, &obits); ! 453: } else { ! 454: FD_SET(p, &ibits); ! 455: } ! 456: if (pfrontp - pbackp || ncc > 0) { ! 457: FD_SET(p, &obits); ! 458: } else { ! 459: FD_SET(f, &ibits); ! 460: } ! 461: if (!SYNCHing) { ! 462: FD_SET(f, &xbits); ! 463: } ! 464: if ((c = select(16, &ibits, &obits, &xbits, ! 465: (struct timeval *)0)) < 1) { ! 466: if (c == -1) { ! 467: if (errno == EINTR) { ! 468: continue; ! 469: } ! 470: } ! 471: sleep(5); ! 472: continue; ! 473: } ! 474: ! 475: /* ! 476: * Any urgent data? ! 477: */ ! 478: if (FD_ISSET(net, &xbits)) { ! 479: SYNCHing = 1; ! 480: } ! 481: ! 482: /* ! 483: * Something to read from the network... ! 484: */ ! 485: if (FD_ISSET(net, &ibits)) { ! 486: #if !defined(SO_OOBINLINE) ! 487: /* ! 488: * In 4.2 (and 4.3 beta) systems, the ! 489: * OOB indication and data handling in the kernel ! 490: * is such that if two separate TCP Urgent requests ! 491: * come in, one byte of TCP data will be overlaid. ! 492: * This is fatal for Telnet, but we try to live ! 493: * with it. ! 494: * ! 495: * In addition, in 4.2 (and...), a special protocol ! 496: * is needed to pick up the TCP Urgent data in ! 497: * the correct sequence. ! 498: * ! 499: * What we do is: if we think we are in urgent ! 500: * mode, we look to see if we are "at the mark". ! 501: * If we are, we do an OOB receive. If we run ! 502: * this twice, we will do the OOB receive twice, ! 503: * but the second will fail, since the second ! 504: * time we were "at the mark", but there wasn't ! 505: * any data there (the kernel doesn't reset ! 506: * "at the mark" until we do a normal read). ! 507: * Once we've read the OOB data, we go ahead ! 508: * and do normal reads. ! 509: * ! 510: * There is also another problem, which is that ! 511: * since the OOB byte we read doesn't put us ! 512: * out of OOB state, and since that byte is most ! 513: * likely the TELNET DM (data mark), we would ! 514: * stay in the TELNET SYNCH (SYNCHing) state. ! 515: * So, clocks to the rescue. If we've "just" ! 516: * received a DM, then we test for the ! 517: * presence of OOB data when the receive OOB ! 518: * fails (and AFTER we did the normal mode read ! 519: * to clear "at the mark"). ! 520: */ ! 521: if (SYNCHing) { ! 522: int atmark; ! 523: ! 524: ioctl(net, SIOCATMARK, (char *)&atmark); ! 525: if (atmark) { ! 526: ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); ! 527: if ((ncc == -1) && (errno == EINVAL)) { ! 528: ncc = read(net, netibuf, sizeof (netibuf)); ! 529: if (sequenceIs(didnetreceive, gotDM)) { ! 530: SYNCHing = stilloob(net); ! 531: } ! 532: } ! 533: } else { ! 534: ncc = read(net, netibuf, sizeof (netibuf)); ! 535: } ! 536: } else { ! 537: ncc = read(net, netibuf, sizeof (netibuf)); ! 538: } ! 539: settimer(didnetreceive); ! 540: #else /* !defined(SO_OOBINLINE)) */ ! 541: ncc = read(net, netibuf, sizeof (netibuf)); ! 542: #endif /* !defined(SO_OOBINLINE)) */ ! 543: if (ncc < 0 && errno == EWOULDBLOCK) ! 544: ncc = 0; ! 545: else { ! 546: if (ncc <= 0) { ! 547: break; ! 548: } ! 549: netip = netibuf; ! 550: } ! 551: } ! 552: ! 553: /* ! 554: * Something to read from the pty... ! 555: */ ! 556: if (FD_ISSET(p, &ibits)) { ! 557: pcc = read(p, ptyibuf, BUFSIZ); ! 558: if (pcc < 0 && errno == EWOULDBLOCK) ! 559: pcc = 0; ! 560: else { ! 561: if (pcc <= 0) ! 562: break; ! 563: ptyip = ptyibuf; ! 564: } ! 565: } ! 566: ! 567: while (pcc > 0) { ! 568: if ((&netobuf[BUFSIZ] - nfrontp) < 2) ! 569: break; ! 570: c = *ptyip++ & 0377, pcc--; ! 571: if (c == IAC) ! 572: *nfrontp++ = c; ! 573: *nfrontp++ = c; ! 574: if (c == '\r') { ! 575: if (pcc > 0 && ((*ptyip & 0377) == '\n')) { ! 576: *nfrontp++ = *ptyip++ & 0377; ! 577: pcc--; ! 578: } else ! 579: *nfrontp++ = '\0'; ! 580: } ! 581: } ! 582: if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) ! 583: netflush(); ! 584: if (ncc > 0) ! 585: telrcv(); ! 586: if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) ! 587: ptyflush(); ! 588: } ! 589: cleanup(); ! 590: } ! 591: ! 592: /* ! 593: * State for recv fsm ! 594: */ ! 595: #define TS_DATA 0 /* base state */ ! 596: #define TS_IAC 1 /* look for double IAC's */ ! 597: #define TS_CR 2 /* CR-LF ->'s CR */ ! 598: #define TS_SB 3 /* throw away begin's... */ ! 599: #define TS_SE 4 /* ...end's (suboption negotiation) */ ! 600: #define TS_WILL 5 /* will option negotiation */ ! 601: #define TS_WONT 6 /* wont " */ ! 602: #define TS_DO 7 /* do " */ ! 603: #define TS_DONT 8 /* dont " */ ! 604: ! 605: telrcv() ! 606: { ! 607: register int c; ! 608: static int state = TS_DATA; ! 609: ! 610: while (ncc > 0) { ! 611: if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) ! 612: return; ! 613: c = *netip++ & 0377, ncc--; ! 614: switch (state) { ! 615: ! 616: case TS_CR: ! 617: state = TS_DATA; ! 618: if ((c == 0) || (c == '\n')) { ! 619: break; ! 620: } ! 621: /* FALL THROUGH */ ! 622: ! 623: case TS_DATA: ! 624: if (c == IAC) { ! 625: state = TS_IAC; ! 626: break; ! 627: } ! 628: if (inter > 0) ! 629: break; ! 630: /* ! 631: * We map \r\n ==> \n, since \r\n says ! 632: * that we want to be in column 1 of the next ! 633: * printable line, and \n is the standard ! 634: * unix way of saying that (\r is only good ! 635: * if CRMOD is set, which it normally is). ! 636: */ ! 637: if ((myopts[TELOPT_BINARY] == OPT_NO) && c == '\r') { ! 638: if ((ncc > 0) && ('\n' == *netip)) { ! 639: netip++; ncc--; ! 640: c = '\n'; ! 641: } else { ! 642: state = TS_CR; ! 643: } ! 644: } ! 645: *pfrontp++ = c; ! 646: break; ! 647: ! 648: case TS_IAC: ! 649: switch (c) { ! 650: ! 651: /* ! 652: * Send the process on the pty side an ! 653: * interrupt. Do this with a NULL or ! 654: * interrupt char; depending on the tty mode. ! 655: */ ! 656: case IP: ! 657: interrupt(); ! 658: break; ! 659: ! 660: case BREAK: ! 661: sendbrk(); ! 662: break; ! 663: ! 664: /* ! 665: * Are You There? ! 666: */ ! 667: case AYT: ! 668: strcpy(nfrontp, "\r\n[Yes]\r\n"); ! 669: nfrontp += 9; ! 670: break; ! 671: ! 672: /* ! 673: * Abort Output ! 674: */ ! 675: case AO: { ! 676: struct ltchars tmpltc; ! 677: ! 678: ptyflush(); /* half-hearted */ ! 679: ioctl(pty, TIOCGLTC, &tmpltc); ! 680: if (tmpltc.t_flushc != '\377') { ! 681: *pfrontp++ = tmpltc.t_flushc; ! 682: } ! 683: netclear(); /* clear buffer back */ ! 684: *nfrontp++ = IAC; ! 685: *nfrontp++ = DM; ! 686: neturg = nfrontp-1; /* off by one XXX */ ! 687: break; ! 688: } ! 689: ! 690: /* ! 691: * Erase Character and ! 692: * Erase Line ! 693: */ ! 694: case EC: ! 695: case EL: { ! 696: struct sgttyb b; ! 697: char ch; ! 698: ! 699: ptyflush(); /* half-hearted */ ! 700: ioctl(pty, TIOCGETP, &b); ! 701: ch = (c == EC) ? ! 702: b.sg_erase : b.sg_kill; ! 703: if (ch != '\377') { ! 704: *pfrontp++ = ch; ! 705: } ! 706: break; ! 707: } ! 708: ! 709: /* ! 710: * Check for urgent data... ! 711: */ ! 712: case DM: ! 713: SYNCHing = stilloob(net); ! 714: settimer(gotDM); ! 715: break; ! 716: ! 717: ! 718: /* ! 719: * Begin option subnegotiation... ! 720: */ ! 721: case SB: ! 722: state = TS_SB; ! 723: continue; ! 724: ! 725: case WILL: ! 726: state = TS_WILL; ! 727: continue; ! 728: ! 729: case WONT: ! 730: state = TS_WONT; ! 731: continue; ! 732: ! 733: case DO: ! 734: state = TS_DO; ! 735: continue; ! 736: ! 737: case DONT: ! 738: state = TS_DONT; ! 739: continue; ! 740: ! 741: case IAC: ! 742: *pfrontp++ = c; ! 743: break; ! 744: } ! 745: state = TS_DATA; ! 746: break; ! 747: ! 748: case TS_SB: ! 749: if (c == IAC) { ! 750: state = TS_SE; ! 751: } else { ! 752: SB_ACCUM(c); ! 753: } ! 754: break; ! 755: ! 756: case TS_SE: ! 757: if (c != SE) { ! 758: if (c != IAC) { ! 759: SB_ACCUM(IAC); ! 760: } ! 761: SB_ACCUM(c); ! 762: state = TS_SB; ! 763: } else { ! 764: SB_TERM(); ! 765: suboption(); /* handle sub-option */ ! 766: state = TS_DATA; ! 767: } ! 768: break; ! 769: ! 770: case TS_WILL: ! 771: if (hisopts[c] != OPT_YES) ! 772: willoption(c); ! 773: state = TS_DATA; ! 774: continue; ! 775: ! 776: case TS_WONT: ! 777: if (hisopts[c] != OPT_NO) ! 778: wontoption(c); ! 779: state = TS_DATA; ! 780: continue; ! 781: ! 782: case TS_DO: ! 783: if (myopts[c] != OPT_YES) ! 784: dooption(c); ! 785: state = TS_DATA; ! 786: continue; ! 787: ! 788: case TS_DONT: ! 789: if (myopts[c] != OPT_NO) { ! 790: dontoption(c); ! 791: } ! 792: state = TS_DATA; ! 793: continue; ! 794: ! 795: default: ! 796: syslog(LOG_ERR, "telnetd: panic state=%d\n", state); ! 797: printf("telnetd: panic state=%d\n", state); ! 798: exit(1); ! 799: } ! 800: } ! 801: } ! 802: ! 803: willoption(option) ! 804: int option; ! 805: { ! 806: char *fmt; ! 807: ! 808: switch (option) { ! 809: ! 810: case TELOPT_BINARY: ! 811: mode(RAW, 0); ! 812: fmt = doopt; ! 813: break; ! 814: ! 815: case TELOPT_ECHO: ! 816: not42 = 0; /* looks like a 4.2 system */ ! 817: /* ! 818: * Now, in a 4.2 system, to break them out of ECHOing ! 819: * (to the terminal) mode, we need to send a "WILL ECHO". ! 820: * Kludge upon kludge! ! 821: */ ! 822: if (myopts[TELOPT_ECHO] == OPT_YES) { ! 823: dooption(TELOPT_ECHO); ! 824: } ! 825: fmt = dont; ! 826: break; ! 827: ! 828: case TELOPT_TTYPE: ! 829: settimer(ttypeopt); ! 830: if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { ! 831: hisopts[TELOPT_TTYPE] = OPT_YES; ! 832: return; ! 833: } ! 834: fmt = doopt; ! 835: break; ! 836: ! 837: case TELOPT_SGA: ! 838: fmt = doopt; ! 839: break; ! 840: ! 841: case TELOPT_TM: ! 842: fmt = dont; ! 843: break; ! 844: ! 845: default: ! 846: fmt = dont; ! 847: break; ! 848: } ! 849: if (fmt == doopt) { ! 850: hisopts[option] = OPT_YES; ! 851: } else { ! 852: hisopts[option] = OPT_NO; ! 853: } ! 854: sprintf(nfrontp, fmt, option); ! 855: nfrontp += sizeof (dont) - 2; ! 856: } ! 857: ! 858: wontoption(option) ! 859: int option; ! 860: { ! 861: char *fmt; ! 862: ! 863: switch (option) { ! 864: case TELOPT_ECHO: ! 865: not42 = 1; /* doesn't seem to be a 4.2 system */ ! 866: break; ! 867: ! 868: case TELOPT_BINARY: ! 869: mode(0, RAW); ! 870: break; ! 871: ! 872: case TELOPT_TTYPE: ! 873: settimer(ttypeopt); ! 874: break; ! 875: } ! 876: ! 877: fmt = dont; ! 878: hisopts[option] = OPT_NO; ! 879: sprintf(nfrontp, fmt, option); ! 880: nfrontp += sizeof (doopt) - 2; ! 881: } ! 882: ! 883: dooption(option) ! 884: int option; ! 885: { ! 886: char *fmt; ! 887: ! 888: switch (option) { ! 889: ! 890: case TELOPT_TM: ! 891: fmt = wont; ! 892: break; ! 893: ! 894: case TELOPT_ECHO: ! 895: mode(ECHO|CRMOD, 0); ! 896: fmt = will; ! 897: break; ! 898: ! 899: case TELOPT_BINARY: ! 900: mode(RAW, 0); ! 901: fmt = will; ! 902: break; ! 903: ! 904: case TELOPT_SGA: ! 905: fmt = will; ! 906: break; ! 907: ! 908: default: ! 909: fmt = wont; ! 910: break; ! 911: } ! 912: if (fmt == will) { ! 913: myopts[option] = OPT_YES; ! 914: } else { ! 915: myopts[option] = OPT_NO; ! 916: } ! 917: sprintf(nfrontp, fmt, option); ! 918: nfrontp += sizeof (doopt) - 2; ! 919: } ! 920: ! 921: ! 922: dontoption(option) ! 923: int option; ! 924: { ! 925: char *fmt; ! 926: ! 927: switch (option) { ! 928: case TELOPT_ECHO: /* we should stop echoing */ ! 929: mode(0, ECHO|CRMOD); ! 930: fmt = wont; ! 931: break; ! 932: ! 933: default: ! 934: fmt = wont; ! 935: break; ! 936: } ! 937: ! 938: if (fmt = wont) { ! 939: myopts[option] = OPT_NO; ! 940: } else { ! 941: myopts[option] = OPT_YES; ! 942: } ! 943: sprintf(nfrontp, fmt, option); ! 944: nfrontp += sizeof (wont) - 2; ! 945: } ! 946: ! 947: /* ! 948: * suboption() ! 949: * ! 950: * Look at the sub-option buffer, and try to be helpful to the other ! 951: * side. ! 952: * ! 953: * Currently we recognize: ! 954: * ! 955: * Terminal type is ! 956: */ ! 957: ! 958: suboption() ! 959: { ! 960: switch (SB_GET()) { ! 961: case TELOPT_TTYPE: { /* Yaaaay! */ ! 962: static char terminalname[5+41] = "TERM="; ! 963: ! 964: settimer(ttypesubopt); ! 965: ! 966: if (SB_GET() != TELQUAL_IS) { ! 967: return; /* ??? XXX but, this is the most robust */ ! 968: } ! 969: ! 970: terminaltype = terminalname+strlen(terminalname); ! 971: ! 972: while ((terminaltype < (terminalname + sizeof terminalname-1)) && ! 973: !SB_EOF()) { ! 974: register int c; ! 975: ! 976: c = SB_GET(); ! 977: if (isupper(c)) { ! 978: c = tolower(c); ! 979: } ! 980: *terminaltype++ = c; /* accumulate name */ ! 981: } ! 982: *terminaltype = 0; ! 983: terminaltype = terminalname; ! 984: break; ! 985: } ! 986: ! 987: default: ! 988: ; ! 989: } ! 990: } ! 991: ! 992: mode(on, off) ! 993: int on, off; ! 994: { ! 995: struct sgttyb b; ! 996: ! 997: ptyflush(); ! 998: ioctl(pty, TIOCGETP, &b); ! 999: b.sg_flags |= on; ! 1000: b.sg_flags &= ~off; ! 1001: ioctl(pty, TIOCSETP, &b); ! 1002: } ! 1003: ! 1004: /* ! 1005: * Send interrupt to process on other side of pty. ! 1006: * If it is in raw mode, just write NULL; ! 1007: * otherwise, write intr char. ! 1008: */ ! 1009: interrupt() ! 1010: { ! 1011: struct sgttyb b; ! 1012: struct tchars tchars; ! 1013: ! 1014: ptyflush(); /* half-hearted */ ! 1015: ioctl(pty, TIOCGETP, &b); ! 1016: if (b.sg_flags & RAW) { ! 1017: *pfrontp++ = '\0'; ! 1018: return; ! 1019: } ! 1020: *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? ! 1021: '\177' : tchars.t_intrc; ! 1022: } ! 1023: ! 1024: /* ! 1025: * Send quit to process on other side of pty. ! 1026: * If it is in raw mode, just write NULL; ! 1027: * otherwise, write quit char. ! 1028: */ ! 1029: sendbrk() ! 1030: { ! 1031: struct sgttyb b; ! 1032: struct tchars tchars; ! 1033: ! 1034: ptyflush(); /* half-hearted */ ! 1035: ioctl(pty, TIOCGETP, &b); ! 1036: if (b.sg_flags & RAW) { ! 1037: *pfrontp++ = '\0'; ! 1038: return; ! 1039: } ! 1040: *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? ! 1041: '\034' : tchars.t_quitc; ! 1042: } ! 1043: ! 1044: ptyflush() ! 1045: { ! 1046: int n; ! 1047: ! 1048: if ((n = pfrontp - pbackp) > 0) ! 1049: n = write(pty, pbackp, n); ! 1050: if (n < 0) ! 1051: return; ! 1052: pbackp += n; ! 1053: if (pbackp == pfrontp) ! 1054: pbackp = pfrontp = ptyobuf; ! 1055: } ! 1056: ! 1057: /* ! 1058: * nextitem() ! 1059: * ! 1060: * Return the address of the next "item" in the TELNET data ! 1061: * stream. This will be the address of the next character if ! 1062: * the current address is a user data character, or it will ! 1063: * be the address of the character following the TELNET command ! 1064: * if the current address is a TELNET IAC ("I Am a Command") ! 1065: * character. ! 1066: */ ! 1067: ! 1068: char * ! 1069: nextitem(current) ! 1070: char *current; ! 1071: { ! 1072: if ((*current&0xff) != IAC) { ! 1073: return current+1; ! 1074: } ! 1075: switch (*(current+1)&0xff) { ! 1076: case DO: ! 1077: case DONT: ! 1078: case WILL: ! 1079: case WONT: ! 1080: return current+3; ! 1081: case SB: /* loop forever looking for the SE */ ! 1082: { ! 1083: register char *look = current+2; ! 1084: ! 1085: for (;;) { ! 1086: if ((*look++&0xff) == IAC) { ! 1087: if ((*look++&0xff) == SE) { ! 1088: return look; ! 1089: } ! 1090: } ! 1091: } ! 1092: } ! 1093: default: ! 1094: return current+2; ! 1095: } ! 1096: } ! 1097: ! 1098: ! 1099: /* ! 1100: * netclear() ! 1101: * ! 1102: * We are about to do a TELNET SYNCH operation. Clear ! 1103: * the path to the network. ! 1104: * ! 1105: * Things are a bit tricky since we may have sent the first ! 1106: * byte or so of a previous TELNET command into the network. ! 1107: * So, we have to scan the network buffer from the beginning ! 1108: * until we are up to where we want to be. ! 1109: * ! 1110: * A side effect of what we do, just to keep things ! 1111: * simple, is to clear the urgent data pointer. The principal ! 1112: * caller should be setting the urgent data pointer AFTER calling ! 1113: * us in any case. ! 1114: */ ! 1115: ! 1116: netclear() ! 1117: { ! 1118: register char *thisitem, *next; ! 1119: char *good; ! 1120: #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ ! 1121: ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) ! 1122: ! 1123: thisitem = netobuf; ! 1124: ! 1125: while ((next = nextitem(thisitem)) <= nbackp) { ! 1126: thisitem = next; ! 1127: } ! 1128: ! 1129: /* Now, thisitem is first before/at boundary. */ ! 1130: ! 1131: good = netobuf; /* where the good bytes go */ ! 1132: ! 1133: while (nfrontp > thisitem) { ! 1134: if (wewant(thisitem)) { ! 1135: int length; ! 1136: ! 1137: next = thisitem; ! 1138: do { ! 1139: next = nextitem(next); ! 1140: } while (wewant(next) && (nfrontp > next)); ! 1141: length = next-thisitem; ! 1142: bcopy(thisitem, good, length); ! 1143: good += length; ! 1144: thisitem = next; ! 1145: } else { ! 1146: thisitem = nextitem(thisitem); ! 1147: } ! 1148: } ! 1149: ! 1150: nbackp = netobuf; ! 1151: nfrontp = good; /* next byte to be sent */ ! 1152: neturg = 0; ! 1153: } ! 1154: ! 1155: /* ! 1156: * netflush ! 1157: * Send as much data as possible to the network, ! 1158: * handling requests for urgent data. ! 1159: */ ! 1160: ! 1161: ! 1162: netflush() ! 1163: { ! 1164: int n; ! 1165: ! 1166: if ((n = nfrontp - nbackp) > 0) { ! 1167: /* ! 1168: * if no urgent data, or if the other side appears to be an ! 1169: * old 4.2 client (and thus unable to survive TCP urgent data), ! 1170: * write the entire buffer in non-OOB mode. ! 1171: */ ! 1172: if ((neturg == 0) || (not42 == 0)) { ! 1173: n = write(net, nbackp, n); /* normal write */ ! 1174: } else { ! 1175: n = neturg - nbackp; ! 1176: /* ! 1177: * In 4.2 (and 4.3) systems, there is some question about ! 1178: * what byte in a sendOOB operation is the "OOB" data. ! 1179: * To make ourselves compatible, we only send ONE byte ! 1180: * out of band, the one WE THINK should be OOB (though ! 1181: * we really have more the TCP philosophy of urgent data ! 1182: * rather than the Unix philosophy of OOB data). ! 1183: */ ! 1184: if (n > 1) { ! 1185: n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ ! 1186: } else { ! 1187: n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ ! 1188: } ! 1189: } ! 1190: } ! 1191: if (n < 0) { ! 1192: if (errno == EWOULDBLOCK) ! 1193: return; ! 1194: /* should blow this guy away... */ ! 1195: return; ! 1196: } ! 1197: nbackp += n; ! 1198: if (nbackp >= neturg) { ! 1199: neturg = 0; ! 1200: } ! 1201: if (nbackp == nfrontp) { ! 1202: nbackp = nfrontp = netobuf; ! 1203: } ! 1204: } ! 1205: ! 1206: cleanup() ! 1207: { ! 1208: ! 1209: rmut(); ! 1210: vhangup(); /* XXX */ ! 1211: shutdown(net, 2); ! 1212: exit(1); ! 1213: } ! 1214: ! 1215: #include <utmp.h> ! 1216: ! 1217: struct utmp wtmp; ! 1218: char wtmpf[] = "/usr/adm/wtmp"; ! 1219: char utmpf[] = "/etc/utmp"; ! 1220: #define SCPYN(a, b) strncpy(a, b, sizeof(a)) ! 1221: #define SCMPN(a, b) strncmp(a, b, sizeof(a)) ! 1222: ! 1223: rmut() ! 1224: { ! 1225: register f; ! 1226: int found = 0; ! 1227: struct utmp *u, *utmp; ! 1228: int nutmp; ! 1229: struct stat statbf; ! 1230: ! 1231: f = open(utmpf, O_RDWR); ! 1232: if (f >= 0) { ! 1233: fstat(f, &statbf); ! 1234: utmp = (struct utmp *)malloc(statbf.st_size); ! 1235: if (!utmp) ! 1236: syslog(LOG_ERR, "utmp malloc failed"); ! 1237: if (statbf.st_size && utmp) { ! 1238: nutmp = read(f, utmp, statbf.st_size); ! 1239: nutmp /= sizeof(struct utmp); ! 1240: ! 1241: for (u = utmp ; u < &utmp[nutmp] ; u++) { ! 1242: if (SCMPN(u->ut_line, line+5) || ! 1243: u->ut_name[0]==0) ! 1244: continue; ! 1245: lseek(f, ((long)u)-((long)utmp), L_SET); ! 1246: SCPYN(u->ut_name, ""); ! 1247: SCPYN(u->ut_host, ""); ! 1248: time(&u->ut_time); ! 1249: write(f, (char *)u, sizeof(wtmp)); ! 1250: found++; ! 1251: } ! 1252: } ! 1253: close(f); ! 1254: } ! 1255: if (found) { ! 1256: f = open(wtmpf, O_WRONLY|O_APPEND); ! 1257: if (f >= 0) { ! 1258: SCPYN(wtmp.ut_line, line+5); ! 1259: SCPYN(wtmp.ut_name, ""); ! 1260: SCPYN(wtmp.ut_host, ""); ! 1261: time(&wtmp.ut_time); ! 1262: write(f, (char *)&wtmp, sizeof(wtmp)); ! 1263: close(f); ! 1264: } ! 1265: } ! 1266: chmod(line, 0666); ! 1267: chown(line, 0, 0); ! 1268: line[strlen("/dev/")] = 'p'; ! 1269: chmod(line, 0666); ! 1270: chown(line, 0, 0); ! 1271: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.