use library version of rmut
[unix-history] / usr / src / libexec / telnetd / telnetd.c
index 0a58886..426a1e7 100644 (file)
@@ -1,23 +1,34 @@
 /*
 /*
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1983, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
 #ifndef lint
 char copyright[] =
  */
 
 #ifndef lint
 char copyright[] =
-"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+"@(#) Copyright (c) 1983, 1986 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.14 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)telnetd.c  5.30 (Berkeley) %G%";
+#endif /* not lint */
 
 /*
 
 /*
- * Stripped-down telnet server.
+ * Telnet server.
  */
  */
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <sys/file.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <sys/file.h>
@@ -36,14 +47,10 @@ static char sccsid[] = "@(#)telnetd.c       5.14 (Berkeley) %G%";
 #include <syslog.h>
 #include <ctype.h>
 
 #include <syslog.h>
 #include <ctype.h>
 
-#define        BELL    '\07'
-#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
-
-#define        OPT_DONT        0               /* don't do this option */
-#define        OPT_WONT        0               /* won't do this option */
-#define        OPT_DO          1               /* do this option */
-#define        OPT_WILL        1               /* will do this option */
-#define        OPT_ALWAYS_LOOK 2               /* special case for echo */
+#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   hisopts[256];
 char   myopts[256];
 
@@ -69,13 +76,17 @@ char        *neturg = 0;            /* one past last bye of urgent data */
        /* the remote system seems to NOT be an old 4.2 */
 int    not42 = 1;
 
        /* the remote system seems to NOT be an old 4.2 */
 int    not42 = 1;
 
+#define        BANNER  "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
 
 
-char   subbuffer[100], *subpointer, *subend;   /* buffer for sub-options */
+               /* buffer for sub-options */
+char   subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
 #define        SB_CLEAR()      subpointer = subbuffer;
 #define        SB_CLEAR()      subpointer = subbuffer;
-#define        SB_TERM()       subend = subpointer;
+#define        SB_TERM()       { subend = subpointer; SB_CLEAR(); }
 #define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
                                *subpointer++ = (c); \
                        }
 #define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
                                *subpointer++ = (c); \
                        }
+#define        SB_GET()        ((*subpointer++)&0xff)
+#define        SB_EOF()        (subpointer >= subend)
 
 int    pcc, ncc;
 
 
 int    pcc, ncc;
 
@@ -96,10 +107,14 @@ struct {
        echotoggle,             /* last time user entered echo character */
        modenegotiated,         /* last time operating mode negotiated */
        didnetreceive,          /* last time we read data from network */
        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;
 
        gotDM;                  /* when did we last see a data mark */
 } clocks;
 
-#define        settimer(x)     clocks.x = clocks.system++
+#define        settimer(x)     (clocks.x = ++clocks.system)
+#define        sequenceIs(x,y) (clocks.x < clocks.y)
 \f
 main(argc, argv)
        char *argv[];
 \f
 main(argc, argv)
        char *argv[];
@@ -161,31 +176,70 @@ main(argc, argv)
        doit(0, &from);
 }
 
        doit(0, &from);
 }
 
+char   *terminaltype = 0;
+char   *envinit[2];
+int    cleanup();
 
 /*
 
 /*
- * Get()
- *
- *     Return next character from file descriptor.
+ * ttloop
  *
  *
- *     This is not meant to be very efficient, since it is only
- * run during startup.
+ *     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.
  */
 
  */
 
-Get(f)
-int    f;              /* the file descriptor */
+void
+ttloop()
 {
 {
-    char       input;
-
-    if (read(f, &input, 1) != 1) {
-       syslog(LOG_ERR, "read: %m\n");
+    if (nfrontp-nbackp) {
+       netflush();
+    }
+    ncc = read(net, netibuf, sizeof netibuf);
+    if (ncc < 0) {
+       syslog(LOG_INFO, "ttloop:  read: %m\n");
+       exit(1);
+    } else if (ncc == 0) {
+       syslog(LOG_INFO, "ttloop:  peer died: %m\n");
        exit(1);
     }
        exit(1);
     }
-    return input&0xff;
+    netip = netibuf;
+    telrcv();                  /* state machine */
+    if (ncc > 0) {
+       pfrontp = pbackp = ptyobuf;
+       telrcv();
+    }
 }
 
 }
 
