BSD 4_4_Lite2 release
[unix-history] / usr / src / libexec / telnetd / state.c
index 986e383..4ee8bea 100644 (file)
@@ -1,46 +1,71 @@
 /*
 /*
- * Copyright (c) 1989 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1989, 1993
+ *     The 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)state.c    5.1 (Berkeley) %G%";
+static char sccsid[] = "@(#)state.c    8.5 (Berkeley) 5/30/95";
 #endif /* not lint */
 
 #include "telnetd.h"
 #endif /* not lint */
 
 #include "telnetd.h"
+#if    defined(AUTHENTICATION)
+#include <libtelnet/auth.h>
+#endif
 
 
-char   doopt[] = { IAC, DO, '%', 'c', 0 };
-char   dont[] = { IAC, DONT, '%', 'c', 0 };
-char   will[] = { IAC, WILL, '%', 'c', 0 };
-char   wont[] = { IAC, WONT, '%', 'c', 0 };
+unsigned char  doopt[] = { IAC, DO, '%', 'c', 0 };
+unsigned char  dont[] = { IAC, DONT, '%', 'c', 0 };
+unsigned char  will[] = { IAC, WILL, '%', 'c', 0 };
+unsigned char  wont[] = { IAC, WONT, '%', 'c', 0 };
 int    not42 = 1;
 
 /*
  * Buffer for sub-options, and macros
  * for suboptions buffer manipulations
  */
 int    not42 = 1;
 
 /*
  * Buffer for sub-options, and macros
  * for suboptions buffer manipulations
  */
-char   subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
+unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer;
 
 
-#define        SB_CLEAR()      subpointer = subbuffer;
+#define        SB_CLEAR()      subpointer = subbuffer
 #define        SB_TERM()       { subend = subpointer; SB_CLEAR(); }
 #define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
                                *subpointer++ = (c); \
                        }
 #define        SB_GET()        ((*subpointer++)&0xff)
 #define        SB_EOF()        (subpointer >= subend)
 #define        SB_TERM()       { subend = subpointer; SB_CLEAR(); }
 #define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
                                *subpointer++ = (c); \
                        }
 #define        SB_GET()        ((*subpointer++)&0xff)
 #define        SB_EOF()        (subpointer >= subend)
+#define        SB_LEN()        (subend - subpointer)
 
 
+#ifdef ENV_HACK
+unsigned char *subsave;
+#define SB_SAVE()      subsave = subpointer;
+#define        SB_RESTORE()    subpointer = subsave;
+#endif
 
 
 /*
 
 
 /*
@@ -56,11 +81,12 @@ char        subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
 #define        TS_DO           7       /* do " */
 #define        TS_DONT         8       /* dont " */
 
 #define        TS_DO           7       /* do " */
 #define        TS_DONT         8       /* dont " */
 
+       void
 telrcv()
 {
        register int c;
        static int state = TS_DATA;
 telrcv()
 {
        register int c;
        static int state = TS_DATA;
-#ifdef CRAY2
+#if    defined(CRAY2) && defined(UNICOS5)
        char *opfrontp = pfrontp;
 #endif
 
        char *opfrontp = pfrontp;
 #endif
 
@@ -68,6 +94,10 @@ telrcv()
                if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
                        break;
                c = *netip++ & 0377, ncc--;
                if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
                        break;
                c = *netip++ & 0377, ncc--;
+#ifdef ENCRYPTION
+               if (decrypt_input)
+                       c = (*decrypt_input)(c);
+#endif /* ENCRYPTION */
                switch (state) {
 
                case TS_CR:
                switch (state) {
 
                case TS_CR:
@@ -94,15 +124,28 @@ telrcv()
                         * unix way of saying that (\r is only good
                         * if CRMOD is set, which it normally is).
                         */
                         * 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)) {
+                       if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
+                               int nc = *netip;
+#ifdef ENCRYPTION
+                               if (decrypt_input)
+                                       nc = (*decrypt_input)(nc & 0xff);
+#endif /* ENCRYPTION */
+#ifdef LINEMODE
                                /*
                                 * If we are operating in linemode,
                                 * convert to local end-of-line.
                                 */
                                /*
                                 * If we are operating in linemode,
                                 * convert to local end-of-line.
                                 */
-                               if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
+                               if (linemode && (ncc > 0) && (('\n' == nc) ||
+                                        ((0 == nc) && tty_iscrnl())) ) {
                                        netip++; ncc--;
                                        c = '\n';
                                        netip++; ncc--;
                                        c = '\n';
-                               } else {
+                               } else
+#endif
+                               {
+#ifdef ENCRYPTION
+                                       if (decrypt_input)
+                                               (void)(*decrypt_input)(-1);
+#endif /* ENCRYPTION */
                                        state = TS_CR;
                                }
                        }
                                        state = TS_CR;
                                }
                        }
