Lots of bugfixes, add the ENVIRON and X-DISPLAY-LOC
[unix-history] / usr / src / libexec / telnetd / telnetd.c
index 045624f..2df6780 100644 (file)
 /*
 /*
- * Copyright (c) 1983,1986 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
  */
 
 #ifndef lint
 char copyright[] =
  */
 
 #ifndef lint
 char copyright[] =
-"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+"@(#) Copyright (c) 1989 Regents of the University of California.\n\
  All rights reserved.\n";
  All rights reserved.\n";
-#endif not lint
+#endif /* not lint */
 
 #ifndef lint
 
 #ifndef lint
-static char sccsid[] = "@(#)telnetd.c  5.25 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)telnetd.c  5.45 (Berkeley) %G%";
+#endif /* not lint */
 
 
-/*
- * Telnet server.
- */
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <netinet/in.h>
-
-#include <arpa/telnet.h>
-
-#include <stdio.h>
-#include <signal.h>
-#include <errno.h>
-#include <sgtty.h>
-#include <netdb.h>
-#include <syslog.h>
-#include <ctype.h>
-
-#define        OPT_NO                  0               /* won't do this option */
-#define        OPT_YES                 1               /* will do this option */
-#define        OPT_YES_BUT_ALWAYS_LOOK 2
-#define        OPT_NO_BUT_ALWAYS_LOOK  3
-char   hisopts[256];
-char   myopts[256];
-
-char   doopt[] = { IAC, DO, '%', 'c', 0 };
-char   dont[] = { IAC, DONT, '%', 'c', 0 };
-char   will[] = { IAC, WILL, '%', 'c', 0 };
-char   wont[] = { IAC, WONT, '%', 'c', 0 };
+#include "telnetd.h"
 
 /*
 
 /*
- * I/O data buffers, pointers, and counters.
+ * I/O data buffers,
+ * pointers, and counters.
  */
 char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
  */
 char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
+char   ptyibuf2[BUFSIZ];
+
+#ifdef CRAY
+int    hostinfo = 1;                   /* do we print login banner? */
+#endif
+
+#ifdef CRAY
+extern int      newmap; /* nonzero if \n maps to ^M^J */
+int    lowpty = 0, highpty;    /* low, high pty numbers */
+#endif /* CRAY */
+
+int debug = 0;
+char *progname;
+
+#if    defined(NEED_GETTOS)
+struct tosent {
+       char    *t_name;        /* name */
+       char    **t_aliases;    /* alias list */
+       char    *t_proto;       /* protocol */
+       int     t_tos;          /* Type Of Service bits */
+};
+
+struct tosent *
+gettosbyname(name, proto)
+char *name, *proto;
+{
+       static struct tosent te;
+       static char *aliasp = 0;
+
+       te.t_name = name;
+       te.t_aliases = &aliasp;
+       te.t_proto = proto;
+       te.t_tos = 020; /* Low Delay bit */
+       return(&te);
+}
+#endif
 
 
-char   ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
+main(argc, argv)
+       char *argv[];
+{
+       struct sockaddr_in from;
+       int on = 1, fromlen;
+#if    defined(HAS_IP_TOS) || defined(NEED_GETTOS)
+       struct tosent *tp;
+#endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */
 
 
-char   netibuf[BUFSIZ], *netip = netibuf;
-#define        NIACCUM(c)      {   *netip++ = c; \
-                           ncc++; \
-                       }
+       pfrontp = pbackp = ptyobuf;
+       netip = netibuf;
+       nfrontp = nbackp = netobuf;
+
+       progname = *argv;
+
+#ifdef CRAY
+       /*
+        * Get number of pty's before trying to process options,
+        * which may include changing pty range.
+        */
+       highpty = getnpty();
+#endif /* CRAY */
+
+top:
+       argc--, argv++;
+
+       if (argc > 0 && strcmp(*argv, "-debug") == 0) {
+               debug++;
+               goto top;
+       }
 
 
-char   netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
-char   *neturg = 0;            /* one past last bye of urgent data */
-       /* the remote system seems to NOT be an old 4.2 */
-int    not42 = 1;
+#ifdef LINEMODE
+       if (argc > 0 && !strcmp(*argv, "-l")) {
+               alwayslinemode = 1;
+               goto top;
+       }
+#endif /* LINEMODE */
+
+#ifdef CRAY
+       if (argc > 0 && !strcmp(*argv, "-h")) {
+               hostinfo = 0;
+               goto top;
+       }
 
 
-#define        BANNER  "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
+       if (argc > 0 && !strncmp(*argv, "-r", 2)) {
+               char *strchr();
+               char *c;
 
 
-               /* buffer for sub-options */
-char   subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
-#define        SB_CLEAR()      subpointer = subbuffer;
-#define        SB_TERM()       { subend = subpointer; SB_CLEAR(); }
-#define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
-                               *subpointer++ = (c); \
+               /*
+                * Allow the specification of alterations to the pty search
+                * range.  It is legal to specify only one, and not change the
+                * other from its default.
+                */
+               *argv += 2;
+               if (**argv == '\0' && argc)
+                       argv++, argc--;
+               c = strchr(*argv, '-');
+               if (c) {
+                       *c++ = '\0';
+                       highpty = atoi(c);
+               }
+               if (**argv != '\0')
+                       lowpty = atoi(*argv);
+               if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) {
+                       usage();
+                       /* NOT REACHED */
+               }
+               goto top;
+       }
+# ifdef        NEWINIT
+       if (argc > 0 && !strncmp(*argv, "-I", 2)) {
+               extern char *gen_id;
+
+               *argv += 2;
+               if (**argv == '\0') {
+                       if (argc < 2) {
+                               usage();
+                               /* NOT REACHED */
                        }
                        }
-#define        SB_GET()        ((*subpointer++)&0xff)
-#define        SB_EOF()        (subpointer >= subend)
+                       argv++, argc--;
+                       if (**argv == '\0') {
+                               usage();
+                               /* NOT REACHED */
+                       }
+               }
+               gen_id = *argv;
+               goto top;
+       }
+# endif        /* NEWINIT */
+#endif /* CRAY */
 
 
-int    pcc, ncc;
+#ifdef DIAGNOSTICS
+       /*
+        * Check for desired diagnostics capabilities.
+        */
+       if (argc > 0 && !strncmp(*argv, "-D", 2)) {
+               *argv += 2;
+               if (**argv == '\0') {
+                       if (argc < 2) {
+                               usage();
+                               /* NOT REACHED */
+                       }
+                       argv++, argc--;
+                       if (**argv == '\0') {
+                               usage();
+                               /* NOT REACHED */
+                       }
+               }
+               if (!strcmp(*argv, "report")) {
+                       diagnostic |= TD_REPORT|TD_OPTIONS;
+               } else if (!strcmp(*argv, "exercise")) {
+                       diagnostic |= TD_EXERCISE;
+               } else if (!strcmp(*argv, "netdata")) {
+                       diagnostic |= TD_NETDATA;
+               } else if (!strcmp(*argv, "ptydata")) {
+                       diagnostic |= TD_PTYDATA;
+               } else if (!strcmp(*argv, "options")) {
+                       diagnostic |= TD_OPTIONS;
+               } else {
+                       usage();
+                       /* NOT REACHED */
+               }
+               goto top;
+       }
+#endif /* DIAGNOSTICS */
 
 
-int    pty, net;
-int    inter;
-extern char **environ;
-extern int errno;
-char   *line;
-int    SYNCHing = 0;           /* we are in TELNET SYNCH mode */
-/*
- * The following are some clocks used to decide how to interpret
- * the relationship between various variables.
- */
+#ifdef BFTPDAEMON
+       /*
+        * Check for bftp daemon
+        */
+       if (argc > 0 && !strncmp(*argv, "-B", 2)) {
+               bftpd++;
+               goto top;
+       }
+#endif /* BFTPDAEMON */
 
 
-struct {
-    int
-       system,                 /* what the current time is */
-       echotoggle,             /* last time user entered echo character */
-       modenegotiated,         /* last time operating mode negotiated */
-       didnetreceive,          /* last time we read data from network */
-       ttypeopt,               /* ttype will/won't received */
-       ttypesubopt,            /* ttype subopt is received */
-       getterminal,            /* time started to get terminal information */
-       gotDM;                  /* when did we last see a data mark */
-} clocks;
-
-#define        settimer(x)     (clocks.x = ++clocks.system)
-#define        sequenceIs(x,y) (clocks.x < clocks.y)
-\f
-main(argc, argv)
-       char *argv[];
-{
-       struct sockaddr_in from;
-       int on = 1, fromlen;
+       if (argc > 0 && **argv == '-') {
+               fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1);
+               usage();
+               /* NOT REACHED */
+       }
 
 
-#if    defined(DEBUG)
-       {
+       if (debug) {
            int s, ns, foo;
            struct servent *sp;
            static struct sockaddr_in sin = { AF_INET };
 
            int s, ns, foo;
            struct servent *sp;
            static struct sockaddr_in sin = { AF_INET };
 
-           sp = getservbyname("telnet", "tcp");
-           if (sp == 0) {
+           if (argc > 1) {
+               usage();
+               /* NOT REACHED */
+           } else if (argc == 1) {
+                   if (sp = getservbyname(*argv, "tcp")) {
+                       sin.sin_port = sp->s_port;
+                   } else {
+                       sin.sin_port = atoi(*argv);
+                       if ((int)sin.sin_port <= 0) {
+                           fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
+                           usage();
+                           /* NOT REACHED */
+                       }
+                       sin.sin_port = htons((u_short)sin.sin_port);
+                  }
+           } else {
+               sp = getservbyname("telnet", "tcp");
+               if (sp == 0) {
                    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
                    exit(1);
                    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
                    exit(1);
-           }
-           sin.sin_port = sp->s_port;
-           argc--, argv++;
-           if (argc > 0) {
-                   sin.sin_port = atoi(*argv);
-                   sin.sin_port = htons((u_short)sin.sin_port);
+               }
+               sin.sin_port = sp->s_port;
            }
 
            s = socket(AF_INET, SOCK_STREAM, 0);
            }
 
            s = socket(AF_INET, SOCK_STREAM, 0);
@@ -134,7 +235,8 @@ main(argc, argv)
                    perror("telnetd: socket");;
                    exit(1);
            }
                    perror("telnetd: socket");;
                    exit(1);
            }
