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: }

Defined functions

cleanup defined in line 1206; used 3 times
doit defined in line 238; used 1 times
dontoption defined in line 922; used 1 times
dooption defined in line 883; used 4 times
fatal defined in line 322; used 2 times
fatalperror defined in line 333; used 4 times
getterminaltype defined in line 212; used 1 times
interrupt defined in line 1009; used 1 times
main defined in line 110; never used
mode defined in line 992; used 5 times
netclear defined in line 1116; used 1 times
netflush defined in line 1162; used 2 times
nextitem defined in line 1068; used 3 times
ptyflush defined in line 1044; used 6 times
rmut defined in line 1223; used 1 times
sendbrk defined in line 1029; used 1 times
stilloob defined in line 351; used 2 times
suboption defined in line 958; used 1 times
telnet defined in line 378; used 1 times
telrcv defined in line 605; used 4 times
ttloop defined in line 183; used 2 times
willoption defined in line 803; used 1 times
wontoption defined in line 858; used 1 times

Defined variables

BANNER1 defined in line 69; used 3 times
SYNCHing defined in line 89; used 5 times
copyright defined in line 8; never used
dont defined in line 47; used 5 times
doopt defined in line 46; used 8 times
envinit defined in line 171; used 3 times
hisopts defined in line 43; used 10 times
inter defined in line 85; used 1 times
line defined in line 88; used 16 times
myopts defined in line 44; used 10 times
ncc defined in line 82; used 21 times
net defined in line 84; used 17 times
netibuf defined in line 58; used 15 times
netip defined in line 58; used 6 times
netobuf defined in line 63; used 7 times
neturg defined in line 64; used 6 times
nfrontp defined in line 63; used 40 times
not42 defined in line 66; used 3 times
pcc defined in line 82; used 10 times
pfrontp defined in line 56; used 15 times
pty defined in line 84; used 11 times
ptyibuf defined in line 54; used 3 times
ptyip defined in line 54; used 4 times
ptyobuf defined in line 56; used 5 times
sccsid defined in line 14; never used
subbuffer defined in line 73; used 5 times
subpointer defined in line 73; used 6 times
terminaltype defined in line 170; used 7 times
utmpf defined in line 1219; used 1 times
will defined in line 48; used 4 times
wont defined in line 49; used 6 times
wtmp defined in line 1217; used 7 times
wtmpf defined in line 1218; used 1 times

Defined macros

NIACCUM defined in line 59; never used
OPT_NO defined in line 39; used 7 times
OPT_NO_BUT_ALWAYS_LOOK defined in line 42; never used
OPT_YES defined in line 40; used 8 times
OPT_YES_BUT_ALWAYS_LOOK defined in line 41; used 3 times
SB_ACCUM defined in line 76; used 3 times
SB_CLEAR defined in line 74; used 1 times
  • in line 75
SB_EOF defined in line 80; used 1 times
SB_GET defined in line 79; used 3 times
SB_TERM defined in line 75; used 1 times
SCMPN defined in line 1221; used 1 times
SCPYN defined in line 1220; used 5 times
TS_CR defined in line 597; used 1 times
TS_DATA defined in line 595; used 8 times
TS_DO defined in line 602; used 1 times
TS_DONT defined in line 603; used 1 times
TS_IAC defined in line 596; used 1 times
TS_SB defined in line 598; used 2 times
TS_SE defined in line 599; used 1 times
TS_WILL defined in line 600; used 1 times
TS_WONT defined in line 601; used 1 times
sequenceIs defined in line 108; used 3 times
settimer defined in line 107; used 6 times
wewant defined in line 1120; used 2 times
Last modified: 1986-05-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3630
Valid CSS Valid XHTML 1.0 Strict