@@ -118,10 +161,14 @@ gotiac:                   switch (c) {
                         * interrupt char; depending on the tty mode.
                         */
                        case IP:
                         * interrupt char; depending on the tty mode.
                         */
                        case IP:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
                                interrupt();
                                break;
 
                        case BREAK:
                                interrupt();
                                break;
 
                        case BREAK:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
                                sendbrk();
                                break;
 
                                sendbrk();
                                break;
 
@@ -129,8 +176,9 @@ gotiac:                     switch (c) {
                         * Are You There?
                         */
                        case AYT:
                         * Are You There?
                         */
                        case AYT:
-                               (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
-                               nfrontp += 9;
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               recv_ayt();
                                break;
 
                        /*
                                break;
 
                        /*
@@ -138,18 +186,23 @@ gotiac:                   switch (c) {
                         */
                        case AO:
                            {
                         */
                        case AO:
                            {
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
                                ptyflush();     /* half-hearted */
                                init_termbuf();
 
                                if (slctab[SLC_AO].sptr &&
                                ptyflush();     /* half-hearted */
                                init_termbuf();
 
                                if (slctab[SLC_AO].sptr &&
-                                   *slctab[SLC_AO].sptr != '\377') {
-                                       *pfrontp++ = *slctab[SLC_AO].sptr;
+                                   *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
+                                   *pfrontp++ =
+                                       (unsigned char)*slctab[SLC_AO].sptr;
                                }
 
                                netclear();     /* clear buffer back */
                                *nfrontp++ = IAC;
                                *nfrontp++ = DM;
                                neturg = nfrontp-1; /* off by one XXX */
                                }
 
                                netclear();     /* clear buffer back */
                                *nfrontp++ = IAC;
                                *nfrontp++ = DM;
                                neturg = nfrontp-1; /* off by one XXX */
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: send IAC", DM));
                                break;
                            }
 
                                break;
                            }
 
@@ -160,14 +213,18 @@ gotiac:                   switch (c) {
                        case EC:
                        case EL:
                            {
                        case EC:
                        case EL:
                            {
-                               unsigned char ch;
+                               cc_t ch;
 
 
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
                                ptyflush();     /* half-hearted */
                                init_termbuf();
                                ptyflush();     /* half-hearted */
                                init_termbuf();
-                               ch = (c == EC) ? *slctab[SLC_EC].sptr :
-                                                *slctab[SLC_EL].sptr;
-                               if (ch != '\377')
-                                       *pfrontp++ = ch;
+                               if (c == EC)
+                                       ch = *slctab[SLC_EC].sptr;
+                               else
+                                       ch = *slctab[SLC_EL].sptr;
+                               if (ch != (cc_t)(_POSIX_VDISABLE))
+                                       *pfrontp++ = (unsigned char)ch;
                                break;
                            }
 
                                break;
                            }
 
@@ -175,6 +232,8 @@ gotiac:                     switch (c) {
                         * Check for urgent data...
                         */
                        case DM:
                         * Check for urgent data...
                         */
                        case DM:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
                                SYNCHing = stilloob(net);
                                settimer(gotDM);
                                break;
                                SYNCHing = stilloob(net);
                                settimer(gotDM);
                                break;
@@ -204,7 +263,7 @@ gotiac:                     switch (c) {
                                state = TS_DONT;
                                continue;
                        case EOR:
                                state = TS_DONT;
                                continue;
                        case EOR:
-                               if (hisopts[TELOPT_EOR])
+                               if (his_state_is_will(TELOPT_EOR))
                                        doeof();
                                break;
 
                                        doeof();
                                break;
 
@@ -250,6 +309,12 @@ gotiac:                    switch (c) {
                                         * then treat remaining stream as
                                         * another command sequence.
                                         */
                                         * then treat remaining stream as
                                         * another command sequence.
                                         */
+
+                                       /* for DIAGNOSTICS */
+                                       SB_ACCUM(IAC);
+                                       SB_ACCUM(c);
+                                       subpointer -= 2;
+
                                        SB_TERM();
                                        suboption();
                                        state = TS_IAC;
                                        SB_TERM();
                                        suboption();
                                        state = TS_IAC;
@@ -258,6 +323,11 @@ gotiac:                    switch (c) {
                                SB_ACCUM(c);
                                state = TS_SB;
                        } else {
                                SB_ACCUM(c);
                                state = TS_SB;
                        } else {
+                               /* for DIAGNOSTICS */
+                               SB_ACCUM(IAC);
+                               SB_ACCUM(SE);
+                               subpointer -= 2;
+
                                SB_TERM();
                                suboption();    /* handle sub-option */
                                state = TS_DATA;
                                SB_TERM();
                                suboption();    /* handle sub-option */
                                state = TS_DATA;
@@ -265,22 +335,22 @@ gotiac:                   switch (c) {
                        break;
 
                case TS_WILL:
                        break;
 
                case TS_WILL:
-                       willoption(c, 0);
+                       willoption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_WONT:
                        state = TS_DATA;
                        continue;
 
                case TS_WONT:
-                       wontoption(c, 0);
+                       wontoption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DO:
                        state = TS_DATA;
                        continue;
 
                case TS_DO:
-                       dooption(c, 0);
+                       dooption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DONT:
                        state = TS_DATA;
                        continue;
 
                case TS_DONT:
-                       dontoption(c, 0);
+                       dontoption(c);
                        state = TS_DATA;
                        continue;
 
                        state = TS_DATA;
                        continue;
 
@@ -290,13 +360,13 @@ gotiac:                   switch (c) {
                        exit(1);
                }
        }
                        exit(1);
                }
        }
-#if    CRAY2
+#if    defined(CRAY2) && defined(UNICOS5)
        if (!linemode) {
                char    xptyobuf[BUFSIZ+NETSLOP];
                char    xbuf2[BUFSIZ];
                register char *cp;
                int n = pfrontp - opfrontp, oc;
        if (!linemode) {
                char    xptyobuf[BUFSIZ+NETSLOP];
                char    xbuf2[BUFSIZ];
                register char *cp;
                int n = pfrontp - opfrontp, oc;
-               bcopy(opfrontp, xptyobuf, n);
+               memmove(xptyobuf, opfrontp, n);
                pfrontp = opfrontp;
                pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
                                        xbuf2, &oc, BUFSIZ);
                pfrontp = opfrontp;
                pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
                                        xbuf2, &oc, BUFSIZ);
@@ -304,20 +374,12 @@ gotiac:                   switch (c) {
                        if ((*nfrontp++ = *cp++) == IAC)
                                *nfrontp++ = IAC;
        }
                        if ((*nfrontp++ = *cp++) == IAC)
                                *nfrontp++ = IAC;
        }
-#endif /* CRAY2 */
+#endif /* defined(CRAY2) && defined(UNICOS5) */
 }  /* end of telrcv */
 
 /*
  * The will/wont/do/dont state machines are based on Dave Borman's
 }  /* end of telrcv */
 
 /*
  * The will/wont/do/dont state machines are based on Dave Borman's
- * Telnet option processing state machine.  We keep track of the full
- * state of the option negotiation with the following state variables
- *     myopts, hisopts - The last fully negotiated state for each
- *                     side of the connection.
- *     mywants, hiswants - The state we wish to be in after a completed
- *                     negotiation.  (hiswants is slightly misleading,
- *                     this is more precisely the state I want him to
- *                     be in.
- *     resp - We count the number of requests we have sent out.
+ * Telnet option processing state machine.
  *
  * These correspond to the following states:
  *     my_state = the last negotiated state
  *
  * These correspond to the following states:
  *     my_state = the last negotiated state
@@ -326,7 +388,7 @@ gotiac:                     switch (c) {
  * All state defaults are negative, and resp defaults to 0.
  *
  * When initiating a request to change state to new_state:
  * All state defaults are negative, and resp defaults to 0.
  *
  * When initiating a request to change state to new_state:
- * 
+ *
  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
  *     do nothing;
  * } else {
  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
  *     do nothing;
  * } else {
@@ -370,20 +432,62 @@ gotiac:                   switch (c) {
  * peer probably should be buffering until this option state negotiation
  * is complete.
  *
  * peer probably should be buffering until this option state negotiation
  * is complete.
  *
- * In processing options, request signifies whether this is a request
- * to send or a response.  request is true if this is a request to 
- * send generated locally.
  */
  */
-willoption(option, request)
-       int option, request;
+       void
+send_do(option, init)
+       int option, init;
+{
+       if (init) {
+               if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
+                   his_want_state_is_will(option))
+                       return;
+               /*
+                * Special case for TELOPT_TM:  We send a DO, but pretend
+                * that we sent a DONT, so that we can send more DOs if
+                * we want to.
+                */
+               if (option == TELOPT_TM)
+                       set_his_want_state_wont(option);
+               else
+                       set_his_want_state_will(option);
+               do_dont_resp[option]++;
+       }
+       (void) sprintf(nfrontp, (char *)doopt, option);
+       nfrontp += sizeof (dont) - 2;
+
+       DIAG(TD_OPTIONS, printoption("td: send do", option));
+}
+
+#ifdef AUTHENTICATION
+extern void auth_request();
+#endif
+#ifdef LINEMODE
+extern void doclientstat();
+#endif
+#ifdef ENCRYPTION
+extern void encrypt_send_support();
+#endif /* ENCRYPTION */
+
+       void
+willoption(option)
+       int option;
 {
        int changeok = 0;
 {
        int changeok = 0;
-       char *fmt = (char *)0;
+       void (*func)() = 0;
+
+       /*
+        * process input from peer.
+        */
 
 
-    /*
-     * process input from peer.
-     */
-    if (request == 0) {
+       DIAG(TD_OPTIONS, printoption("td: recv will", option));
+
+       if (do_dont_resp[option]) {
+               do_dont_resp[option]--;
+               if (do_dont_resp[option] && his_state_is_will(option))
+                       do_dont_resp[option]--;
+       }
+       if (do_dont_resp[option] == 0) {
+           if (his_want_state_is_wont(option)) {
                switch (option) {
 
                case TELOPT_BINARY:
                switch (option) {
 
                case TELOPT_BINARY:
@@ -394,33 +498,23 @@ willoption(option, request)
                        break;
 
                case TELOPT_ECHO:
                        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!
+                        * See comments below for more info.
                         */
                         */
-                       if (myopts[TELOPT_ECHO] == OPT_YES) {
-                               dooption(TELOPT_ECHO, 1);
-                       }
-                       /*
-                        * Fool the state machine into sending a don't.
-                        * This also allows the initial echo sending code to
-                        * break out of the loop that it is
-                        * in.  (Look in telnet())
-                        */
-                       hiswants[TELOPT_ECHO] = OPT_NO;
+                       not42 = 0;      /* looks like a 4.2 system */
                        break;
 
                case TELOPT_TM:
 #if    defined(LINEMODE) && defined(KLUDGELINEMODE)
                        /*
                        break;
 
                case TELOPT_TM:
 #if    defined(LINEMODE) && defined(KLUDGELINEMODE)
                        /*
-                        * This telnetd implementation does not really support
-                        * timing marks, it just uses them to support the kludge
-                        * linemode stuff.  If we receive a will or wont TM in
-                        * response to our do TM request that may have been sent
-                        * to determine kludge linemode support, process it,
-                        * otherwise TM should get a negative response back.
+                        * This telnetd implementation does not really
+                        * support timing marks, it just uses them to
+                        * support the kludge linemode stuff.  If we
+                        * receive a will or wont TM in response to our
+                        * do TM request that may have been sent to
+                        * determine kludge linemode support, process
+                        * it, otherwise TM should get a negative
+                        * response back.
                         */
                        /*
                         * Handle the linemode kludge stuff.
                         */
                        /*
                         * Handle the linemode kludge stuff.
@@ -435,26 +529,22 @@ willoption(option, request)
                        if (lmodetype < KLUDGE_LINEMODE) {
                                lmodetype = KLUDGE_LINEMODE;
                                clientstat(TELOPT_LINEMODE, WILL, 0);
                        if (lmodetype < KLUDGE_LINEMODE) {
                                lmodetype = KLUDGE_LINEMODE;
                                clientstat(TELOPT_LINEMODE, WILL, 0);
-                               dontoption(TELOPT_SGA, 0);
+                               send_wont(TELOPT_SGA, 1);
+                       } else if (lmodetype == NO_AUTOKLUDGE) {
+                               lmodetype = KLUDGE_OK;
                        }
 #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
                        /*
                        }
 #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
                        /*
-                        * cheat the state machine so that it
-                        * looks like we never sent the TM at
-                        * all.  The bad part of this is that
-                        * if the client sends a will TM on his
-                        * own to turn on linemode, then he
-                        * won't get a response.
+                        * We never respond to a WILL TM, and
+                        * we leave the state WONT.
                         */
                         */
-                       hiswants[TELOPT_TM] = OPT_NO;
-                       resp[TELOPT_TM]--;
                        return;
 
                case TELOPT_LFLOW:
                        /*
                        return;
 
                case TELOPT_LFLOW:
                        /*
-                        * If we are going to support flow control option,
-                        * then don't worry peer that we can't change the
-                        * flow control characters.
+                        * If we are going to support flow control
+                        * option, then don't worry peer that we can't
+                        * change the flow control characters.
                         */
                        slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
                        slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
                         */
                        slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
                        slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
@@ -464,52 +554,79 @@ willoption(option, request)
                case TELOPT_SGA:
                case TELOPT_NAWS:
                case TELOPT_TSPEED:
                case TELOPT_SGA:
                case TELOPT_NAWS:
                case TELOPT_TSPEED:
+               case TELOPT_XDISPLOC:
+               case TELOPT_NEW_ENVIRON:
+               case TELOPT_OLD_ENVIRON:
+                       changeok++;
+                       break;
+
 #ifdef LINEMODE
                case TELOPT_LINEMODE:
 #ifdef LINEMODE
                case TELOPT_LINEMODE:
-#endif LINEMODE
+# ifdef        KLUDGELINEMODE
+                       /*
+                        * Note client's desire to use linemode.
+                        */
+                       lmodetype = REAL_LINEMODE;
+# endif        /* KLUDGELINEMODE */
+                       func = doclientstat;
                        changeok++;
                        break;
                        changeok++;
                        break;
+#endif /* LINEMODE */
 
 
-               default:
+#ifdef AUTHENTICATION
+               case TELOPT_AUTHENTICATION:
+                       func = auth_request;
+                       changeok++;
                        break;
                        break;
-               }
+#endif
 
 
-       }
+#ifdef ENCRYPTION
+               case TELOPT_ENCRYPT:
+                       func = encrypt_send_support;
+                       changeok++;
+                       break;
+#endif /* ENCRYPTION */
 
 
-       if (request) {
-               if (!((resp[option] == 0 && hisopts[option] == OPT_YES) ||
-                   hiswants[option] == OPT_YES)) {
-                       hiswants[option] = OPT_YES;
-                       fmt = doopt;
-                       resp[option]++;
-               }
-       } else {
-               if (resp[option]) {
-                       resp[option]--;
-                       if (resp[option] && hisopts[option] == OPT_YES)
-                               resp[option]--;
+               default:
+                       break;
                }
                }
-               if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) {
-                       if (changeok)
-                               hiswants[option] = OPT_YES;
-                       else
-                               resp[option]++;
-                       fmt = (hiswants[option] ? doopt : dont);
+               if (changeok) {
+                       set_his_want_state_will(option);
+                       send_do(option, 0);
+               } else {
+                       do_dont_resp[option]++;
+                       send_dont(option, 0);
                }
                }
-               hisopts[option] = OPT_YES;
-       }
-
-       if (fmt) {
-               (void) sprintf(nfrontp, fmt, option);
-               nfrontp += sizeof (dont) - 2;
-       }
-
-       /*
-        * Handle other processing that should occur after we have
-        * responded to client input.
-        */
-       if (!request) {
+           } else {
+               /*
+                * Option processing that should happen when
+                * we receive conformation of a change in
+                * state that we had requested.
+                */
                switch (option) {
                switch (option) {
+               case TELOPT_ECHO:
+                       not42 = 0;      /* looks like a 4.2 system */
+                       /*
+                        * Egads, he responded "WILL ECHO".  Turn
+                        * it off right now!
+                        */
+                       send_dont(option, 1);
+                       /*
+                        * "WILL ECHO".  Kludge upon kludge!
+                        * A 4.2 client is now echoing user input at
+                        * the tty.  This is probably undesireable and
+                        * it should be stopped.  The client will
+                        * respond WONT TM to the DO TM that we send to
+                        * check for kludge linemode.  When the WONT TM
+                        * arrives, linemode will be turned off and a
+                        * change propogated to the pty.  This change
+                        * will cause us to process the new pty state
+                        * in localstat(), which will notice that
+                        * linemode is off and send a WILL ECHO
+                        * so that we are properly in character mode and
+                        * all is well.
+                        */
+                       break;
 #ifdef LINEMODE
                case TELOPT_LINEMODE:
 # ifdef        KLUDGELINEMODE
 #ifdef LINEMODE
                case TELOPT_LINEMODE:
 # ifdef        KLUDGELINEMODE
@@ -518,29 +635,70 @@ willoption(option, request)
                         */
                        lmodetype = REAL_LINEMODE;
 # endif        /* KLUDGELINEMODE */
                         */
                        lmodetype = REAL_LINEMODE;
 # endif        /* KLUDGELINEMODE */
-                       clientstat(TELOPT_LINEMODE, WILL, 0);
+                       func = doclientstat;
                        break;
                        break;
-#endif LINEMODE
-               
-               default:
+#endif /* LINEMODE */
+
+#ifdef AUTHENTICATION
+               case TELOPT_AUTHENTICATION:
+                       func = auth_request;
+                       break;
+#endif
+
+#ifdef ENCRYPTION
+               case TELOPT_ENCRYPT:
+                       func = encrypt_send_support;
+                       break;
+#endif /* ENCRYPTION */
+               case TELOPT_LFLOW:
+                       func = flowstat;
                        break;
                }
                        break;
                }
+           }
        }
        }
-
+       set_his_state_will(option);
+       if (func)
+               (*func)();
 }  /* end of willoption */
 
 }  /* end of willoption */
 
-wontoption(option, request)
-       int option, request;
+       void
+send_dont(option, init)
+       int option, init;
 {
 {
-       char *fmt = (char *)0;
+       if (init) {
+               if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
+                   his_want_state_is_wont(option))
+                       return;
+               set_his_want_state_wont(option);
+               do_dont_resp[option]++;
+       }
+       (void) sprintf(nfrontp, (char *)dont, option);
+       nfrontp += sizeof (doopt) - 2;
+
+       DIAG(TD_OPTIONS, printoption("td: send dont", option));
+}
 
 
+       void
+wontoption(option)
+       int option;
+{
        /*
         * Process client input.
         */
        /*
         * Process client input.
         */
-       if (!request) {
+
+       DIAG(TD_OPTIONS, printoption("td: recv wont", option));
+
+       if (do_dont_resp[option]) {
+               do_dont_resp[option]--;
+               if (do_dont_resp[option] && his_state_is_wont(option))
+                       do_dont_resp[option]--;
+       }
+       if (do_dont_resp[option] == 0) {
+           if (his_want_state_is_will(option)) {
+               /* it is always ok to change to negative state */
                switch (option) {
                case TELOPT_ECHO:
                switch (option) {
                case TELOPT_ECHO:
-                       not42 = 1;      /* doesn't seem to be a 4.2 system */
+                       not42 = 1; /* doesn't seem to be a 4.2 system */
                        break;
 
                case TELOPT_BINARY:
                        break;
 
                case TELOPT_BINARY:
@@ -556,37 +714,28 @@ wontoption(option, request)
                         * If real linemode is supported, then client is
                         * asking to turn linemode off.
                         */
                         * If real linemode is supported, then client is
                         * asking to turn linemode off.
                         */
-                       if (lmodetype == REAL_LINEMODE)
+                       if (lmodetype != REAL_LINEMODE)
+                               break;
 # endif        /* KLUDGELINEMODE */
 # endif        /* KLUDGELINEMODE */
-                               clientstat(TELOPT_LINEMODE, WONT, 0);
+                       clientstat(TELOPT_LINEMODE, WONT, 0);
                        break;
                        break;
-#endif LINEMODE
+#endif /* LINEMODE */
 
                case TELOPT_TM:
 
                case TELOPT_TM:
-#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
-                       if (lmodetype < REAL_LINEMODE) {
-                               lmodetype = NO_LINEMODE;
-                               clientstat(TELOPT_LINEMODE, WONT, 0);
-                               dooption(TELOPT_SGA, 0);
-                       }
-#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
                        /*
                        /*
-                        * If we get a WONT TM, and had sent a DO TM, don't
-                        * respond with a DONT TM, just leave it as is.
-                        * Short circut the state machine to achive this.
-                        * The bad part of this is that if the client sends
-                        * a WONT TM on his own to turn off linemode, then he
-                        * won't get a response.
+                        * If we get a WONT TM, and had sent a DO TM,
+                        * don't respond with a DONT TM, just leave it
+                        * as is.  Short circut the state machine to
+                        * achive this.
                         */
                         */
-                       hiswants[TELOPT_TM] = OPT_NO;
-                       resp[TELOPT_TM]--;
+                       set_his_want_state_wont(TELOPT_TM);
                        return;
 
                case TELOPT_LFLOW:
                        /*
                        return;
 
                case TELOPT_LFLOW:
                        /*
-                        * If we are not going to support flow control option,
-                        * then let peer know that we can't change the
-                        * flow control characters.
+                        * If we are not going to support flow control
+                        * option, then let peer know that we can't
+                        * change the flow control characters.
                         */
                        slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
                        slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
                         */
                        slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
                        slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
@@ -594,61 +743,133 @@ wontoption(option, request)
                        slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
                        break;
 
                        slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
                        break;
 
-               default:
+#if    defined(AUTHENTICATION)
+               case TELOPT_AUTHENTICATION:
+                       auth_finished(0, AUTH_REJECT);
+                       break;
+#endif
+
+               /*
+                * For options that we might spin waiting for
+                * sub-negotiation, if the client turns off the
+                * option rather than responding to the request,
+                * we have to treat it here as if we got a response
+                * to the sub-negotiation, (by updating the timers)
+                * so that we'll break out of the loop.
+                */
+               case TELOPT_TTYPE:
+                       settimer(ttypesubopt);
                        break;
                        break;
-               }
-       }
 
 
+               case TELOPT_TSPEED:
+                       settimer(tspeedsubopt);
+                       break;
 
 
-       if (request) {
-               if (!((resp[option] == 0 && hisopts[option] == OPT_NO) ||
-                       hiswants[option] == OPT_NO)) {
-                       hiswants[option] = OPT_NO;
-                       fmt = dont;
-                       resp[option]++;
-               }
-       } else {
-               if (resp[option]) {
-                       resp[option]--;
-                       if (resp[option] && hisopts[option] == OPT_NO)
-                               resp[option]--;
+               case TELOPT_XDISPLOC:
+                       settimer(xdisplocsubopt);
+                       break;
+
+               case TELOPT_OLD_ENVIRON:
+                       settimer(oenvironsubopt);
+                       break;
+
+               case TELOPT_NEW_ENVIRON:
+                       settimer(environsubopt);
+                       break;
+
+               default:
+                       break;
                }
                }
-               if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) {
-                       /* it is always ok to change to negative state */
-                       hiswants[option] = OPT_NO;
-                       fmt = dont;
+               set_his_want_state_wont(option);
+               if (his_state_is_will(option))
+                       send_dont(option, 0);
+           } else {
+               switch (option) {
+               case TELOPT_TM:
+#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
+                       if (lmodetype < NO_AUTOKLUDGE) {
+                               lmodetype = NO_LINEMODE;
+                               clientstat(TELOPT_LINEMODE, WONT, 0);
+                               send_will(TELOPT_SGA, 1);
+                               send_will(TELOPT_ECHO, 1);
+                       }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       break;
+
+#if    defined(AUTHENTICATION)
+               case TELOPT_AUTHENTICATION:
+                       auth_finished(0, AUTH_REJECT);
+                       break;
+#endif
+               default:
+                       break;
                }
                }
-               hisopts[option] = OPT_NO;
+           }
        }
        }
+       set_his_state_wont(option);
+
+}  /* end of wontoption */
 
 
-       if (fmt) {
-               (void) sprintf(nfrontp, fmt, option);
-               nfrontp += sizeof (doopt) - 2;
+       void
+send_will(option, init)
+       int option, init;
+{
+       if (init) {
+               if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
+                   my_want_state_is_will(option))
+                       return;
+               set_my_want_state_will(option);
+               will_wont_resp[option]++;
        }
        }
+       (void) sprintf(nfrontp, (char *)will, option);
+       nfrontp += sizeof (doopt) - 2;
 
 
-}  /* end of wontoption */
+       DIAG(TD_OPTIONS, printoption("td: send will", option));
+}
 
 
-dooption(option, request)
-       int option, request;
+#if    !defined(LINEMODE) || !defined(KLUDGELINEMODE)
+/*
+ * When we get a DONT SGA, we will try once to turn it
+ * back on.  If the other side responds DONT SGA, we
+ * leave it at that.  This is so that when we talk to
+ * clients that understand KLUDGELINEMODE but not LINEMODE,
+ * we'll keep them in char-at-a-time mode.
+ */
+int turn_on_sga = 0;
+#endif
+
+       void
+dooption(option)
+       int option;
 {
        int changeok = 0;
 {
        int changeok = 0;
-       char *fmt = (char *)0;
 
        /*
         * Process client input.
         */
 
        /*
         * Process client input.
         */
-       if (!request) {
+
+       DIAG(TD_OPTIONS, printoption("td: recv do", option));
+
+       if (will_wont_resp[option]) {
+               will_wont_resp[option]--;
+               if (will_wont_resp[option] && my_state_is_will(option))
+                       will_wont_resp[option]--;
+       }
+       if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
                switch (option) {
                case TELOPT_ECHO:
 #ifdef LINEMODE
                switch (option) {
                case TELOPT_ECHO:
 #ifdef LINEMODE
-                       if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
+# ifdef        KLUDGELINEMODE
+                       if (lmodetype == NO_LINEMODE)
+# else
+                       if (his_state_is_wont(TELOPT_LINEMODE))
+# endif
 #endif
 #endif
+                       {
                                init_termbuf();
                                tty_setecho(1);
                                set_termbuf();
                                init_termbuf();
                                tty_setecho(1);
                                set_termbuf();
-#ifdef LINEMODE
                        }
                        }
-#endif
                        changeok++;
                        break;
 
                        changeok++;
                        break;
 
@@ -662,24 +883,29 @@ dooption(option, request)
                case TELOPT_SGA:
 #if    defined(LINEMODE) && defined(KLUDGELINEMODE)
                        /*
                case TELOPT_SGA:
 #if    defined(LINEMODE) && defined(KLUDGELINEMODE)
                        /*
-                        * If kludge linemode is in use, then we must process
-                        * an incoming do SGA for linemode purposes.
+                        * If kludge linemode is in use, then we must
+                        * process an incoming do SGA for linemode
+                        * purposes.
                         */
                        if (lmodetype == KLUDGE_LINEMODE) {
                                /*
                         */
                        if (lmodetype == KLUDGE_LINEMODE) {
                                /*
-                                * Receipt of "do SGA" in kludge linemode
-                                * is the peer asking us to turn off linemode.
-                                * Make note of the request.
+                                * Receipt of "do SGA" in kludge
+                                * linemode is the peer asking us to
+                                * turn off linemode.  Make note of
+                                * the request.
                                 */
                                clientstat(TELOPT_LINEMODE, WONT, 0);
                                /*
                                 */
                                clientstat(TELOPT_LINEMODE, WONT, 0);
                                /*
-                                * If linemode did not get turned off then
-                                * don't tell peer that we did.  Breaking
-                                * here forces a wont SGA to be returned.
+                                * If linemode did not get turned off
+                                * then don't tell peer that we did.
+                                * Breaking here forces a wont SGA to
+                                * be returned.
                                 */
                                if (linemode)
                                        break;
                        }
                                 */
                                if (linemode)
                                        break;
                        }
+#else
+                       turn_on_sga = 0;
 #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
                        changeok++;
                        break;
 #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
                        changeok++;
                        break;
@@ -689,56 +915,94 @@ dooption(option, request)
                        break;
 
                case TELOPT_TM:
                        break;
 
                case TELOPT_TM:
+                       /*
+                        * Special case for TM.  We send a WILL, but
+                        * pretend we sent a WONT.
+                        */
+                       send_will(option, 0);
+                       set_my_want_state_wont(option);
+                       set_my_state_wont(option);
+                       return;
+
+               case TELOPT_LOGOUT:
+                       /*
+                        * When we get a LOGOUT option, respond
+                        * with a WILL LOGOUT, make sure that
+                        * it gets written out to the network,
+                        * and then just go away...
+                        */
+                       set_my_want_state_will(TELOPT_LOGOUT);
+                       send_will(TELOPT_LOGOUT, 0);
+                       set_my_state_will(TELOPT_LOGOUT);
+                       (void)netflush();
+                       cleanup(0);
+                       /* NOT REACHED */
+                       break;
+
+#ifdef ENCRYPTION
+               case TELOPT_ENCRYPT:
+                       changeok++;
+                       break;
+#endif /* ENCRYPTION */
                case TELOPT_LINEMODE:
                case TELOPT_TTYPE:
                case TELOPT_NAWS:
                case TELOPT_TSPEED:
                case TELOPT_LFLOW:
                case TELOPT_LINEMODE:
                case TELOPT_TTYPE:
                case TELOPT_NAWS:
                case TELOPT_TSPEED:
                case TELOPT_LFLOW:
+               case TELOPT_XDISPLOC:
+#ifdef TELOPT_ENVIRON
+               case TELOPT_NEW_ENVIRON:
+#endif
+               case TELOPT_OLD_ENVIRON:
                default:
                        break;
                }
                default:
                        break;
                }
-       }
-
-       if (request) {
-               if (!((resp[option] == 0 && myopts[option] == OPT_YES) ||
-                   mywants[option] == OPT_YES)) {
-                       mywants[option] = OPT_YES;
-                       fmt = will;
-                       resp[option]++;
-               }
-       } else {
-               if (resp[option]) {
-                       resp[option]--;
-                       if (resp[option] && myopts[option] == OPT_YES)
-                               resp[option]--;
-               }
-               if ((resp[option] == 0) && (mywants[option] != OPT_YES)) {
-                       if (changeok)
-                               mywants[option] = OPT_YES;
-                       else
-                               resp[option]++;
-                       fmt = (mywants[option] ? will : wont);
+               if (changeok) {
+                       set_my_want_state_will(option);
+                       send_will(option, 0);
+               } else {
+                       will_wont_resp[option]++;
+                       send_wont(option, 0);
                }
                }
-               myopts[option] = OPT_YES;
-       }
-
-       if (fmt) {
-               (void) sprintf(nfrontp, fmt, option);
-               nfrontp += sizeof (doopt) - 2;
        }
        }