-char   *terminaltype;
-char   *envinit[2];
-int    cleanup();
+/*
+ * getterminaltype
+ *
+ *     Ask the other end to send along its terminal type.
+ * Output is the variable terminaltype filled in.
+ */
+
+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)) {
+       ttloop();
+    }
+    if (hisopts[TELOPT_TTYPE] == OPT_YES) {
+       static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
+
+       bcopy(sbbuf, nfrontp, sizeof sbbuf);
+       nfrontp += sizeof sbbuf;
+       while (sequenceIs(ttypesubopt, getterminal)) {
+           ttloop();
+       }
+    }
+}
 
 /*
  * Get a pty, scan input lines.
 
 /*
  * Get a pty, scan input lines.
@@ -199,103 +253,6 @@ doit(f, who)
        struct sgttyb b;
        struct hostent *hp;
        int c;
        struct sgttyb b;
        struct hostent *hp;
        int c;
-       int gotterminaltype = 0;
-
-       /*
-        * Try to get a terminal type from the foreign host.
-        */
-
-       {
-           static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
-
-           terminaltype = 0;
-           if (write(f, sbuf, sizeof sbuf) == -1) {
-               syslog(LOG_ERR, "write sbuf: %m\n");
-               exit(1);
-           }
-           for (;;) {          /* ugly, but we are VERY early */
-               while ((c = Get(f)) != IAC) {
-                   NIACCUM(c);
-               }
-               if ((c = Get(f)) == WILL) {
-                   if ((c = Get(f)) == TELOPT_TTYPE) {
-                       static char sbbuf[] = { IAC, SB, TELOPT_TTYPE,
-                                                       TELQUAL_SEND, IAC, SE };
-                       if (write(f, sbbuf, sizeof sbbuf) == -1) {
-                           syslog(LOG_ERR, "write sbbuf: %m\n");
-                           exit(1);
-                       }
-                       break;
-                   } else {
-                       NIACCUM(IAC);
-                       NIACCUM(WILL);
-                       NIACCUM(c);
-                   }
-               } else if (c == WONT) {
-                   if ((c = Get(f)) == TELOPT_TTYPE) {
-                       terminaltype = "TERM=network";
-                       break;
-                   } else {
-                       NIACCUM(IAC);
-                       NIACCUM(WONT);
-                       NIACCUM(c);
-                   }
-               } else {
-                   NIACCUM(IAC);
-                   NIACCUM(c);
-               }
-           }
-           if (!terminaltype) {
-               for (;;) {
-                   while ((c = Get(f)) != IAC) {
-                       NIACCUM(c);
-                   }
-                   if ((c = Get(f)) != SB) {
-                       NIACCUM(IAC);
-                       NIACCUM(c);
-                   } else if ((c = Get(f)) != TELOPT_TTYPE) {
-                       NIACCUM(IAC);
-                       NIACCUM(SB);
-                       NIACCUM(c);
-                   } else if ((c = Get(f)) != TELQUAL_IS) {
-                       NIACCUM(IAC);
-                       NIACCUM(SB);
-                       NIACCUM(TELOPT_TTYPE);
-                       NIACCUM(c);
-                   } else {            /* Yaaaay! */
-                       static char terminalname[5+41] = "TERM=";
-
-                       terminaltype = terminalname+strlen(terminalname);
-
-                       while (terminaltype <
-                                   (terminalname + sizeof terminalname-1)) {
-                           if ((c = Get(f)) == IAC) {
-                               if ((c = Get(f)) == SE) {
-                                   break;              /* done */
-                               } else {
-                                   *terminaltype++ = IAC;      /* ? */
-                                   if (isupper(c)) {
-                                       c = tolower(c);
-                                   }
-                                   *terminaltype++ = c;
-                               }
-                           } else {
-                               if (isupper(c)) {
-                                   c = tolower(c);
-                               }
-                               *terminaltype++ = c;    /* accumulate name */
-                           }
-                       }
-                       *terminaltype = 0;
-                       terminaltype = terminalname;
-                       gotterminaltype = 1;
-                       break;
-                   }
-               }
-           }
-           envinit[0] = terminaltype;
-           envinit[1] = 0;
-       }
 
        for (c = 'p'; c <= 's'; c++) {
                struct stat stb;
 
        for (c = 'p'; c <= 's'; c++) {
                struct stat stb;
@@ -306,8 +263,8 @@ doit(f, who)
                if (stat(line, &stb) < 0)
                        break;
                for (i = 0; i < 16; i++) {
                if (stat(line, &stb) < 0)
                        break;
                for (i = 0; i < 16; i++) {
-                       line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
-                       p = open(line, 2);
+                       line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
+                       p = open(line, O_RDWR);
                        if (p > 0)
                                goto gotpty;
                }
                        if (p > 0)
                                goto gotpty;
                }
@@ -324,7 +281,15 @@ gotpty:
        }
        t = open(line, O_RDWR);
        if (t < 0)
        }
        t = open(line, O_RDWR);
        if (t < 0)