-           if (bind(s, &sin, sizeof sin) < 0) {
+           (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+           if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
                perror("bind");
                exit(1);
            }
                perror("bind");
                exit(1);
            }
@@ -143,240 +245,260 @@ main(argc, argv)
                exit(1);
            }
            foo = sizeof sin;
                exit(1);
            }
            foo = sizeof sin;
-           ns = accept(s, &sin, &foo);
+           ns = accept(s, (struct sockaddr *)&sin, &foo);
            if (ns < 0) {
                perror("accept");
                exit(1);
            }
            if (ns < 0) {
                perror("accept");
                exit(1);
            }
-           dup2(ns, 0);
-           close(s);
+           (void) dup2(ns, 0);
+           (void) close(ns);
+           (void) close(s);
+       } else if (argc > 0) {
+               usage();
+               /* NOT REACHED */
        }
        }
-#endif /* defined(DEBUG) */
+
        openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
        fromlen = sizeof (from);
        openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
        fromlen = sizeof (from);
-       if (getpeername(0, &from, &fromlen) < 0) {
-               fprintf(stderr, "%s: ", argv[0]);
+       if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+               fprintf(stderr, "%s: ", progname);
                perror("getpeername");
                _exit(1);
        }
        if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
                syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
        }
                perror("getpeername");
                _exit(1);
        }
        if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
                syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
        }
-       doit(0, &from);
-}
-
-char   *terminaltype = 0;
-char   *envinit[2];
-int    cleanup();
-
-/*
- * ttloop
- *
- *     A small subroutine to flush the network output buffer, get some data
- * from the network, and pass it through the telnet state machine.  We
- * also flush the pty input buffer (by dropping its data) if it becomes
- * too full.
- */
 
 
-void
-ttloop()
+#if    defined(HAS_IP_TOS) || defined(NEED_GETTOS)
+       if ((tp = gettosbyname("telnet", "tcp")) &&
+           (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
+               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */
+       net = 0;
+       doit(&from);
+       /* NOTREACHED */
+}  /* end of main */
+
+usage()
 {
 {
-    if (nfrontp-nbackp) {
-       netflush();
-    }
-    ncc = read(net, netibuf, sizeof netibuf);
-    if (ncc < 0) {
-       syslog(LOG_INFO, "ttloop:  read: %m\n");
+       fprintf(stderr, "Usage: telnetd [-debug] [-h]");
+#ifdef NEWINIT
+       fprintf(stderr, " [-Iinitid]");
+#endif /* NEWINIT */
+#ifdef DIAGNOSTICS
+       fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]");
+#endif /* DIAGNOSTICS */
+#ifdef LINEMODE
+       fprintf(stderr, " [-l]");
+#endif
+#ifdef CRAY
+       fprintf(stderr, " [-r[lowpty]-[highpty]]");
+#endif
+#ifdef BFTPDAEMON
+       fprintf(stderr, " [-B]");
+#endif /* BFTPDAEMON */
+       fprintf(stderr, " [port]\n");
        exit(1);
        exit(1);
-    } else if (ncc == 0) {
-       syslog(LOG_INFO, "ttloop:  peer died: %m\n");
-       exit(1);
-    }
-    netip = netibuf;
-    telrcv();                  /* state machine */
-    if (ncc > 0) {
-       pfrontp = pbackp = ptyobuf;
-       telrcv();
-    }
 }
 
 }
 
+void   cleanup();
+
 /*
  * getterminaltype
  *
 /*
  * getterminaltype
  *
- *     Ask the other end to send along its terminal type.
+ *     Ask the other end to send along its terminal type and speed.
  * Output is the variable terminaltype filled in.
  */
  * Output is the variable terminaltype filled in.
  */