+       set_my_state_will(option);
 
 }  /* end of dooption */
 
 
 }  /* end of dooption */
 
-
-dontoption(option, request)
-       int option, request;
+       void
+send_wont(option, init)
+       int option, init;
 {
 {
-       char *fmt = (char *)0;
+       if (init) {
+               if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
+                   my_want_state_is_wont(option))
+                       return;
+               set_my_want_state_wont(option);
+               will_wont_resp[option]++;
+       }
+       (void) sprintf(nfrontp, (char *)wont, option);
+       nfrontp += sizeof (wont) - 2;
+
+       DIAG(TD_OPTIONS, printoption("td: send wont", option));
+}
 
 
+       void
+dontoption(option)
+       int option;
+{
        /*
         * Process client input.
         */
        /*
         * Process client input.
         */
-       if (!request) {
+
+
+       DIAG(TD_OPTIONS, printoption("td: recv dont", option));
+
+       if (will_wont_resp[option]) {
+               will_wont_resp[option]--;
+               if (will_wont_resp[option] && my_state_is_wont(option))
+                       will_wont_resp[option]--;
+       }
+       if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
                switch (option) {
                case TELOPT_BINARY:
                        init_termbuf();
                switch (option) {
                case TELOPT_BINARY:
                        init_termbuf();
@@ -746,70 +1010,75 @@ dontoption(option, request)
                        set_termbuf();
                        break;
 
                        set_termbuf();
                        break;
 
-               case TELOPT_ECHO:               /* we should stop echoing */
+               case TELOPT_ECHO:       /* we should stop echoing */
 #ifdef LINEMODE
 #ifdef LINEMODE
-                       if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
+# ifdef        KLUDGELINEMODE
+                       if ((lmodetype != REAL_LINEMODE) &&
+                           (lmodetype != KLUDGE_LINEMODE))
+# else
+                       if (his_state_is_wont(TELOPT_LINEMODE))
+# endif
 #endif
 #endif
+                       {
                                init_termbuf();
                                tty_setecho(0);
                                set_termbuf();
                                init_termbuf();
                                tty_setecho(0);
                                set_termbuf();
-#ifdef LINEMODE
                        }
                        }
-#endif
                        break;
 
                case TELOPT_SGA:
 #if    defined(LINEMODE) && defined(KLUDGELINEMODE)
                        /*
                        break;
 
                case TELOPT_SGA:
 #if    defined(LINEMODE) && defined(KLUDGELINEMODE)
                        /*
-                        * If kludge linemode is in use, then we must process an
-                        * incoming do SGA for linemode purposes.
+                        * If kludge linemode is in use, then we
+                        * must process an incoming do SGA for
+                        * linemode purposes.
                         */
                         */
-                       if (lmodetype == KLUDGE_LINEMODE) {
+                       if ((lmodetype == KLUDGE_LINEMODE) ||
+                           (lmodetype == KLUDGE_OK)) {
                                /*
                                /*
-                                * The client is asking us to turn linemode
-                                * on.
+                                * The client is asking us to turn
+                                * linemode on.
                                 */
                                 */
+                               lmodetype = KLUDGE_LINEMODE;
                                clientstat(TELOPT_LINEMODE, WILL, 0);
                                /*
                                clientstat(TELOPT_LINEMODE, WILL, 0);
                                /*
-                                * If we did not turn line mode on, then what do
-                                * we say?  Will SGA?  This violates design of
-                                * telnet.  Gross.  Very Gross.
+                                * If we did not turn line mode on,
+                                * then what do we say?  Will SGA?
+                                * This violates design of telnet.
+                                * Gross.  Very Gross.
                                 */
                        }
                                 */
                        }
+                       break;
+#else
+                       set_my_want_state_wont(option);
+                       if (my_state_is_will(option))
+                               send_wont(option, 0);
+                       set_my_state_wont(option);
+                       if (turn_on_sga ^= 1)
+                               send_will(option, 1);
+                       return;
 #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
 
                default:
                        break;
                }
 #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
 
                default:
                        break;
                }
-       }
 
 
-       if (request) {
-               if (!((resp[option] == 0 && myopts[option] == OPT_NO) ||
-                   mywants[option] == OPT_NO)) {
-                       mywants[option] = OPT_NO;
-                       fmt = wont;
-                       resp[option]++;
-               }
-       } else {
-               if (resp[option]) {
-                       resp[option]--;
-                       if (resp[option] && myopts[option] == OPT_NO)
-                               resp[option]--;
-               }
-               if ((resp[option] == 0) && (mywants[option] != OPT_NO)) {
-                       mywants[option] = OPT_NO;
-                       fmt = wont;
-               }
-               myopts[option] = OPT_NO;
-       }
-
-       if (fmt) {
-           (void) sprintf(nfrontp, fmt, option);
-           nfrontp += sizeof (wont) - 2;
+               set_my_want_state_wont(option);
+               if (my_state_is_will(option))
+                       send_wont(option, 0);
        }
        }