-               fatalperror(f, line, errno);
+               fatalperror(f, line);
+       if (fchmod(t, 0))
+               fatalperror(f, line);
+       (void)signal(SIGHUP, SIG_IGN);
+       vhangup();
+       (void)signal(SIGHUP, SIG_DFL);
+       t = open(line, O_RDWR);
+       if (t < 0)
+               fatalperror(f, line);
        ioctl(t, TIOCGETP, &b);
        b.sg_flags = CRMOD|XTABS|ANYP;
        ioctl(t, TIOCSETP, &b);
        ioctl(t, TIOCGETP, &b);
        b.sg_flags = CRMOD|XTABS|ANYP;
        ioctl(t, TIOCSETP, &b);
@@ -337,8 +302,17 @@ gotpty:
                host = hp->h_name;
        else
                host = inet_ntoa(who->sin_addr);
                host = hp->h_name;
        else
                host = inet_ntoa(who->sin_addr);
+
+       net = f;
+       pty = p;
+
+       /*
+        * get terminal type.
+        */
+       getterminaltype();
+
        if ((i = fork()) < 0)
        if ((i = fork()) < 0)
-               fatalperror(f, "fork", errno);
+               fatalperror(f, "fork");
        if (i)
                telnet(f, p);
        close(f);
        if (i)
                telnet(f, p);
        close(f);
@@ -347,14 +321,18 @@ gotpty:
        dup2(t, 1);
        dup2(t, 2);
        close(t);
        dup2(t, 1);
        dup2(t, 2);
        close(t);
+       envinit[0] = terminaltype;
+       envinit[1] = 0;
        environ = envinit;
        /*
         * -h : pass on name of host.
        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).
         */
        execl("/bin/login", "login", "-h", host,
         * -p : don't clobber the environment (so terminal type stays set).
         */
        execl("/bin/login", "login", "-h", host,
-                                       gotterminaltype ? "-p" : 0, 0);
-       fatalperror(f, "/bin/login", errno);
+                                       terminaltype ? "-p" : 0, 0);
+       fatalperror(f, "/bin/login");
        /*NOTREACHED*/
 }
 
        /*NOTREACHED*/
 }
 
@@ -369,10 +347,9 @@ fatal(f, msg)
        exit(1);
 }
 
        exit(1);
 }
 
-fatalperror(f, msg, errno)
+fatalperror(f, msg)
        int f;
        char *msg;
        int f;
        char *msg;