-
+static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
 void
 getterminaltype()
 {
 void
 getterminaltype()
 {
-    static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
-
-    settimer(getterminal);
-    bcopy(sbuf, nfrontp, sizeof sbuf);
-    nfrontp += sizeof sbuf;
-    hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
-    while (sequenceIs(ttypeopt, getterminal)) {
+    void ttloop();
+
+    settimer(baseline);
+    send_do(TELOPT_TTYPE, 1);
+    send_do(TELOPT_TSPEED, 1);
+    send_do(TELOPT_XDISPLOC, 1);
+    send_do(TELOPT_ENVIRON, 1);
+    while (his_will_wont_is_changing(TELOPT_TTYPE) ||
+          his_will_wont_is_changing(TELOPT_TSPEED) ||
+          his_will_wont_is_changing(TELOPT_XDISPLOC) ||
+          his_will_wont_is_changing(TELOPT_ENVIRON)) {
        ttloop();
     }
        ttloop();
     }
-    if (hisopts[TELOPT_TTYPE] == OPT_YES) {
-       static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
+    if (his_state_is_will(TELOPT_TSPEED)) {
+       static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
+
+       bcopy(sbbuf, nfrontp, sizeof sbbuf);
+       nfrontp += sizeof sbbuf;
+    }
+    if (his_state_is_will(TELOPT_XDISPLOC)) {
+       static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
+
+       bcopy(sbbuf, nfrontp, sizeof sbbuf);
+       nfrontp += sizeof sbbuf;
+    }
+    if (his_state_is_will(TELOPT_ENVIRON)) {
+       static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE };
 
        bcopy(sbbuf, nfrontp, sizeof sbbuf);
        nfrontp += sizeof sbbuf;
 
        bcopy(sbbuf, nfrontp, sizeof sbbuf);
        nfrontp += sizeof sbbuf;
-       while (sequenceIs(ttypesubopt, getterminal)) {
+    }
+    if (his_state_is_will(TELOPT_TTYPE)) {
+
+       bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+       nfrontp += sizeof ttytype_sbbuf;
+    }
+    if (his_state_is_will(TELOPT_TSPEED)) {
+       while (sequenceIs(tspeedsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_XDISPLOC)) {
+       while (sequenceIs(xdisplocsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_ENVIRON)) {
+       while (sequenceIs(environsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_TTYPE)) {
+       char first[256], last[256];
+
+       while (sequenceIs(ttypesubopt, baseline))
            ttloop();
            ttloop();
+
+       /*
+        * If the other side has already disabled the option, then
+        * we have to just go with what we (might) have already gotten.
+        */
+       if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
+           (void) strncpy(first, terminaltype, sizeof(first));
+           for(;;) {
+               /*
+                * Save the unknown name, and request the next name.
+                */
+               (void) strncpy(last, terminaltype, sizeof(last));
+               _gettermname();
+               if (terminaltypeok(terminaltype))
+                   break;
+               if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
+                   his_state_is_wont(TELOPT_TTYPE)) {
+                   /*
+                    * We've hit the end.  If this is the same as
+                    * the first name, just go with it.
+                    */
+                   if (strncmp(first, terminaltype, sizeof(first) == 0))
+                       break;
+                   /*
+                    * Get the terminal name one more time, so that
+                    * RFC1091 compliant telnets will cycle back to
+                    * the start of the list.
+                    */
+                    _gettermname();
+                   if (strncmp(first, terminaltype, sizeof(first) != 0))
+                       (void) strncpy(terminaltype, first, sizeof(first));
+                   break;
+               }
+           }
        }
     }
        }
     }
+}  /* end of getterminaltype */
+
+_gettermname()
+{
+    /*
+     * If the client turned off the option,
+     * we can't send another request, so we
+     * just return.
+     */
+    if (his_state_is_wont(TELOPT_TTYPE))
+       return;
+    settimer(baseline);
+    bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+    nfrontp += sizeof ttytype_sbbuf;
+    while (sequenceIs(ttypesubopt, baseline))
+       ttloop();
+}
+
+terminaltypeok(s)
+char *s;
+{
+    char buf[1024];
+
+    if (terminaltype == NULL)
+       return(1);
+
+    /*
+     * tgetent() will return 1 if the type is known, and
+     * 0 if it is not known.  If it returns -1, it couldn't
+     * open the database.  But if we can't open the database,
+     * it won't help to say we failed, because we won't be
+     * able to verify anything else.  So, we treat -1 like 1.
+     */
+    if (tgetent(buf, s) == 0)
+       return(0);
+    return(1);
 }
 
 /*
  * Get a pty, scan input lines.
  */
 }
 
 /*
  * Get a pty, scan input lines.
  */
-doit(f, who)
-       int f;
+doit(who)
        struct sockaddr_in *who;
 {
        char *host, *inet_ntoa();
        struct sockaddr_in *who;
 {
        char *host, *inet_ntoa();
-       int i, p, t;
-       struct sgttyb b;
+       int t;
        struct hostent *hp;
        struct hostent *hp;
-       int c;
 
 
-       for (c = 'p'; c <= 's'; c++) {
-               struct stat stb;
+       /*
+        * Find an available pty to use.
+        */
+       pty = getpty();
+       if (pty < 0)
+               fatal(net, "All network ports in use");
 
 
-               line = "/dev/ptyXX";
-               line[strlen("/dev/pty")] = c;
-               line[strlen("/dev/ptyp")] = '0';
-               if (stat(line, &stb) < 0)
-                       break;
-               for (i = 0; i < 16; i++) {
-                       line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
-                       p = open(line, 2);
-                       if (p > 0)
-                               goto gotpty;
-               }
-       }
-       fatal(f, "All network ports in use");
-       /*NOTREACHED*/
-gotpty:
-       dup2(f, 0);
-       line[strlen("/dev/")] = 't';
-       t = open("/dev/tty", O_RDWR);
-       if (t >= 0) {
-               ioctl(t, TIOCNOTTY, 0);
-               close(t);
-       }
-       t = open(line, O_RDWR);
-       if (t < 0)
-               fatalperror(f, line, errno);
-       ioctl(t, TIOCGETP, &b);
-       b.sg_flags = CRMOD|XTABS|ANYP;
-       ioctl(t, TIOCSETP, &b);
-       ioctl(p, TIOCGETP, &b);
-       b.sg_flags &= ~ECHO;
-       ioctl(p, TIOCSETP, &b);
-       hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
+       t = getptyslave();
+
+       /* get name of connected client */
+       hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
                who->sin_family);
        if (hp)
                host = hp->h_name;
        else
                host = inet_ntoa(who->sin_addr);
 
                who->sin_family);
        if (hp)
                host = hp->h_name;
        else
                host = inet_ntoa(who->sin_addr);
 
-       net = f;
-       pty = p;
-
+       init_env();
        /*
         * get terminal type.
         */
        getterminaltype();
        /*
         * get terminal type.
         */
        getterminaltype();
+       setenv("TERM", terminaltype ? terminaltype : "network", 1);
 
 
-       if ((i = fork()) < 0)
-               fatalperror(f, "fork", errno);
-       if (i)
-               telnet(f, p);
-       close(f);
-       close(p);
-       dup2(t, 0);
-       dup2(t, 1);
-       dup2(t, 2);
-       close(t);
-       envinit[0] = terminaltype;
-       envinit[1] = 0;
-       environ = envinit;
        /*
        /*
-        * -h : pass on name of host.
-        *              WARNING:  -h is accepted by login if and only if
-        *                      getuid() == 0.
-        * -p : don't clobber the environment (so terminal type stays set).
+        * Start up the login process on the slave side of the terminal
         */
         */
-       execl("/bin/login", "login", "-h", host,
-                                       terminaltype ? "-p" : 0, 0);
-       fatalperror(f, "/bin/login", errno);
-       /*NOTREACHED*/
-}
-
-fatal(f, msg)
-       int f;
-       char *msg;
-{
-       char buf[BUFSIZ];
-
-       (void) sprintf(buf, "telnetd: %s.\r\n", msg);
-       (void) write(f, buf, strlen(buf));
-       exit(1);
-}
-
-fatalperror(f, msg, errno)
-       int f;
-       char *msg;
-       int errno;
-{
-       char buf[BUFSIZ];
-       extern char *sys_errlist[];
-
-       (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
-       fatal(f, buf);
-}
-
-
-/*
- * Check a descriptor to see if out of band data exists on it.
- */
+       startslave(t, host);
 
 
+       telnet(net, pty);  /* begin server processing */
+       /*NOTREACHED*/
+}  /* end of doit */
 
 
-stilloob(s)
-int    s;              /* socket number */
-{
-    static struct timeval timeout = { 0 };
-    fd_set     excepts;
-    int value;
-
-    do {
-       FD_ZERO(&excepts);
-       FD_SET(s, &excepts);
-       value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
-    } while ((value == -1) && (errno == EINTR));
-
-    if (value < 0) {
-       fatalperror(pty, "select", errno);
-    }
-    if (FD_ISSET(s, &excepts)) {
-       return 1;
-    } else {
-       return 0;
-    }
-}
-\f
+#ifndef        MAXHOSTNAMELEN
+#define        MAXHOSTNAMELEN 64
+#endif MAXHOSTNAMELEN
 /*
  * Main loop.  Select from pty and network, and
  * hand data to telnet receiver finite state machine.
  */
 telnet(f, p)
 /*
  * Main loop.  Select from pty and network, and
  * hand data to telnet receiver finite state machine.
  */
 telnet(f, p)