+       set_my_state_wont(option);
 
 }  /* end of dontoption */
 
 
 }  /* end of dontoption */
 
+#ifdef ENV_HACK
+int env_ovar = -1;
+int env_ovalue = -1;
+#else  /* ENV_HACK */
+# define env_ovar OLD_ENV_VAR
+# define env_ovalue OLD_ENV_VALUE
+#endif /* ENV_HACK */
+
 /*
  * suboption()
  *
 /*
  * suboption()
  *
@@ -823,16 +1092,19 @@ dontoption(option, request)
  *     Window size
  *     Terminal speed
  */
  *     Window size
  *     Terminal speed
  */
+       void
 suboption()
 {
     register int subchar;
 
 suboption()
 {
     register int subchar;
 
+    DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
+
     subchar = SB_GET();
     switch (subchar) {
     case TELOPT_TSPEED: {
        register int xspeed, rspeed;
 
     subchar = SB_GET();
     switch (subchar) {
     case TELOPT_TSPEED: {
        register int xspeed, rspeed;
 
-       if (hisopts[TELOPT_TSPEED] == OPT_NO)   /* Ignore if option disabled */
+       if (his_state_is_wont(TELOPT_TSPEED))   /* Ignore if option disabled */
                break;
 
        settimer(tspeedsubopt);
                break;
 
        settimer(tspeedsubopt);
@@ -840,13 +1112,13 @@ suboption()
        if (SB_EOF() || SB_GET() != TELQUAL_IS)
                return;
 
        if (SB_EOF() || SB_GET() != TELQUAL_IS)
                return;
 
-       xspeed = atoi(subpointer);
+       xspeed = atoi((char *)subpointer);
 
        while (SB_GET() != ',' && !SB_EOF());
        if (SB_EOF())
                return;
 
 
        while (SB_GET() != ',' && !SB_EOF());
        if (SB_EOF())
                return;
 
-       rspeed = atoi(subpointer);
+       rspeed = atoi((char *)subpointer);
        clientstat(TELOPT_TSPEED, xspeed, rspeed);
 
        break;
        clientstat(TELOPT_TSPEED, xspeed, rspeed);
 
        break;
@@ -854,17 +1126,17 @@ suboption()
     }  /* end of case TELOPT_TSPEED */
 
     case TELOPT_TTYPE: {               /* Yaaaay! */
     }  /* end of case TELOPT_TSPEED */
 
     case TELOPT_TTYPE: {               /* Yaaaay! */
-       static char terminalname[5+41] = "TERM=";
+       static char terminalname[41];
 
 
-       if (hisopts[TELOPT_TSPEED] == OPT_NO)   /* Ignore if option disabled */
+       if (his_state_is_wont(TELOPT_TTYPE))    /* Ignore if option disabled */
                break;
        settimer(ttypesubopt);
 
                break;
        settimer(ttypesubopt);
 
-       if (SB_GET() != TELQUAL_IS) {
+       if (SB_EOF() || SB_GET() != TELQUAL_IS) {
            return;             /* ??? XXX but, this is the most robust */
        }
 
            return;             /* ??? XXX but, this is the most robust */
        }
 
-       terminaltype = terminalname+sizeof("TERM=")-1;
+       terminaltype = terminalname;
 
        while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
                                                                    !SB_EOF()) {
 
        while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
                                                                    !SB_EOF()) {
@@ -884,7 +1156,7 @@ suboption()
     case TELOPT_NAWS: {
        register int xwinsize, ywinsize;
 
     case TELOPT_NAWS: {
        register int xwinsize, ywinsize;
 
-       if (hisopts[TELOPT_NAWS] == OPT_NO)     /* Ignore if option disabled */
+       if (his_state_is_wont(TELOPT_NAWS))     /* Ignore if option disabled */
                break;
 
        if (SB_EOF())
                break;
 
        if (SB_EOF())
@@ -909,14 +1181,17 @@ suboption()
     case TELOPT_LINEMODE: {
        register int request;
 
     case TELOPT_LINEMODE: {
        register int request;
 
-       if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */
+       if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */
                break;
        /*
         * Process linemode suboptions.
         */
                break;
        /*
         * Process linemode suboptions.
         */
-       if (SB_EOF()) break;  /* garbage was sent */
-       request = SB_GET();  /* get will/wont */
-       if (SB_EOF()) break;  /* another garbage check */
+       if (SB_EOF())
+           break;              /* garbage was sent */
+       request = SB_GET();     /* get will/wont */
+
+       if (SB_EOF())
+           break;              /* another garbage check */
 
        if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
                /*
 
        if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
                /*
@@ -924,13 +1199,18 @@ suboption()
                 */
                start_slc(1);
                do_opt_slc(subpointer, subend - subpointer);
                 */
                start_slc(1);
                do_opt_slc(subpointer, subend - subpointer);
-               end_slc(0);
-
+               (void) end_slc(0);
+               break;
        } else if (request == LM_MODE) {
        } else if (request == LM_MODE) {
+               if (SB_EOF())
+                   return;
                useeditmode = SB_GET();  /* get mode flag */
                clientstat(LM_MODE, 0, 0);
                useeditmode = SB_GET();  /* get mode flag */
                clientstat(LM_MODE, 0, 0);
+               break;
        }
 
        }
 
+       if (SB_EOF())
+           break;
        switch (SB_GET()) {  /* what suboption? */
        case LM_FORWARDMASK:
                /*
        switch (SB_GET()) {  /* what suboption? */
        case LM_FORWARDMASK:
                /*
@@ -942,16 +1222,18 @@ suboption()
        default:
                break;
        }
        default:
                break;
        }
-
+       break;
     }  /* end of case TELOPT_LINEMODE */
 #endif
     case TELOPT_STATUS: {
        int mode;
 
     }  /* end of case TELOPT_LINEMODE */
 #endif
     case TELOPT_STATUS: {
        int mode;
 
+       if (SB_EOF())
+           break;
        mode = SB_GET();
        switch (mode) {
        case TELQUAL_SEND:
        mode = SB_GET();
        switch (mode) {
        case TELQUAL_SEND:
-           if (myopts[TELOPT_STATUS] == OPT_YES)
+           if (my_state_is_will(TELOPT_STATUS))
                send_status();
            break;
 
                send_status();
            break;
 
@@ -961,7 +1243,269 @@ suboption()
        default:
            break;
        }
        default:
            break;
        }
-    }
+       break;
+    }  /* end of case TELOPT_STATUS */
+
+    case TELOPT_XDISPLOC: {
+       if (SB_EOF() || SB_GET() != TELQUAL_IS)
+               return;
+       settimer(xdisplocsubopt);
+       subpointer[SB_LEN()] = '\0';
+       (void)setenv("DISPLAY", (char *)subpointer, 1);
+       break;
+    }  /* end of case TELOPT_XDISPLOC */
+
+#ifdef TELOPT_NEW_ENVIRON
+    case TELOPT_NEW_ENVIRON:
+#endif
+    case TELOPT_OLD_ENVIRON: {
+       register int c;
+       register char *cp, *varp, *valp;
+
+       if (SB_EOF())
+               return;
+       c = SB_GET();
+       if (c == TELQUAL_IS) {
+               if (subchar == TELOPT_OLD_ENVIRON)
+                       settimer(oenvironsubopt);
+               else
+                       settimer(environsubopt);
+       } else if (c != TELQUAL_INFO) {
+               return;
+       }
+
+#ifdef TELOPT_NEW_ENVIRON
+       if (subchar == TELOPT_NEW_ENVIRON) {
+           while (!SB_EOF()) {
+               c = SB_GET();
+               if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
+                       break;
+           }
+       } else
+#endif
+       {
+#ifdef ENV_HACK
+           /*
+            * We only want to do this if we haven't already decided
+            * whether or not the other side has its VALUE and VAR
+            * reversed.
+            */
+           if (env_ovar < 0) {
+               register int last = -1;         /* invalid value */
+               int empty = 0;
+               int got_var = 0, got_value = 0, got_uservar = 0;
+
+               /*
+                * The other side might have its VALUE and VAR values
+                * reversed.  To be interoperable, we need to determine
+                * which way it is.  If the first recognized character
+                * is a VAR or VALUE, then that will tell us what
+                * type of client it is.  If the fist recognized
+                * character is a USERVAR, then we continue scanning
+                * the suboption looking for two consecutive
+                * VAR or VALUE fields.  We should not get two
+                * consecutive VALUE fields, so finding two
+                * consecutive VALUE or VAR fields will tell us
+                * what the client is.
+                */
+               SB_SAVE();
+               while (!SB_EOF()) {
+                       c = SB_GET();
+                       switch(c) {
+                       case OLD_ENV_VAR:
+                               if (last < 0 || last == OLD_ENV_VAR
+                                   || (empty && (last == OLD_ENV_VALUE)))
+                                       goto env_ovar_ok;
+                               got_var++;
+                               last = OLD_ENV_VAR;
+                               break;
+                       case OLD_ENV_VALUE:
+                               if (last < 0 || last == OLD_ENV_VALUE
+                                   || (empty && (last == OLD_ENV_VAR)))
+                                       goto env_ovar_wrong;
+                               got_value++;
+                               last = OLD_ENV_VALUE;
+                               break;
+                       case ENV_USERVAR:
+                               /* count strings of USERVAR as one */
+                               if (last != ENV_USERVAR)
+                                       got_uservar++;
+                               if (empty) {
+                                       if (last == OLD_ENV_VALUE)
+                                               goto env_ovar_ok;
+                                       if (last == OLD_ENV_VAR)
+                                               goto env_ovar_wrong;
+                               }
+                               last = ENV_USERVAR;
+                               break;
+                       case ENV_ESC:
+                               if (!SB_EOF())
+                                       c = SB_GET();
+                               /* FALL THROUGH */
+                       default:
+                               empty = 0;
+                               continue;
+                       }
+                       empty = 1;
+               }
+               if (empty) {
+                       if (last == OLD_ENV_VALUE)
+                               goto env_ovar_ok;
+                       if (last == OLD_ENV_VAR)
+                               goto env_ovar_wrong;
+               }
+               /*
+                * Ok, the first thing was a USERVAR, and there
+                * are not two consecutive VAR or VALUE commands,
+                * and none of the VAR or VALUE commands are empty.
+                * If the client has sent us a well-formed option,
+                * then the number of VALUEs received should always
+                * be less than or equal to the number of VARs and
+                * USERVARs received.
+                *
+                * If we got exactly as many VALUEs as VARs and
+                * USERVARs, the client has the same definitions.
+                *
+                * If we got exactly as many VARs as VALUEs and
+                * USERVARS, the client has reversed definitions.
+                */
+               if (got_uservar + got_var == got_value) {
+           env_ovar_ok:
+                       env_ovar = OLD_ENV_VAR;
+                       env_ovalue = OLD_ENV_VALUE;
+               } else if (got_uservar + got_value == got_var) {
+           env_ovar_wrong:
+                       env_ovar = OLD_ENV_VALUE;
+                       env_ovalue = OLD_ENV_VAR;
+                       DIAG(TD_OPTIONS, {sprintf(nfrontp,
+                               "ENVIRON VALUE and VAR are reversed!\r\n");
+                               nfrontp += strlen(nfrontp);});
+
+               }
+           }
+           SB_RESTORE();
+#endif
+
+           while (!SB_EOF()) {
+               c = SB_GET();
+               if ((c == env_ovar) || (c == ENV_USERVAR))
+                       break;
+           }
+       }
+
+       if (SB_EOF())
+               return;
+
+       cp = varp = (char *)subpointer;
+       valp = 0;
+
+       while (!SB_EOF()) {
+               c = SB_GET();
+               if (subchar == TELOPT_OLD_ENVIRON) {
+                       if (c == env_ovar)
+                               c = NEW_ENV_VAR;
+                       else if (c == env_ovalue)
+                               c = NEW_ENV_VALUE;
+               }
+               switch (c) {
+
+               case NEW_ENV_VALUE:
+                       *cp = '\0';
+                       cp = valp = (char *)subpointer;
+                       break;
+
+               case NEW_ENV_VAR:
+               case ENV_USERVAR:
+                       *cp = '\0';
+                       if (valp)
+                               (void)setenv(varp, valp, 1);
+                       else
+                               unsetenv(varp);
+                       cp = varp = (char *)subpointer;
+                       valp = 0;
+                       break;
+
+               case ENV_ESC:
+                       if (SB_EOF())
+                               break;
+                       c = SB_GET();
+                       /* FALL THROUGH */
+               default:
+                       *cp++ = c;
+                       break;
+               }
+       }
+       *cp = '\0';
+       if (valp)
+               (void)setenv(varp, valp, 1);
+       else
+               unsetenv(varp);
+       break;
+    }  /* end of case TELOPT_NEW_ENVIRON */
+#if    defined(AUTHENTICATION)
+    case TELOPT_AUTHENTICATION:
+       if (SB_EOF())
+               break;
+       switch(SB_GET()) {
+       case TELQUAL_SEND:
+       case TELQUAL_REPLY:
+               /*
+                * These are sent by us and cannot be sent by
+                * the client.
+                */
+               break;
+       case TELQUAL_IS:
+               auth_is(subpointer, SB_LEN());
+               break;
+       case TELQUAL_NAME:
+               auth_name(subpointer, SB_LEN());
+               break;
+       }
+       break;
+#endif
+#ifdef ENCRYPTION
+    case TELOPT_ENCRYPT:
+       if (SB_EOF())
+               break;
+       switch(SB_GET()) {
+       case ENCRYPT_SUPPORT:
+               encrypt_support(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_IS:
+               encrypt_is(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_REPLY:
+               encrypt_reply(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_START:
+               encrypt_start(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_END:
+               encrypt_end();
+               break;
+       case ENCRYPT_REQSTART:
+               encrypt_request_start(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_REQEND:
+               /*
+                * We can always send an REQEND so that we cannot
+                * get stuck encrypting.  We should only get this
+                * if we have been able to get in the correct mode
+                * anyhow.
+                */
+               encrypt_request_end();
+               break;
+       case ENCRYPT_ENC_KEYID:
+               encrypt_enc_keyid(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_DEC_KEYID:
+               encrypt_dec_keyid(subpointer, SB_LEN());
+               break;
+       default:
+               break;
+       }
+       break;
+#endif /* ENCRYPTION */
 
     default:
        break;
 
     default:
        break;
@@ -969,13 +1513,20 @@ suboption()
 
 }  /* end of suboption */
 
 
 }  /* end of suboption */
 
-#define        ADD(c)   *ncp++ = c;
-#define        ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
+       void
+doclientstat()
+{
+       clientstat(TELOPT_LINEMODE, WILL, 0);
+}
+
+#define        ADD(c)   *ncp++ = c
+#define        ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
+       void
 send_status()
 {
 send_status()
 {
-       char statusbuf[256];
-       register char *ncp;
-       register int i;
+       unsigned char statusbuf[256];
+       register unsigned char *ncp;
+       register unsigned char i;
 
        ncp = statusbuf;
 
 
        ncp = statusbuf;
 
@@ -986,32 +1537,56 @@ send_status()
        ADD(TELOPT_STATUS);
        ADD(TELQUAL_IS);
 
        ADD(TELOPT_STATUS);
        ADD(TELQUAL_IS);
 
-       for (i = 0; i < NTELOPTS; i++) {
-               if (myopts[i] == OPT_YES) {
+       /*
+        * We check the want_state rather than the current state,
+        * because if we received a DO/WILL for an option that we
+        * don't support, and the other side didn't send a DONT/WONT
+        * in response to our WONT/DONT, then the "state" will be
+        * WILL/DO, and the "want_state" will be WONT/DONT.  We
+        * need to go by the latter.
+        */
+       for (i = 0; i < (unsigned char)NTELOPTS; i++) {
+               if (my_want_state_is_will(i)) {
                        ADD(WILL);
                        ADD_DATA(i);
                        ADD(WILL);
                        ADD_DATA(i);
-                       if (i == IAC)
-                               ADD(IAC);
                }
                }
-               if (hisopts[i] == OPT_YES) {
+               if (his_want_state_is_will(i)) {
                        ADD(DO);
                        ADD_DATA(i);
                        ADD(DO);
                        ADD_DATA(i);
-                       if (i == IAC)
-                               ADD(IAC);
+               }
+       }
+
+       if (his_want_state_is_will(TELOPT_LFLOW)) {
+               ADD(SB);
+               ADD(TELOPT_LFLOW);
+               if (flowmode) {
+                       ADD(LFLOW_ON);
+               } else {
+                       ADD(LFLOW_OFF);
+               }
+               ADD(SE);
+
+               if (restartany >= 0) {
+                       ADD(SB);
+                       ADD(TELOPT_LFLOW);
+                       if (restartany) {
+                               ADD(LFLOW_RESTART_ANY);
+                       } else {
+                               ADD(LFLOW_RESTART_XON);
+                       }
+                       ADD(SE);
                }
        }
 
 #ifdef LINEMODE
                }
        }
 
 #ifdef LINEMODE
-       if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
-               char *cp, *cpe;
+       if (his_want_state_is_will(TELOPT_LINEMODE)) {
+               unsigned char *cp, *cpe;
                int len;
 
                ADD(SB);
                ADD(TELOPT_LINEMODE);
                ADD(LM_MODE);
                ADD_DATA(editmode);
                int len;
 
                ADD(SB);
                ADD(TELOPT_LINEMODE);
                ADD(LM_MODE);
                ADD_DATA(editmode);
-               if (editmode == IAC)
-                       ADD(IAC);
                ADD(SE);
 
                ADD(SB);
                ADD(SE);
 
                ADD(SB);
@@ -1031,4 +1606,7 @@ send_status()
 
        writenet(statusbuf, ncp - statusbuf);
        netflush();     /* Send it on its way */
 
        writenet(statusbuf, ncp - statusbuf);
        netflush();     /* Send it on its way */
+
+       DIAG(TD_OPTIONS,
+               {printsub('>', statusbuf, ncp - statusbuf); netflush();});
 }
 }