-       int errno;
 {
        char buf[BUFSIZ];
        extern char *sys_errlist[];
 {
        char buf[BUFSIZ];
        extern char *sys_errlist[];
@@ -398,10 +375,10 @@ int       s;              /* socket number */
        FD_ZERO(&excepts);
        FD_SET(s, &excepts);
        value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
        FD_ZERO(&excepts);
        FD_SET(s, &excepts);
        value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
-    } while ((value == -1) && (errno = EINTR));
+    } while ((value == -1) && (errno == EINTR));
 
     if (value < 0) {
 
     if (value < 0) {
-       fatalperror(pty, "select", errno);
+       fatalperror(pty, "select");
     }
     if (FD_ISSET(s, &excepts)) {
        return 1;
     }
     if (FD_ISSET(s, &excepts)) {
        return 1;
@@ -417,23 +394,39 @@ int       s;              /* socket number */
 telnet(f, p)
 {
        int on = 1;
 telnet(f, p)
 {
        int on = 1;
-       char hostname[32];
+       char hostname[MAXHOSTNAMELEN];
+#define        TABBUFSIZ       512
+       char    defent[TABBUFSIZ];
+       char    defstrs[TABBUFSIZ];
+#undef TABBUFSIZ
+       char *HE;
+       char *HN;
+       char *IM;
 
 
-       net = f, pty = p;
        ioctl(f, FIONBIO, &on);
        ioctl(p, FIONBIO, &on);
        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);
 #if    defined(SO_OOBINLINE)
        setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
 #endif /* defined(SO_OOBINLINE) */
        signal(SIGTSTP, SIG_IGN);
+       /*
+        * Ignoring SIGTTOU keeps the kernel from blocking us
+        * in ttioctl() in /sys/tty.c.
+        */
+       signal(SIGTTOU, SIG_IGN);
        signal(SIGCHLD, cleanup);
        setpgrp(0, 0);
 
        /*
         * Request to do remote echo and to suppress go ahead.
         */
        signal(SIGCHLD, cleanup);
        setpgrp(0, 0);
 
        /*
         * Request to do remote echo and to suppress go ahead.
         */
-       dooption(TELOPT_ECHO);
-       dooption(TELOPT_SGA);
+       if (!myopts[TELOPT_ECHO]) {
+           dooption(TELOPT_ECHO);
+       }
+       if (!myopts[TELOPT_SGA]) {
+           dooption(TELOPT_SGA);
+       }
        /*
         * 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.
@@ -444,16 +437,40 @@ 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).
         */
-       sprintf(nfrontp, doopt, TELOPT_ECHO);
+       (void) sprintf(nfrontp, doopt, TELOPT_ECHO);
        nfrontp += sizeof doopt-2;
        nfrontp += sizeof doopt-2;
-       hisopts[TELOPT_ECHO] = OPT_ALWAYS_LOOK;
+       hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
 
        /*
         * Show banner that getty never gave.
 
        /*
         * Show banner that getty never gave.
+        *
+        * We put the banner in the pty input buffer.  This way, it
+        * gets carriage return null processing, etc., just like all
+        * other pty --> client data.
         */
         */
+
        gethostname(hostname, sizeof (hostname));
        gethostname(hostname, sizeof (hostname));
-       sprintf(nfrontp, BANNER, hostname, "");
-       nfrontp += strlen(nfrontp);
+       if (getent(defent, "default") == 1) {
+               char *getstr();
+               char *p=defstrs;
+
+               HE = getstr("he", &p);
+               HN = getstr("hn", &p);
+               IM = getstr("im", &p);
+               if (HN && *HN)
+                       strcpy(hostname, HN);
+               edithost(HE, hostname);
+               if (IM && *IM)
+                       putf(IM, ptyibuf+1);
+       } else {
+               sprintf(ptyibuf+1, BANNER, hostname);
+       }
+
+       ptyip = ptyibuf+1;              /* Prime the pump */
+       pcc = strlen(ptyip);            /* ditto */
+
+       /* Clear ptybuf[0] - where the packet information is received */
+       ptyibuf[0] = 0;
 
        /*
         * Call telrcv() once to pick up anything received during
 
        /*
         * Call telrcv() once to pick up anything received during
@@ -477,6 +494,7 @@ 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);
                }
@@ -512,7 +530,7 @@ telnet(f, p)
                if (FD_ISSET(net, &ibits)) {
 #if    !defined(SO_OOBINLINE)
                        /*
                if (FD_ISSET(net, &ibits)) {
 #if    !defined(SO_OOBINLINE)
                        /*
-                        * In 4.2 (and some early 4.3) systems, the
+                        * In 4.2 (and 4.3 beta) systems, the
                         * OOB indication and data handling in the kernel
                         * is such that if two separate TCP Urgent requests
                         * come in, one byte of TCP data will be overlaid.
                         * OOB indication and data handling in the kernel
                         * is such that if two separate TCP Urgent requests
                         * come in, one byte of TCP data will be overlaid.
@@ -553,7 +571,7 @@ telnet(f, p)
                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
                            if ((ncc == -1) && (errno == EINVAL)) {
                                ncc = read(net, netibuf, sizeof (netibuf));
                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
                            if ((ncc == -1) && (errno == EINVAL)) {
                                ncc = read(net, netibuf, sizeof (netibuf));
-                               if (clocks.didnetreceive < clocks.gotDM) {
+                               if (sequenceIs(didnetreceive, gotDM)) {
                                    SYNCHing = stilloob(net);
                                }
                            }
                                    SYNCHing = stilloob(net);
                                }
                            }
@@ -580,6 +598,11 @@ telnet(f, p)
                /*
                 * 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)
@@ -587,9 +610,18 @@ telnet(f, p)
                        else {
                                if (pcc <= 0)
                                        break;
                        else {
                                if (pcc <= 0)
                                        break;
-                               ptyip = ptyibuf;
+                               /* Skip past "packet" */
+                               pcc--;
+                               ptyip = ptyibuf+1;
                        }
                }
                        }
                }