+int f, p;
 {
        int on = 1;
        char hostname[MAXHOSTNAMELEN];
 {
        int on = 1;
        char hostname[MAXHOSTNAMELEN];
+#if    defined(CRAY2) && defined(UNICOS5)
+       int termstat();
+       int interrupt(), sendbrk();
+#endif
 #define        TABBUFSIZ       512
        char    defent[TABBUFSIZ];
        char    defstrs[TABBUFSIZ];
 #define        TABBUFSIZ       512
        char    defent[TABBUFSIZ];
        char    defstrs[TABBUFSIZ];
@@ -384,31 +506,20 @@ telnet(f, p)
        char *HE;
        char *HN;
        char *IM;
        char *HE;
        char *HN;
        char *IM;
-
-       ioctl(f, FIONBIO, &on);
-       ioctl(p, FIONBIO, &on);
-       ioctl(p, TIOCPKT, &on);
-#if    defined(SO_OOBINLINE)
-       setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
-#endif /* defined(SO_OOBINLINE) */
-       signal(SIGTSTP, SIG_IGN);
+       void netflush();
+       
        /*
        /*
-        * Ignoring SIGTTOU keeps the kernel from blocking us
-        * in ttioctl() in /sys/tty.c.
+        * Initialize the slc mapping table.
         */
         */
-       signal(SIGTTOU, SIG_IGN);
-       signal(SIGCHLD, cleanup);
-       setpgrp(0, 0);
+       get_slc_defaults();
 
        /*
 
        /*
-        * Request to do remote echo and to suppress go ahead.
+        * Do some tests where it is desireable to wait for a response.
+        * Rather than doing them slowly, one at a time, do them all
+        * at once.
         */
         */
-       if (!myopts[TELOPT_ECHO]) {
-           dooption(TELOPT_ECHO);
-       }
-       if (!myopts[TELOPT_SGA]) {
-           dooption(TELOPT_SGA);
-       }
+       if (my_state_is_wont(TELOPT_SGA))
+               send_will(TELOPT_SGA, 1);
        /*
         * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
         * because 4.2 clients are unable to deal with TCP urgent data.
        /*
         * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
         * because 4.2 clients are unable to deal with TCP urgent data.
@@ -419,9 +530,154 @@ telnet(f, p)
         * WE, the server, sends it; it does NOT mean that the client will
         * echo the terminal input).
         */
         * WE, the server, sends it; it does NOT mean that the client will
         * echo the terminal input).
         */
-       (void) sprintf(nfrontp, doopt, TELOPT_ECHO);
-       nfrontp += sizeof doopt-2;
-       hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
+       send_do(TELOPT_ECHO, 1);
+
+#ifdef LINEMODE
+       if (his_state_is_wont(TELOPT_LINEMODE)) {
+               /* Query the peer for linemode support by trying to negotiate
+                * the linemode option.
+                */
+               linemode = 0;
+               editmode = 0;
+               send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
+       }
+#endif /* LINEMODE */
+
+       /*
+        * Send along a couple of other options that we wish to negotiate.
+        */
+       send_do(TELOPT_NAWS, 1);
+       send_will(TELOPT_STATUS, 1);
+       flowmode = 1;  /* default flow control state */
+       send_do(TELOPT_LFLOW, 1);
+
+       /*
+        * Spin, waiting for a response from the DO ECHO.  However,
+        * some REALLY DUMB telnets out there might not respond
+        * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
+        * telnets so far seem to respond with WONT for a DO that
+        * they don't understand...) because by the time we get the
+        * response, it will already have processed the DO ECHO.
+        * Kludge upon kludge.
+        */
+       while (his_will_wont_is_changing(TELOPT_NAWS))
+               ttloop();
+
+       /*
+        * But...
+        * The client might have sent a WILL NAWS as part of its
+        * startup code; if so, we'll be here before we get the
+        * response to the DO ECHO.  We'll make the assumption
+        * that any implementation that understands about NAWS
+        * is a modern enough implementation that it will respond
+        * to our DO ECHO request; hence we'll do another spin
+        * waiting for the ECHO option to settle down, which is
+        * what we wanted to do in the first place...
+        */
+       if (his_want_state_is_will(TELOPT_ECHO) &&
+           his_state_is_will(TELOPT_NAWS)) {
+               while (his_will_wont_is_changing(TELOPT_ECHO))
+                       ttloop();
+       }
+       /*
+        * On the off chance that the telnet client is broken and does not
+        * respond to the DO ECHO we sent, (after all, we did send the
+        * DO NAWS negotiation after the DO ECHO, and we won't get here
+        * until a response to the DO NAWS comes back) simulate the
+        * receipt of a will echo.  This will also send a WONT ECHO
+        * to the client, since we assume that the client failed to
+        * respond because it believes that it is already in DO ECHO
+        * mode, which we do not want.
+        */
+       if (his_want_state_is_will(TELOPT_ECHO)) {
+#ifdef DIAGNOSTICS
+               if (diagnostic & TD_OPTIONS) {
+                       sprintf(nfrontp, "td: simulating recv\r\n");
+                       nfrontp += strlen(nfrontp);
+               }
+#endif /* DIAGNOSTICS */
+               willoption(TELOPT_ECHO);
+       }
+
+       /*
+        * Finally, to clean things up, we turn on our echo.  This
+        * will break stupid 4.2 telnets out of local terminal echo.
+        */
+
+       if (my_state_is_wont(TELOPT_ECHO))
+               send_will(TELOPT_ECHO, 1);
+
+       /*
+        * Turn on packet mode, and default to line at at time mode.
+        */
+       (void) ioctl(p, TIOCPKT, (char *)&on);
+#ifdef LINEMODE
+       tty_setlinemode(1);
+
+# ifdef        KLUDGELINEMODE
+       /*
+        * Continuing line mode support.  If client does not support
+        * real linemode, attempt to negotiate kludge linemode by sending
+        * the do timing mark sequence.
+        */
+       if (lmodetype < REAL_LINEMODE)
+               send_do(TELOPT_TM, 1);
+# endif        /* KLUDGELINEMODE */
+#endif /* LINEMODE */
+
+       /*
+        * Call telrcv() once to pick up anything received during
+        * terminal type negotiation, 4.2/4.3 determination, and
+        * linemode negotiation.
+        */
+       telrcv();
+
+       (void) ioctl(f, FIONBIO, (char *)&on);
+       (void) ioctl(p, FIONBIO, (char *)&on);
+#if    defined(CRAY2) && defined(UNICOS5)
+       init_termdriver(f, p, interrupt, sendbrk);
+#endif
+
+#if    defined(SO_OOBINLINE)
+       (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
+#endif /* defined(SO_OOBINLINE) */
+
+#ifdef SIGTSTP
+       (void) signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGTTOU
+       /*
+        * Ignoring SIGTTOU keeps the kernel from blocking us
+        * in ttioct() in /sys/tty.c.
+        */
+       (void) signal(SIGTTOU, SIG_IGN);
+#endif
+
+       (void) signal(SIGCHLD, cleanup);
+
+#if    defined(CRAY2) && defined(UNICOS5)
+       /*
+        * Cray-2 will send a signal when pty modes are changed by slave
+        * side.  Set up signal handler now.
+        */
+       if ((int)signal(SIGUSR1, termstat) < 0)
+               perror("signal");
+       else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
+               perror("ioctl:TCSIGME");
+       /*
+        * Make processing loop check terminal characteristics early on.
+        */
+       termstat();
+#endif
+
+#ifdef NO_SETSID
+       (void) setpgrp(0, 0);
+#else
+       (void) setsid();
+#endif
+#if    defined(TIOCSCTTY) && defined(CRAY)
+       ioctl(p, TIOCSCTTY, 0);
+#endif
 
        /*
         * Show banner that getty never gave.
 
        /*
         * Show banner that getty never gave.
@@ -431,34 +687,50 @@ telnet(f, p)
         * other pty --> client data.
         */
 
         * other pty --> client data.
         */
 
-       gethostname(hostname, sizeof (hostname));
+       (void) gethostname(hostname, sizeof (hostname));
+
        if (getent(defent, "default") == 1) {
                char *getstr();
        if (getent(defent, "default") == 1) {
                char *getstr();
-               char *p=defstrs;
+               char *cp=defstrs;
 
 
-               HE = getstr("he", &p);
-               HN = getstr("hn", &p);
-               IM = getstr("im", &p);
+               HE = getstr("he", &cp);
+               HN = getstr("hn", &cp);
+               IM = getstr("im", &cp);
                if (HN && *HN)
                if (HN && *HN)
-                       strcpy(hostname, HN);
-               edithost(HE, hostname);
-               if (IM && *IM)
-                       putf(IM, ptyibuf+1, p);
+                       (void) strcpy(hostname, HN);
+               if (IM == 0)
+                       IM = "";
        } else {
        } else {
-               sprintf(ptyibuf+1, BANNER, hostname);
+#ifdef CRAY
+               if (hostinfo == 0)
+                       IM = 0;
+               else
+#endif
+                       IM = DEFAULT_IM;
+               HE = 0;
        }
        }
-
-       ptyip = ptyibuf+1;              /* Prime the pump */
-       pcc = strlen(ptyip);            /* ditto */
-
-       /* Clear ptybuf[0] - where the packet information is received */
-       ptyibuf[0] = 0;
-
+       edithost(HE, hostname);
+       if (IM && *IM)
+               putf(IM, ptyibuf2);
+
+       if (pcc)
+               (void) strncat(ptyibuf2, ptyip, pcc+1);
+       ptyip = ptyibuf2;
+       pcc = strlen(ptyip);
+#ifdef LINEMODE
        /*
        /*
-        * Call telrcv() once to pick up anything received during
-        * terminal type negotiation.
+        * Last check to make sure all our states are correct.
         */
         */
-       telrcv();
+       init_termbuf();
+       localstat();
+#endif /* LINEMODE */
+
+#ifdef DIAGNOSTICS
+       if (diagnostic & TD_REPORT) {
+               sprintf(nfrontp, "td: Entering processing loop\r\n");
+               nfrontp += strlen(nfrontp);
+       }
+#endif /* DIAGNOSTICS */
 
        for (;;) {
                fd_set ibits, obits, xbits;
 
        for (;;) {
                fd_set ibits, obits, xbits;
@@ -467,6 +739,10 @@ telnet(f, p)
                if (ncc < 0 && pcc < 0)
                        break;
 
                if (ncc < 0 && pcc < 0)
                        break;
 
+#if    defined(CRAY2) && defined(UNICOS5)
+               if (needtermstat)
+                       _termstat();
+#endif /* defined(CRAY2) && defined(UNICOS5) */
                FD_ZERO(&ibits);
                FD_ZERO(&obits);
                FD_ZERO(&xbits);
                FD_ZERO(&ibits);
                FD_ZERO(&obits);
                FD_ZERO(&xbits);
@@ -476,7 +752,6 @@ telnet(f, p)
                 */
                if (nfrontp - nbackp || pcc > 0) {
                        FD_SET(f, &obits);
                 */
                if (nfrontp - nbackp || pcc > 0) {
                        FD_SET(f, &obits);
-                       FD_SET(p, &xbits);
                } else {
                        FD_SET(p, &ibits);
                }
                } else {
                        FD_SET(p, &ibits);
                }
@@ -548,7 +823,7 @@ telnet(f, p)
                    if (SYNCHing) {
                        int atmark;
 
                    if (SYNCHing) {
                        int atmark;
 
-                       ioctl(net, SIOCATMARK, (char *)&atmark);
+                       (void) ioctl(net, SIOCATMARK, (char *)&atmark);
                        if (atmark) {
                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
                            if ((ncc == -1) && (errno == EINVAL)) {
                        if (atmark) {
                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
                            if ((ncc == -1) && (errno == EINVAL)) {
@@ -575,16 +850,20 @@ telnet(f, p)
                        }
                        netip = netibuf;
                    }
                        }
                        netip = netibuf;
                    }
+#ifdef DIAGNOSTICS
+                   if (diagnostic & (TD_REPORT | TD_NETDATA)) {
+                           sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
+                           nfrontp += strlen(nfrontp);
+                   }
+                   if (diagnostic & TD_NETDATA) {
+                           printdata("nd", netip, ncc);
+                   }
+#endif /* DIAGNOSTICS */
                }
 
                /*
                 * Something to read from the pty...
                 */
                }
 
                /*
                 * Something to read from the pty...
                 */
-               if (FD_ISSET(p, &xbits)) {
-                       if (read(p, ptyibuf, 1) != 1) {
-                               break;
-                       }
-               }
                if (FD_ISSET(p, &ibits)) {
                        pcc = read(p, ptyibuf, BUFSIZ);
                        if (pcc < 0 && errno == EWOULDBLOCK)
                if (FD_ISSET(p, &ibits)) {
                        pcc = read(p, ptyibuf, BUFSIZ);
                        if (pcc < 0 && errno == EWOULDBLOCK)
@@ -592,18 +871,56 @@ telnet(f, p)
                        else {
                                if (pcc <= 0)
                                        break;
                        else {
                                if (pcc <= 0)
                                        break;
-                               /* Skip past "packet" */
+#if    !defined(CRAY2) || !defined(UNICOS5)
+#ifdef LINEMODE
+                               /*
+                                * If ioctl from pty, pass it through net
+                                */
+                               if (ptyibuf[0] & TIOCPKT_IOCTL) {
+                                       copy_termbuf(ptyibuf+1, pcc-1);
+                                       localstat();
+                                       pcc = 1;
+                               }
+#endif LINEMODE
+                               if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
+                                       netclear();     /* clear buffer back */
+#ifdef notdef
+                                       /*
+                                        * We really should have this in, but
+                                        * there are client telnets on some
+                                        * operating systems get screwed up
+                                        * royally if we send them urgent
+                                        * mode data.  So, for now, we'll not
+                                        * do this...
+                                        */
+                                       *nfrontp++ = IAC;
+                                       *nfrontp++ = DM;
+                                       neturg = nfrontp-1; /* off by one XXX */
+#endif
+                               }
+                               if (his_state_is_will(TELOPT_LFLOW) &&
+                                   (ptyibuf[0] &
+                                    (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
+                                       (void) sprintf(nfrontp, "%c%c%c%c%c%c",
+                                           IAC, SB, TELOPT_LFLOW,
+                                           ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
+                                           IAC, SE);
+                                       nfrontp += 6;
+                               }
                                pcc--;
                                ptyip = ptyibuf+1;
                                pcc--;
                                ptyip = ptyibuf+1;
+#else  /* defined(CRAY2) && defined(UNICOS5) */
+                               if (!uselinemode) {
+                                       unpcc = pcc;
+                                       unptyip = ptyibuf;
+                                       pcc = term_output(&unptyip, ptyibuf2,
+                                                               &unpcc, BUFSIZ);
+                                       ptyip = ptyibuf2;
+                               } else
+                                       ptyip = ptyibuf;
+#endif /* defined(CRAY2) && defined(UNICOS5) */
                        }
                }
                        }
                }
-               if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
-                       netclear();     /* clear buffer back */
-                       *nfrontp++ = IAC;
-                       *nfrontp++ = DM;
-                       neturg = nfrontp-1;  /* off by one XXX */
-                       ptyibuf[0] = 0;
-               }
 
                while (pcc > 0) {
                        if ((&netobuf[BUFSIZ] - nfrontp) < 2)
 
                while (pcc > 0) {
                        if ((&netobuf[BUFSIZ] - nfrontp) < 2)
@@ -611,9 +928,13 @@ telnet(f, p)
                        c = *ptyip++ & 0377, pcc--;
                        if (c == IAC)
                                *nfrontp++ = c;
                        c = *ptyip++ & 0377, pcc--;
                        if (c == IAC)
                                *nfrontp++ = c;
+#if    defined(CRAY2) && defined(UNICOS5)
+                       else if (c == '\n' &&
+                                    my_state_is_wont(TELOPT_BINARY) && newmap)
+                               *nfrontp++ = '\r';
+#endif /* defined(CRAY2) && defined(UNICOS5) */
                        *nfrontp++ = c;
                        *nfrontp++ = c;
-                       /* Don't do CR-NUL if we are in binary mode */
-                       if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
+                       if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
                                        *nfrontp++ = *ptyip++ & 0377;
                                        pcc--;
                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
                                        *nfrontp++ = *ptyip++ & 0377;
                                        pcc--;
@@ -621,6 +942,18 @@ telnet(f, p)
                                        *nfrontp++ = '\0';
                        }
                }
                                        *nfrontp++ = '\0';
                        }
                }
+#if    defined(CRAY2) && defined(UNICOS5)
+               /*
+                * If chars were left over from the terminal driver,
+                * note their existence.
+                */
+                if (!uselinemode && unpcc) {
+                       pcc = unpcc;
+                       unpcc = 0;
+                       ptyip = unptyip;
+               }
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+
                if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
                        netflush();
                if (ncc > 0)
                if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
                        netflush();
                if (ncc > 0)
@@ -629,419 +962,13 @@ telnet(f, p)
                        ptyflush();
        }
        cleanup();
                        ptyflush();
        }
        cleanup();
