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

Defined functions

cleanup defined in line 1220; used 3 times
doit defined in line 236; used 1 times
dontoption defined in line 936; used 1 times
dooption defined in line 897; used 4 times
fatal defined in line 328; used 2 times
fatalperror defined in line 339; used 6 times
getterminaltype defined in line 210; used 1 times
interrupt defined in line 1023; used 1 times
main defined in line 108; never used
mode defined in line 1006; used 5 times
netclear defined in line 1130; used 2 times
netflush defined in line 1176; used 2 times
nextitem defined in line 1082; used 3 times
ptyflush defined in line 1058; used 6 times
rmut defined in line 1237; used 1 times
sendbrk defined in line 1043; used 1 times
stilloob defined in line 355; used 2 times
suboption defined in line 972; used 1 times
telnet defined in line 382; used 1 times
telrcv defined in line 619; used 4 times
ttloop defined in line 181; used 2 times
willoption defined in line 817; used 1 times
wontoption defined in line 872; used 1 times

Defined variables

BANNER1 defined in line 67; used 3 times
SYNCHing defined in line 87; used 5 times
copyright defined in line 8; never used
dont defined in line 45; used 5 times
doopt defined in line 44; used 8 times
envinit defined in line 169; used 3 times
hisopts defined in line 41; used 11 times
inter defined in line 83; used 1 times
line defined in line 86; used 19 times
myopts defined in line 42; used 10 times
ncc defined in line 80; used 19 times
net defined in line 82; used 17 times
netibuf defined in line 56; used 15 times
netip defined in line 56; used 4 times
netobuf defined in line 61; used 7 times
neturg defined in line 62; used 7 times
nfrontp defined in line 61; used 43 times
not42 defined in line 64; used 3 times
pcc defined in line 80; used 11 times
pfrontp defined in line 54; used 15 times
pty defined in line 82; used 11 times
ptyibuf defined in line 52; used 4 times
ptyip defined in line 52; used 4 times
ptyobuf defined in line 54; used 5 times
sccsid defined in line 12; never used
subbuffer defined in line 71; used 5 times
subpointer defined in line 71; used 6 times
terminaltype defined in line 168; used 7 times
utmpf defined in line 1233; used 1 times
will defined in line 46; used 4 times
wont defined in line 47; used 6 times
wtmp defined in line 1231; used 7 times
wtmpf defined in line 1232; used 1 times

Defined macros

NIACCUM defined in line 57; never used
OPT_NO defined in line 37; used 8 times
OPT_NO_BUT_ALWAYS_LOOK defined in line 40; never used
OPT_YES defined in line 38; used 8 times
OPT_YES_BUT_ALWAYS_LOOK defined in line 39; used 3 times
SB_ACCUM defined in line 74; used 3 times
SB_CLEAR defined in line 72; used 1 times
  • in line 73
SB_EOF defined in line 78; used 1 times
SB_GET defined in line 77; used 3 times
SB_TERM defined in line 73; used 1 times
SCMPN defined in line 1235; used 1 times
SCPYN defined in line 1234; used 5 times
TS_CR defined in line 611; used 1 times
TS_DATA defined in line 609; used 8 times
TS_DO defined in line 616; used 1 times
TS_DONT defined in line 617; used 1 times
TS_IAC defined in line 610; used 1 times
TS_SB defined in line 612; used 2 times
TS_SE defined in line 613; used 1 times
TS_WILL defined in line 614; used 1 times
TS_WONT defined in line 615; used 1 times
sequenceIs defined in line 106; used 3 times
settimer defined in line 105; used 6 times
wewant defined in line 1134; used 2 times
Last modified: 1996-11-17
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 9936
Valid CSS Valid XHTML 1.0 Strict