+               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)
@@ -598,7 +630,8 @@ telnet(f, p)
                        if (c == IAC)
                                *nfrontp++ = c;
                        *nfrontp++ = c;
                        if (c == IAC)
                                *nfrontp++ = c;
                        *nfrontp++ = c;
-                       if (c == '\r') {
+                       /* Don't do CR-NUL if we are in binary mode */
+                       if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
                                        *nfrontp++ = *ptyip++ & 0377;
                                        pcc--;
                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
                                        *nfrontp++ = *ptyip++ & 0377;
                                        pcc--;
@@ -642,6 +675,7 @@ telrcv()
 
                case TS_CR:
                        state = TS_DATA;
 
                case TS_CR:
                        state = TS_DATA;
+                       /* Strip off \n or \0 after a \r */
                        if ((c == 0) || (c == '\n')) {
                                break;
                        }
                        if ((c == 0) || (c == '\n')) {
                                break;
                        }
@@ -655,19 +689,18 @@ telrcv()
                        if (inter > 0)
                                break;
                        /*
                        if (inter > 0)
                                break;
                        /*
-                        * We map \r\n ==> \n, since \r\n says
+                        * 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).
                         */
                         * 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 ((myopts[TELOPT_BINARY] == OPT_DONT) && c == '\r') {
-                               if ((ncc > 0) && ('\n' == *netip)) {
-                                       netip++; ncc--;
-                                       c = '\n';
-                               } else {
-                                       state = TS_CR;
-                               }
+                       if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
+                               state = TS_CR;
                        }
                        *pfrontp++ = c;
                        break;
                        }
                        *pfrontp++ = c;
                        break;
@@ -795,31 +828,32 @@ telrcv()
                        break;
 
                case TS_WILL:
                        break;
 
                case TS_WILL:
-                       if (hisopts[c] != OPT_WILL)
+                       if (hisopts[c] != OPT_YES)
                                willoption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_WONT:
                                willoption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_WONT:
-                       if (hisopts[c] != OPT_WONT)
+                       if (hisopts[c] != OPT_NO)
                                wontoption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DO:
                                wontoption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DO:
-                       if (myopts[c] != OPT_DO)
+                       if (myopts[c] != OPT_YES)
                                dooption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DONT:
                                dooption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DONT:
-                       if (myopts[c] != OPT_DONT) {
+                       if (myopts[c] != OPT_NO) {
                                dontoption(c);
                        }
                        state = TS_DATA;
                        continue;
 
                default:
                                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);
                }
                        printf("telnetd: panic state=%d\n", state);
                        exit(1);
                }
@@ -845,13 +879,21 @@ willoption(option)
                 * (to the terminal) mode, we need to send a "WILL ECHO".
                 * Kludge upon kludge!
                 */
                 * (to the terminal) mode, we need to send a "WILL ECHO".
                 * Kludge upon kludge!
                 */
-               if (myopts[TELOPT_ECHO] == OPT_DO) {
+               if (myopts[TELOPT_ECHO] == OPT_YES) {
                    dooption(TELOPT_ECHO);
                }
                fmt = dont;
                break;
 
        case TELOPT_TTYPE:
                    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_SGA:
                fmt = doopt;
                break;
@@ -865,11 +907,11 @@ willoption(option)
                break;
        }
        if (fmt == doopt) {
                break;
        }
        if (fmt == doopt) {
-               hisopts[option] = OPT_WILL;
+               hisopts[option] = OPT_YES;
        } else {
        } else {
-               hisopts[option] = OPT_WONT;
+               hisopts[option] = OPT_NO;
        }
        }