-}
+}  /* end of telnet */
        
        
-/*
- * State for recv fsm
- */
-#define        TS_DATA         0       /* base state */
-#define        TS_IAC          1       /* look for double IAC's */
-#define        TS_CR           2       /* CR-LF ->'s CR */
-#define        TS_SB           3       /* throw away begin's... */
-#define        TS_SE           4       /* ...end's (suboption negotiation) */
-#define        TS_WILL         5       /* will option negotiation */
-#define        TS_WONT         6       /* wont " */
-#define        TS_DO           7       /* do " */
-#define        TS_DONT         8       /* dont " */
-
-telrcv()
-{
-       register int c;
-       static int state = TS_DATA;
-
-       while (ncc > 0) {
-               if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
-                       return;
-               c = *netip++ & 0377, ncc--;
-               switch (state) {
-
-               case TS_CR:
-                       state = TS_DATA;
-                       /* Strip off \n or \0 after a \r */
-                       if ((c == 0) || (c == '\n')) {
-                               break;
-                       }
-                       /* FALL THROUGH */
-
-               case TS_DATA:
-                       if (c == IAC) {
-                               state = TS_IAC;
-                               break;
-                       }
-                       if (inter > 0)
-                               break;
-                       /*
-                        * We now map \r\n ==> \r for pragmatic reasons.
-                        * Many client implementations send \r\n when
-                        * the user hits the CarriageReturn key.
-                        *
-                        * We USED to map \r\n ==> \n, since \r\n says
-                        * that we want to be in column 1 of the next
-                        * printable line, and \n is the standard
-                        * unix way of saying that (\r is only good
-                        * if CRMOD is set, which it normally is).
-                        */
-                       if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
-                               state = TS_CR;
-                       }
-                       *pfrontp++ = c;
-                       break;
-
-               case TS_IAC:
-                       switch (c) {
-
-                       /*
-                        * Send the process on the pty side an
-                        * interrupt.  Do this with a NULL or
-                        * interrupt char; depending on the tty mode.
-                        */
-                       case IP:
-                               interrupt();
-                               break;
-
-                       case BREAK:
-                               sendbrk();
-                               break;
-
-                       /*
-                        * Are You There?
-                        */
-                       case AYT:
-                               strcpy(nfrontp, "\r\n[Yes]\r\n");
-                               nfrontp += 9;
-                               break;
-
-                       /*
-                        * Abort Output
-                        */
-                       case AO: {
-                                       struct ltchars tmpltc;
-
-                                       ptyflush();     /* half-hearted */
-                                       ioctl(pty, TIOCGLTC, &tmpltc);
-                                       if (tmpltc.t_flushc != '\377') {
-                                               *pfrontp++ = tmpltc.t_flushc;
-                                       }
-                                       netclear();     /* clear buffer back */
-                                       *nfrontp++ = IAC;
-                                       *nfrontp++ = DM;
-                                       neturg = nfrontp-1; /* off by one XXX */
-                                       break;
-                               }
-
-                       /*
-                        * Erase Character and
-                        * Erase Line
-                        */
-                       case EC:
-                       case EL: {
-                                       struct sgttyb b;
-                                       char ch;
-
-                                       ptyflush();     /* half-hearted */
-                                       ioctl(pty, TIOCGETP, &b);
-                                       ch = (c == EC) ?
-                                               b.sg_erase : b.sg_kill;
-                                       if (ch != '\377') {
-                                               *pfrontp++ = ch;
-                                       }
-                                       break;
-                               }
-
-                       /*
-                        * Check for urgent data...
-                        */
-                       case DM:
-                               SYNCHing = stilloob(net);
-                               settimer(gotDM);
-                               break;
-
-
-                       /*
-                        * Begin option subnegotiation...
-                        */
-                       case SB:
-                               state = TS_SB;
-                               continue;
-
-                       case WILL:
-                               state = TS_WILL;
-                               continue;
-
-                       case WONT:
-                               state = TS_WONT;
-                               continue;
-
-                       case DO:
-                               state = TS_DO;
-                               continue;
-
-                       case DONT:
-                               state = TS_DONT;
-                               continue;
-
-                       case IAC:
-                               *pfrontp++ = c;
-                               break;
-                       }
-                       state = TS_DATA;
-                       break;
-
-               case TS_SB:
-                       if (c == IAC) {
-                               state = TS_SE;
-                       } else {
-                               SB_ACCUM(c);
-                       }
-                       break;
-
-               case TS_SE:
-                       if (c != SE) {
-                               if (c != IAC) {
-                                       SB_ACCUM(IAC);
-                               }
-                               SB_ACCUM(c);
-                               state = TS_SB;
-                       } else {
-                               SB_TERM();
-                               suboption();    /* handle sub-option */
-                               state = TS_DATA;
-                       }
-                       break;
-
-               case TS_WILL:
-                       if (hisopts[c] != OPT_YES)
-                               willoption(c);
-                       state = TS_DATA;
-                       continue;
-
-               case TS_WONT:
-                       if (hisopts[c] != OPT_NO)
-                               wontoption(c);
-                       state = TS_DATA;
-                       continue;
-
-               case TS_DO:
-                       if (myopts[c] != OPT_YES)
-                               dooption(c);
-                       state = TS_DATA;
-                       continue;
-
-               case TS_DONT:
-                       if (myopts[c] != OPT_NO) {
-                               dontoption(c);
-                       }
-                       state = TS_DATA;
-                       continue;
-
-               default:
-                       syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
-                       printf("telnetd: panic state=%d\n", state);
-                       exit(1);
-               }
-       }
-}
-
-willoption(option)
-       int option;
-{
-       char *fmt;
-
-       switch (option) {
-
-       case TELOPT_BINARY:
-               mode(RAW, 0);
-               fmt = doopt;
-               break;
-
-       case TELOPT_ECHO:
-               not42 = 0;              /* looks like a 4.2 system */
-               /*
-                * Now, in a 4.2 system, to break them out of ECHOing
-                * (to the terminal) mode, we need to send a "WILL ECHO".
-                * Kludge upon kludge!
-                */
-               if (myopts[TELOPT_ECHO] == OPT_YES) {
-                   dooption(TELOPT_ECHO);
-               }
-               fmt = dont;
-               break;
-
-       case TELOPT_TTYPE:
-               settimer(ttypeopt);
-               if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
-                   hisopts[TELOPT_TTYPE] = OPT_YES;
-                   return;
-               }
-               fmt = doopt;
-               break;
-
-       case TELOPT_SGA:
-               fmt = doopt;
-               break;
-
-       case TELOPT_TM:
-               fmt = dont;
-               break;
-
-       default:
-               fmt = dont;
-               break;
-       }
-       if (fmt == doopt) {
-               hisopts[option] = OPT_YES;
-       } else {
-               hisopts[option] = OPT_NO;
-       }
-       (void) sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof (dont) - 2;
-}
-
-wontoption(option)
-       int option;
-{
-       char *fmt;
-
-       switch (option) {
-       case TELOPT_ECHO:
-               not42 = 1;              /* doesn't seem to be a 4.2 system */
-               break;
-
-       case TELOPT_BINARY:
-               mode(0, RAW);
-               break;
-
-       case TELOPT_TTYPE:
-           settimer(ttypeopt);
-           break;
-       }
-
-       fmt = dont;
-       hisopts[option] = OPT_NO;
-       (void) sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof (doopt) - 2;
-}
-
-dooption(option)
-       int option;
-{
-       char *fmt;
-
-       switch (option) {
-
-       case TELOPT_TM:
-               fmt = wont;
-               break;
-
-       case TELOPT_ECHO:
-               mode(ECHO|CRMOD, 0);
-               fmt = will;
-               break;
-
-       case TELOPT_BINARY:
-               mode(RAW, 0);
-               fmt = will;
-               break;
-
-       case TELOPT_SGA:
-               fmt = will;
-               break;
-
-       default:
-               fmt = wont;
-               break;
-       }
-       if (fmt == will) {
-           myopts[option] = OPT_YES;
-       } else {
-           myopts[option] = OPT_NO;
-       }
-       (void) sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof (doopt) - 2;
-}
-
-
-dontoption(option)
-int option;
-{
-    char *fmt;
-
-    switch (option) {
-    case TELOPT_ECHO:          /* we should stop echoing */
-       mode(0, ECHO);
-       fmt = wont;
-       break;
-
-    default:
-       fmt = wont;
-       break;
-    }
-
-    if (fmt = wont) {
-       myopts[option] = OPT_NO;
-    } else {
-       myopts[option] = OPT_YES;
-    }
-    (void) sprintf(nfrontp, fmt, option);
-    nfrontp += sizeof (wont) - 2;
-}
-
-/*
- * suboption()
- *
- *     Look at the sub-option buffer, and try to be helpful to the other
- * side.
- *
- *     Currently we recognize:
- *
- *     Terminal type is
- */
-
-suboption()
-{
-    switch (SB_GET()) {
-    case TELOPT_TTYPE: {               /* Yaaaay! */
-       static char terminalname[5+41] = "TERM=";
-
-       settimer(ttypesubopt);
-
-       if (SB_GET() != TELQUAL_IS) {
-           return;             /* ??? XXX but, this is the most robust */
-       }
-
-       terminaltype = terminalname+strlen(terminalname);
-
-       while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
-                                                                   !SB_EOF()) {
-           register int c;
-
-           c = SB_GET();
-           if (isupper(c)) {
-               c = tolower(c);
-           }
-           *terminaltype++ = c;    /* accumulate name */
-       }
-       *terminaltype = 0;
-       terminaltype = terminalname;
-       break;
-    }
-
-    default:
-       ;
-    }
-}
-
-mode(on, off)
-       int on, off;
-{
-       struct sgttyb b;
-
-       ptyflush();
-       ioctl(pty, TIOCGETP, &b);
-       b.sg_flags |= on;
-       b.sg_flags &= ~off;
-       ioctl(pty, TIOCSETP, &b);
-}
+#ifndef        TCSIG
+# ifdef        TIOCSIG
+#  define TCSIG TIOCSIG
+# endif
+#endif
 
 /*
  * Send interrupt to process on other side of pty.
 
 /*
  * Send interrupt to process on other side of pty.
@@ -1050,17 +977,15 @@ mode(on, off)
  */
 interrupt()
 {
  */
 interrupt()
 {
-       struct sgttyb b;
-       struct tchars tchars;
-
        ptyflush();     /* half-hearted */
        ptyflush();     /* half-hearted */
-       ioctl(pty, TIOCGETP, &b);
-       if (b.sg_flags & RAW) {
-               *pfrontp++ = '\0';
-               return;
-       }
-       *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
-               '\177' : tchars.t_intrc;
+
+#ifdef TCSIG
+       (void) ioctl(pty, TCSIG, (char *)SIGINT);
+#else  /* TCSIG */
+       init_termbuf();
+       *pfrontp++ = slctab[SLC_IP].sptr ?
+                       (unsigned char)*slctab[SLC_IP].sptr : '\177';
+#endif /* TCSIG */
 }
 
 /*
 }
 
 /*
@@ -1070,343 +995,42 @@ interrupt()
  */
 sendbrk()
 {
  */
 sendbrk()
 {
-       struct sgttyb b;
-       struct tchars tchars;
-
        ptyflush();     /* half-hearted */
        ptyflush();     /* half-hearted */
-       ioctl(pty, TIOCGETP, &b);
-       if (b.sg_flags & RAW) {
-               *pfrontp++ = '\0';
-               return;
-       }
-       *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
-               '\034' : tchars.t_quitc;
-}
-
-ptyflush()
-{
-       int n;
-
-       if ((n = pfrontp - pbackp) > 0)
-               n = write(pty, pbackp, n);
-       if (n < 0)
-               return;
-       pbackp += n;
-       if (pbackp == pfrontp)
-               pbackp = pfrontp = ptyobuf;
-}
-\f
-/*
- * nextitem()
- *
- *     Return the address of the next "item" in the TELNET data
- * stream.  This will be the address of the next character if
- * the current address is a user data character, or it will
- * be the address of the character following the TELNET command
- * if the current address is a TELNET IAC ("I Am a Command")
- * character.
- */
-
-char *
-nextitem(current)
-char   *current;
-{
-    if ((*current&0xff) != IAC) {
-       return current+1;
-    }
-    switch (*(current+1)&0xff) {
-    case DO:
-    case DONT:
-    case WILL:
-    case WONT:
-       return current+3;
-    case SB:           /* loop forever looking for the SE */
-       {
-           register char *look = current+2;
-
-           for (;;) {
-               if ((*look++&0xff) == IAC) {
-                   if ((*look++&0xff) == SE) {
-                       return look;
-                   }
-               }
-           }
-       }
-    default:
-       return current+2;
-    }
-}
-
-
-/*
- * netclear()
- *
- *     We are about to do a TELNET SYNCH operation.  Clear
- * the path to the network.
- *
- *     Things are a bit tricky since we may have sent the first
- * byte or so of a previous TELNET command into the network.
- * So, we have to scan the network buffer from the beginning
- * until we are up to where we want to be.
- *
- *     A side effect of what we do, just to keep things
- * simple, is to clear the urgent data pointer.  The principal
- * caller should be setting the urgent data pointer AFTER calling
- * us in any case.
- */
-
-netclear()
-{
-    register char *thisitem, *next;
-    char *good;
-#define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
-                               ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
-
-    thisitem = netobuf;
-
-    while ((next = nextitem(thisitem)) <= nbackp) {
-       thisitem = next;
-    }
-
-    /* Now, thisitem is first before/at boundary. */
-
-    good = netobuf;    /* where the good bytes go */
-
-    while (nfrontp > thisitem) {
-       if (wewant(thisitem)) {
-           int length;
-
-           next = thisitem;
-           do {
-               next = nextitem(next);
-           } while (wewant(next) && (nfrontp > next));
-           length = next-thisitem;
-           bcopy(thisitem, good, length);
-           good += length;
-           thisitem = next;
-       } else {
-           thisitem = nextitem(thisitem);
-       }
-    }
-
-    nbackp = netobuf;
-    nfrontp = good;            /* next byte to be sent */
-    neturg = 0;
-}
-\f
-/*
- *  netflush
- *             Send as much data as possible to the network,
- *     handling requests for urgent data.
- */
-
-
-netflush()
-{
-    int n;
-
-    if ((n = nfrontp - nbackp) > 0) {
-       /*
-        * if no urgent data, or if the other side appears to be an
-        * old 4.2 client (and thus unable to survive TCP urgent data),
-        * write the entire buffer in non-OOB mode.
-        */
-       if ((neturg == 0) || (not42 == 0)) {
-           n = write(net, nbackp, n);  /* normal write */
-       } else {
-           n = neturg - nbackp;
-           /*
-            * In 4.2 (and 4.3) systems, there is some question about
-            * what byte in a sendOOB operation is the "OOB" data.
-            * To make ourselves compatible, we only send ONE byte
-            * out of band, the one WE THINK should be OOB (though
-            * we really have more the TCP philosophy of urgent data
-            * rather than the Unix philosophy of OOB data).
-            */
-           if (n > 1) {
-               n = send(net, nbackp, n-1, 0);  /* send URGENT all by itself */
-           } else {
-               n = send(net, nbackp, n, MSG_OOB);      /* URGENT data */
-           }
-       }
-    }
-    if (n < 0) {
-       if (errno == EWOULDBLOCK)
-           return;
-       /* should blow this guy away... */
-       return;
-    }
-    nbackp += n;
-    if (nbackp >= neturg) {
-       neturg = 0;
-    }
-    if (nbackp == nfrontp) {
-       nbackp = nfrontp = netobuf;
-    }
-}
-
-cleanup()
-{
-
-       rmut();
-       vhangup();      /* XXX */
-       shutdown(net, 2);
-       exit(1);
-}
-
-#include <utmp.h>
-
-struct utmp wtmp;
-char   wtmpf[] = "/usr/adm/wtmp";
-char   utmpf[] = "/etc/utmp";
-#define SCPYN(a, b)    strncpy(a, b, sizeof(a))
-#define SCMPN(a, b)    strncmp(a, b, sizeof(a))
-
-rmut()
-{
-       register f;
-       int found = 0;
-       struct utmp *u, *utmp;
-       int nutmp;
-       struct stat statbf;
-
-       f = open(utmpf, O_RDWR);
-       if (f >= 0) {
-               fstat(f, &statbf);
-               utmp = (struct utmp *)malloc(statbf.st_size);
-               if (!utmp)
-                       syslog(LOG_ERR, "utmp malloc failed");
-               if (statbf.st_size && utmp) {
-                       nutmp = read(f, utmp, statbf.st_size);
-                       nutmp /= sizeof(struct utmp);
-               
-                       for (u = utmp ; u < &utmp[nutmp] ; u++) {
-                               if (SCMPN(u->ut_line, line+5) ||
-                                   u->ut_name[0]==0)
-                                       continue;
-                               lseek(f, ((long)u)-((long)utmp), L_SET);
-                               SCPYN(u->ut_name, "");
-                               SCPYN(u->ut_host, "");
-                               time(&u->ut_time);
-                               write(f, (char *)u, sizeof(wtmp));
-                               found++;
-                       }
-               }
-               close(f);
-       }
-       if (found) {
-               f = open(wtmpf, O_WRONLY|O_APPEND);
-               if (f >= 0) {
-                       SCPYN(wtmp.ut_line, line+5);
-                       SCPYN(wtmp.ut_name, "");
-                       SCPYN(wtmp.ut_host, "");
-                       time(&wtmp.ut_time);
-                       write(f, (char *)&wtmp, sizeof(wtmp));
-                       close(f);
-               }
-       }
-       chmod(line, 0666);
-       chown(line, 0, 0);
-       line[strlen("/dev/")] = 'p';
-       chmod(line, 0666);
-       chown(line, 0, 0);
+#ifdef TCSIG
+       (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
+#else  /* TCSIG */
+       init_termbuf();
+       *pfrontp++ = slctab[SLC_ABORT].sptr ?
+                       (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
+#endif /* TCSIG */
 }
 
 }
 
-char   editedhost[32];
-
-edithost(pat, host)
-       register char *pat;
-       register char *host;
+sendsusp()
 {
 {
-       register char *res = editedhost;
-
-       if (!pat)
-               pat = "";
-       while (*pat) {
-               switch (*pat) {
-
-               case '#':
-                       if (*host)
-                               host++;
-                       break;
-
-               case '@':
-                       if (*host)
-                               *res++ = *host++;
-                       break;
-
-               default:
-                       *res++ = *pat;
-                       break;
-
-               }
-               if (res == &editedhost[sizeof editedhost - 1]) {
-                       *res = '\0';
-                       return;
-               }
-               pat++;
-       }
-       if (*host)
-               strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
-       else
-               *res = '\0';
-       editedhost[sizeof editedhost - 1] = '\0';
-}
-
-static char *putlocation;
-
-puts(s)
-register char *s;
-{
-
-       while (*s)
-               putchr(*s++);
-}
-
-putchr(cc)
-{
-       *putlocation++ = cc;
+#ifdef SIGTSTP
+       ptyflush();     /* half-hearted */
+# ifdef        TCSIG
+       (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
+# else /* TCSIG */
+       *pfrontp++ = slctab[SLC_SUSP].sptr ?
+                       (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
+# endif        /* TCSIG */
+#endif /* SIGTSTP */
 }
 
 }
 
-putf(cp, where, tty)
-register char *cp;
-char *where;
-int tty;
+doeof()
 {
 {
-       char *slash;
-       char datebuffer[60];
-       extern char *rindex();
-
-       putlocation = where;
-
-       while (*cp) {
-               if (*cp != '%') {
-                       putchr(*cp++);
-                       continue;
-               }
-               switch (*++cp) {
-
-               case 't':
-                       slash = rindex(line, '/');
-                       if (slash == (char *) 0)
-                               puts(line);
-                       else
-                               puts(&slash[1]);
-                       break;
-
-               case 'h':
-                       puts(editedhost);
-                       break;
-
-               case 'd':
-                       get_date(datebuffer);
-                       puts(datebuffer);
-                       break;
-
-               case '%':
-                       putchr('%');
-                       break;
-               }
-               cp++;
+#if    defined(USE_TERMIO) && defined(SYSV_TERMIO)
+       extern char oldeofc;
+#endif
+       init_termbuf();
+
+#if    defined(USE_TERMIO) && defined(SYSV_TERMIO)
+       if (!tty_isediting()) {
+               *pfrontp++ = oldeofc;
+               return;
        }
        }
+#endif
+       *pfrontp++ = slctab[SLC_EOF].sptr ?
+                       (unsigned char)*slctab[SLC_EOF].sptr : '\004';
 }
 }