-       sprintf(nfrontp, fmt, option);
+       (void) sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (dont) - 2;
 }
 
        nfrontp += sizeof (dont) - 2;
 }
 
@@ -886,10 +928,15 @@ wontoption(option)
        case TELOPT_BINARY:
                mode(0, RAW);
                break;
        case TELOPT_BINARY:
                mode(0, RAW);
                break;
+
+       case TELOPT_TTYPE:
+           settimer(ttypeopt);
+           break;
        }
        }
+
        fmt = dont;
        fmt = dont;
-       hisopts[option] = OPT_WONT;
-       sprintf(nfrontp, fmt, option);
+       hisopts[option] = OPT_NO;
+       (void) sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (doopt) - 2;
 }
 
        nfrontp += sizeof (doopt) - 2;
 }
 
@@ -923,11 +970,11 @@ dooption(option)
                break;
        }
        if (fmt == will) {
                break;
        }
        if (fmt == will) {
-           myopts[option] = OPT_DO;
+           myopts[option] = OPT_YES;
        } else {
        } else {
-           myopts[option] = OPT_DONT;
+           myopts[option] = OPT_NO;
        }
        }
-       sprintf(nfrontp, fmt, option);
+       (void) sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (doopt) - 2;
 }
 
        nfrontp += sizeof (doopt) - 2;
 }
 
@@ -939,19 +986,21 @@ int option;
 
     switch (option) {
     case TELOPT_ECHO:          /* we should stop echoing */
 
     switch (option) {
     case TELOPT_ECHO:          /* we should stop echoing */
-       mode(0, ECHO|CRMOD);
+       mode(0, ECHO);
        fmt = wont;
        break;
        fmt = wont;
        break;
+
     default:
        fmt = wont;
        break;
     }
     default:
        fmt = wont;
        break;
     }
+
     if (fmt = wont) {
     if (fmt = wont) {
-       myopts[option] = OPT_DONT;
+       myopts[option] = OPT_NO;
     } else {
     } else {
-       myopts[option] = OPT_DO;
+       myopts[option] = OPT_YES;
     }
     }
-    sprintf(nfrontp, fmt, option);
+    (void) sprintf(nfrontp, fmt, option);
     nfrontp += sizeof (wont) - 2;
 }
 
     nfrontp += sizeof (wont) - 2;
 }
 
@@ -963,12 +1012,38 @@ int option;
  *
  *     Currently we recognize:
  *
  *
  *     Currently we recognize:
  *
- *     (nothing - we only do terminal type at start-up time)
+ *     Terminal type is
  */
 
 suboption()
 {
  */
 
 suboption()
 {
-    switch (subbuffer[0]&0xff) {
+    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:
        ;
     }
     default:
        ;
     }
@@ -1190,67 +1265,114 @@ netflush()
 
 cleanup()
 {
 
 cleanup()
 {
-
-       rmut();
-       vhangup();      /* XXX */
+       char *p;
+
+       p = line + sizeof("/dev/") - 1;
+       if (logout(p))
+               logwtmp(p, "", "");
+       (void)chmod(line, 0666);
+       (void)chown(line, 0, 0);
+       *p = 'p';
+       (void)chmod(line, 0666);
+       (void)chown(line, 0, 0);
        shutdown(net, 2);
        exit(1);
 }
 
        shutdown(net, 2);
        exit(1);
 }
 
-#include <utmp.h>
+char   editedhost[32];
 
 
-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()
+edithost(pat, host)
+       register char *pat;
+       register char *host;
 {
 {
-       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++;
-                       }
+       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;
+
                }
                }
-               close(f);
+               if (res == &editedhost[sizeof editedhost - 1]) {
+                       *res = '\0';
+                       return;
+               }
+               pat++;
        }
        }
-       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);
+       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;
+}
+
+putf(cp, where)
+register char *cp;
+char *where;
+{
+       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++;
        }
        }
-       chmod(line, 0666);
-       chown(line, 0, 0);
-       line[strlen("/dev/")] = 'p';
-       chmod(line, 0666);
-       chown(line, 0, 0);
 }
 }