]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD telnetd(8) 63/3463/1
authorDavid van Moolenbroek <david@minix3.org>
Wed, 15 Feb 2017 20:36:35 +0000 (20:36 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 9 Mar 2017 23:40:17 +0000 (23:40 +0000)
Change-Id: Ie1ae80e8a4945f6f4ccce8f468c1b04a8d89cf43

18 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
libexec/Makefile
libexec/telnetd/Makefile [new file with mode: 0644]
libexec/telnetd/authenc.c [new file with mode: 0644]
libexec/telnetd/defs.h [new file with mode: 0644]
libexec/telnetd/ext.h [new file with mode: 0644]
libexec/telnetd/global.c [new file with mode: 0644]
libexec/telnetd/pathnames.h [new file with mode: 0644]
libexec/telnetd/slc.c [new file with mode: 0644]
libexec/telnetd/state.c [new file with mode: 0644]
libexec/telnetd/sys_term.c [new file with mode: 0644]
libexec/telnetd/telnetd.8 [new file with mode: 0644]
libexec/telnetd/telnetd.c [new file with mode: 0644]
libexec/telnetd/telnetd.h [new file with mode: 0644]
libexec/telnetd/termstat.c [new file with mode: 0644]
libexec/telnetd/utility.c [new file with mode: 0644]

index 6c02eb5cc4e7d084e2960700d98b347ac4861ecb..f25b07396d466bc913754f4ea3d6d2d1c9bf7d0d 100644 (file)
 ./usr/libexec/ld.elf_so                                 minix-base
 ./usr/libexec/makewhatis                                minix-base
 ./usr/libexec/rshd                                      minix-base
+./usr/libexec/telnetd                                   minix-base
 ./usr/libexec/virecover                                 minix-base
 ./usr/log                                               minix-base
 ./usr/log/messages                                      minix-base
index abfe3e6244c1a5ccc22d4c6c7cc309ab10d762b5..e86139f649fb2662e25896642be21e0613073721 100644 (file)
 ./usr/libdata/debug/usr/libexec/lto1.debug              minix-debug     gcc=5,gcccmds,debug
 ./usr/libdata/debug/usr/libexec/makewhatis.debug        minix-debug     debug
 ./usr/libdata/debug/usr/libexec/rshd.debug              minix-debug     debug
+./usr/libdata/debug/usr/libexec/telnetd.debug           minix-debug     debug
 ./usr/libdata/debug/usr/mdec                            minix-debug
 ./usr/libdata/debug/usr/sbin                            minix-debug
 ./usr/libdata/debug/usr/sbin/arp.debug                  minix-debug     debug
index c8019ee63ac8c070bd7768f120e4feebee5d291e..05205538e63e836349aabbfb6ca77cc7bcc8a6f5 100644 (file)
 ./usr/man/man8/sysctl.8                                 minix-man
 ./usr/man/man8/syslogd.8                                minix-man
 ./usr/man/man8/tcpd.8                                   minix-man       obsolete
+./usr/man/man8/telnetd.8                                minix-man
 ./usr/man/man8/traceroute.8                             minix-man
 ./usr/man/man8/traceroute6.8                            minix-man       use_inet6
 ./usr/man/man8/uds.8                                    minix-man       obsolete
index e4e83dccf449a418c4ee7dbbaeb4a8dd010252db..7e079ac51c67c5e7ea4df9740e5379b94de12061 100644 (file)
@@ -7,9 +7,7 @@ SUBDIR= \
        fingerd ftpd getty \
        ld.elf_so \
        rshd \
-       
-
-
+       telnetd
 
 .if defined(__MINIX)
 SUBDIR+= makewhatis
diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile
new file mode 100644 (file)
index 0000000..62590a6
--- /dev/null
@@ -0,0 +1,43 @@
+#      $NetBSD: Makefile,v 1.49 2012/01/09 16:36:48 christos Exp $
+#      from: @(#)Makefile      8.2 (Berkeley) 12/15/93
+
+WARNS?=        4               # XXX: const issues in sys_term.c
+
+.include <bsd.own.mk>
+
+MAN=   telnetd.8
+
+PROG=  telnetd
+.if defined(__MINIX)
+CPPFLAGS+=-DDIAGNOSTICS
+.else # !defined(__MINIX)
+CPPFLAGS+=-DLINEMODE -DKLUDGELINEMODE -DDIAGNOSTICS
+.endif # !defined(__MINIX)
+CPPFLAGS+=-DOLD_ENVIRON -DENV_HACK -DSECURELOGIN -DSUPPORT_UTMP -DSUPPORT_UTMPX
+SRCS=  authenc.c global.c slc.c state.c sys_term.c telnetd.c \
+       termstat.c utility.c
+DPADD= ${LIBTELNETDIR}/libtelnet.a ${LIBTERMINFO} ${LIBUTIL}
+LDADD+=        ${LIBTELNETDIR}/libtelnet.a -lterminfo -lutil
+
+CPPFLAGS+=-I${NETBSDSRCDIR}/lib
+CPPFLAGS+=-I${.CURDIR}
+
+LIBTELNETDIR!= cd ${.CURDIR}/../../lib/libtelnet; ${PRINTOBJDIR}
+
+.if (${USE_KERBEROS} != "no")
+CPPFLAGS+=-DKRB5 -DAUTHENTICATION -DENCRYPTION
+LDADD+= -lkrb5 -lasn1 -lcom_err -lroken
+DPADD+=        ${LIBKRB5} ${LIBASN1} ${LIBCOM_ERR} ${LIBROKEN}
+.endif
+
+.if (${MKCRYPTO} != "no")
+LDADD+=-ldes -lcrypto -lcrypt
+DPADD+=${LIBDES} ${LIBCRYPTO} ${LIBCRYPT}
+.endif
+
+.if (${USE_PAM} != "no")
+LDADD+= -lpam ${PAM_STATIC_LDADD}
+DPADD+=        ${LIBPAM} ${PAM_STATIC_DPADD}
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/telnetd/authenc.c b/libexec/telnetd/authenc.c
new file mode 100644 (file)
index 0000000..d583e10
--- /dev/null
@@ -0,0 +1,85 @@
+/*     $NetBSD: authenc.c,v 1.12 2005/02/06 05:58:20 perry Exp $       */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)authenc.c  8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: authenc.c,v 1.12 2005/02/06 05:58:20 perry Exp $");
+#endif
+#endif /* not lint */
+
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+#include "telnetd.h"
+#include <libtelnet/misc.h>
+
+int
+telnet_net_write(unsigned char *str, int len)
+{
+       if (nfrontp + len < netobuf + BUFSIZ) {
+               output_datalen((const char *)str, len);
+               return(len);
+       }
+       return(0);
+}
+
+void
+net_encrypt(void)
+{
+#ifdef ENCRYPTION
+       char *s = (nclearto > nbackp) ? nclearto : nbackp;
+       if (s < nfrontp && encrypt_output) {
+               (*encrypt_output)((unsigned char *)s, nfrontp - s);
+       }
+       nclearto = nfrontp;
+#endif /* ENCRYPTION */
+}
+
+int
+telnet_spin(void)
+{
+       ttloop();
+       return(0);
+}
+
+char *
+telnet_getenv(char *val)
+{
+       return(getenv(val));
+}
+
+char *
+telnet_gets(char *prompt, char *result, int length, int echo)
+{
+       return((char *)0);
+}
+#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
diff --git a/libexec/telnetd/defs.h b/libexec/telnetd/defs.h
new file mode 100644 (file)
index 0000000..a51eea6
--- /dev/null
@@ -0,0 +1,200 @@
+/*     $NetBSD: defs.h,v 1.16 2007/02/21 21:14:07 hubertf Exp $        */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ *
+ *     from: @(#)defs.h        8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Telnet server defines
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#if defined(PRINTOPTIONS) && defined(DIAGNOSTICS)
+#define TELOPTS
+#define TELCMDS
+#define        SLC_NAMES
+#endif
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+
+#include <arpa/telnet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+/*
+ * I/O data buffers defines
+ */
+#define        NETSLOP 64
+
+#define        NIACCUM(c)      do { \
+                           *netip++ = c; \
+                           ncc++; \
+                       } while (0)
+
+/* clock manipulations */
+#define        settimer(x)     (clocks.x = ++clocks.system)
+#define        sequenceIs(x,y) (clocks.x < clocks.y)
+
+/*
+ * Linemode support states, in decreasing order of importance
+ */
+#define REAL_LINEMODE  0x04
+#define KLUDGE_OK      0x03
+#define        NO_AUTOKLUDGE   0x02
+#define KLUDGE_LINEMODE        0x01
+#define NO_LINEMODE    0x00
+
+/*
+ * Structures of information for each special character function.
+ */
+typedef struct {
+       unsigned char   flag;           /* the flags for this function */
+       cc_t            val;            /* the value of the special character */
+} slcent, *Slcent;
+
+typedef struct {
+       slcent          defset;         /* the default settings */
+       slcent          current;        /* the current settings */
+       cc_t            *sptr;          /* a pointer to the char in */
+                                       /* system data structures */
+} slcfun, *Slcfun;
+
+#ifdef DIAGNOSTICS
+/*
+ * Diagnostics capabilities
+ */
+#define        TD_REPORT       0x01    /* Report operations to client */
+#define TD_EXERCISE    0x02    /* Exercise client's implementation */
+#define TD_NETDATA     0x04    /* Display received data stream */
+#define TD_PTYDATA     0x08    /* Display data passed to pty */
+#define        TD_OPTIONS      0x10    /* Report just telnet options */
+#endif /* DIAGNOSTICS */
+
+/*
+ * We keep track of each side of the option negotiation.
+ */
+
+#define        MY_STATE_WILL           0x01
+#define        MY_WANT_STATE_WILL      0x02
+#define        MY_STATE_DO             0x04
+#define        MY_WANT_STATE_DO        0x08
+
+/*
+ * Macros to check the current state of things
+ */
+
+#define        my_state_is_do(opt)             (options[opt]&MY_STATE_DO)
+#define        my_state_is_will(opt)           (options[opt]&MY_STATE_WILL)
+#define my_want_state_is_do(opt)       (options[opt]&MY_WANT_STATE_DO)
+#define my_want_state_is_will(opt)     (options[opt]&MY_WANT_STATE_WILL)
+
+#define        my_state_is_dont(opt)           (!my_state_is_do(opt))
+#define        my_state_is_wont(opt)           (!my_state_is_will(opt))
+#define my_want_state_is_dont(opt)     (!my_want_state_is_do(opt))
+#define my_want_state_is_wont(opt)     (!my_want_state_is_will(opt))
+
+#define        set_my_state_do(opt)            (options[opt] |= MY_STATE_DO)
+#define        set_my_state_will(opt)          (options[opt] |= MY_STATE_WILL)
+#define        set_my_want_state_do(opt)       (options[opt] |= MY_WANT_STATE_DO)
+#define        set_my_want_state_will(opt)     (options[opt] |= MY_WANT_STATE_WILL)
+
+#define        set_my_state_dont(opt)          (options[opt] &= ~MY_STATE_DO)
+#define        set_my_state_wont(opt)          (options[opt] &= ~MY_STATE_WILL)
+#define        set_my_want_state_dont(opt)     (options[opt] &= ~MY_WANT_STATE_DO)
+#define        set_my_want_state_wont(opt)     (options[opt] &= ~MY_WANT_STATE_WILL)
+
+/*
+ * Tricky code here.  What we want to know is if the MY_STATE_WILL
+ * and MY_WANT_STATE_WILL bits have the same value.  Since the two
+ * bits are adjacent, a little arithmetic will show that by adding
+ * in the lower bit, the upper bit will be set if the two bits were
+ * different, and clear if they were the same.
+ */
+#define my_will_wont_is_changing(opt) \
+                       ((options[opt]+MY_STATE_WILL) & MY_WANT_STATE_WILL)
+
+#define my_do_dont_is_changing(opt) \
+                       ((options[opt]+MY_STATE_DO) & MY_WANT_STATE_DO)
+
+/*
+ * Make everything symmetrical
+ */
+
+#define        HIS_STATE_WILL                  MY_STATE_DO
+#define        HIS_WANT_STATE_WILL             MY_WANT_STATE_DO
+#define HIS_STATE_DO                   MY_STATE_WILL
+#define HIS_WANT_STATE_DO              MY_WANT_STATE_WILL
+
+#define        his_state_is_do                 my_state_is_will
+#define        his_state_is_will               my_state_is_do
+#define his_want_state_is_do           my_want_state_is_will
+#define his_want_state_is_will         my_want_state_is_do
+
+#define        his_state_is_dont               my_state_is_wont
+#define        his_state_is_wont               my_state_is_dont
+#define his_want_state_is_dont         my_want_state_is_wont
+#define his_want_state_is_wont         my_want_state_is_dont
+
+#define        set_his_state_do                set_my_state_will
+#define        set_his_state_will              set_my_state_do
+#define        set_his_want_state_do           set_my_want_state_will
+#define        set_his_want_state_will         set_my_want_state_do
+
+#define        set_his_state_dont              set_my_state_wont
+#define        set_his_state_wont              set_my_state_dont
+#define        set_his_want_state_dont         set_my_want_state_wont
+#define        set_his_want_state_wont         set_my_want_state_dont
+
+#define his_will_wont_is_changing      my_do_dont_is_changing
+#define his_do_dont_is_changing                my_will_wont_is_changing
+
+/*
+ * Initialization buffer for tty device [16 characters long]
+ */
+#define NULL16STR      "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
diff --git a/libexec/telnetd/ext.h b/libexec/telnetd/ext.h
new file mode 100644 (file)
index 0000000..6726aab
--- /dev/null
@@ -0,0 +1,207 @@
+/*     $NetBSD: ext.h,v 1.22 2012/01/10 23:39:11 joerg Exp $   */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ *
+ *     from: @(#)ext.h 8.2 (Berkeley) 12/15/93
+ */
+
+/*
+ * Telnet server variable declarations
+ */
+extern char    options[256];
+extern char    do_dont_resp[256];
+extern char    will_wont_resp[256];
+extern int     linemode;       /* linemode on/off */
+#ifdef LINEMODE
+extern int     uselinemode;    /* what linemode to use (on/off) */
+extern int     editmode;       /* edit modes in use */
+extern int     useeditmode;    /* edit modes to use */
+extern int     alwayslinemode; /* command line option */
+# ifdef        KLUDGELINEMODE
+extern int     lmodetype;      /* Client support for linemode */
+# endif        /* KLUDGELINEMODE */
+#endif /* LINEMODE */
+extern int     flowmode;       /* current flow control state */
+extern int     restartany;     /* restart output on any character state */
+#ifdef DIAGNOSTICS
+extern int     diagnostic;     /* telnet diagnostic capabilities */
+#endif /* DIAGNOSTICS */
+#ifdef SECURELOGIN
+extern int     require_secure_login;
+#endif
+#ifdef AUTHENTICATION
+extern int     auth_level;
+#endif
+
+extern slcfun  slctab[NSLC + 1];       /* slc mapping table */
+extern char    terminaltype[41];
+
+/*
+ * I/O data buffers, pointers, and counters.
+ */
+extern char    ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp;
+extern char    netibuf[BUFSIZ], *netip;
+extern char    netobuf[BUFSIZ+NETSLOP], *nfrontp, *nbackp;
+extern char    *neturg;                /* one past last bye of urgent data */
+extern int     pcc, ncc;
+extern int     pty, net;
+extern char    *line;
+extern int     SYNCHing;               /* we are in TELNET SYNCH mode */
+
+#include <sys/cdefs.h>
+
+extern void
+       _termstat(void),
+       add_slc(char, char, cc_t),
+       check_slc(void),
+       change_slc(int, int, cc_t),
+       cleanup(int) __dead,
+       clientstat(int, int, int),
+       copy_termbuf(char *, int),
+       deferslc(void),
+       defer_terminit(void),
+       do_opt_slc(unsigned char *, int),
+       doeof(void),
+       dooption(int),
+       dontoption(int),
+       edithost(const char *, const char *),
+       fatal(int, const char *) __dead,
+       fatalperror(int, const char *) __dead,
+       get_slc_defaults(void),
+       init_env(void),
+       init_termbuf(void),
+       interrupt(void),
+       localstat(void),
+       flowstat(void),
+       netclear(void),
+       netflush(void),
+#ifdef DIAGNOSTICS
+       printoption(const char *, int),
+       printdata(const char *, char *, int),
+#ifndef ENCRYPTION
+       printsub(int, unsigned char *, int),
+#endif
+#endif
+       ptyflush(void),
+       putchr(int),
+       recv_ayt(void),
+       send_do(int, int),
+       send_dont(int, int),
+       send_slc(void),
+       send_status(void),
+       send_will(int, int),
+       send_wont(int, int),
+       sendbrk(void),
+       sendsusp(void),
+       set_termbuf(void),
+       start_login(char *, int, char *) __dead,
+       start_slc(int),
+       startslave(char *, int, char *),
+       suboption(void),
+       telrcv(void),
+       ttloop(void),
+       tty_binaryin(int),
+       tty_binaryout(int);
+
+extern char *
+       putf(const char *, char *);
+
+extern int
+       end_slc(unsigned char **),
+       getnpty(void),
+       getpty(int *),
+       spcset(int, cc_t *, cc_t **),
+       stilloob(int),
+       terminit(void),
+       termstat(void),
+       tty_flowmode(void),
+       tty_restartany(void),
+       tty_isbinaryin(void),
+       tty_isbinaryout(void),
+       tty_iscrnl(void),
+       tty_isecho(void),
+       tty_isediting(void),
+       tty_islitecho(void),
+       tty_isnewmap(void),
+       tty_israw(void),
+       tty_issofttab(void),
+       tty_istrapsig(void),
+       tty_linemode(void);
+
+extern void
+       tty_rspeed(int),
+       tty_setecho(int),
+       tty_setedit(int),
+       tty_setlinemode(int),
+       tty_setlitecho(int),
+       tty_setsig(int),
+       tty_setsofttab(int),
+       tty_tspeed(int),
+       willoption(int),
+       wontoption(int),
+       writenet(unsigned char *, int);
+
+extern int output_data(const char *, ...)
+       __attribute__((__format__(__printf__, 1, 2)));
+extern int output_datalen(const char *, size_t);
+
+#ifdef ENCRYPTION
+extern char    *nclearto;
+#endif /* ENCRYPTION */
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+extern struct {
+    int
+       system,                 /* what the current time is */
+       echotoggle,             /* last time user entered echo character */
+       modenegotiated,         /* last time operating mode negotiated */
+       didnetreceive,          /* last time we read data from network */
+       ttypesubopt,            /* ttype subopt is received */
+       tspeedsubopt,           /* tspeed subopt is received */
+       environsubopt,          /* environ subopt is received */
+       oenvironsubopt,         /* old environ subopt is received */
+       xdisplocsubopt,         /* xdisploc subopt is received */
+       baseline,               /* time started to do timed action */
+       gotDM;                  /* when did we last see a data mark */
+} clocks;
+
+#ifndef        DEFAULT_IM
+# define DEFAULT_IM    "\r\n\r\n4.4 BSD UNIX (%h) (%t)\r\n\r\r\n\r"
+#endif
+
+#ifdef AUTHENTICATION
+#include <libtelnet/auth.h>
+#endif
+
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
diff --git a/libexec/telnetd/global.c b/libexec/telnetd/global.c
new file mode 100644 (file)
index 0000000..2b5ef2d
--- /dev/null
@@ -0,0 +1,51 @@
+/*     $NetBSD: global.c,v 1.8 2003/08/07 09:46:51 agc Exp $   */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)global.c   8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: global.c,v 1.8 2003/08/07 09:46:51 agc Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * Allocate global variables.  We do this
+ * by including the header file that defines
+ * them all as externs, but first we define
+ * the keyword "extern" to be nothing, so that
+ * we will actually allocate the space.
+ */
+
+#include <defs.h>
+#define extern
+#include <ext.h>
diff --git a/libexec/telnetd/pathnames.h b/libexec/telnetd/pathnames.h
new file mode 100644 (file)
index 0000000..cf4f3b2
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $NetBSD: pathnames.h,v 1.7 2003/08/07 09:46:51 agc Exp $        */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ *
+ *     from: @(#)pathnames.h   8.1 (Berkeley) 6/4/93
+ */
+
+#include <paths.h>
+
+#ifndef _PATH_LOGIN
+# define       _PATH_LOGIN     "/usr/bin/login"
+#endif
diff --git a/libexec/telnetd/slc.c b/libexec/telnetd/slc.c
new file mode 100644 (file)
index 0000000..346713c
--- /dev/null
@@ -0,0 +1,488 @@
+/*     $NetBSD: slc.c,v 1.15 2006/11/24 19:46:58 christos Exp $        */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)slc.c      8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: slc.c,v 1.15 2006/11/24 19:46:58 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include "telnetd.h"
+
+#ifdef LINEMODE
+/*
+ * local varibles
+ */
+static unsigned char   *def_slcbuf = (unsigned char *)0;
+static int             def_slclen = 0;
+static int             slcchange;      /* change to slc is requested */
+static unsigned char   *slcptr;        /* pointer into slc buffer */
+static unsigned char   slcbuf[NSLC*6]; /* buffer for slc negotiation */
+
+void default_slc(void);
+void process_slc(u_int, u_int, cc_t);
+
+/*
+ * send_slc
+ *
+ * Write out the current special characters to the client.
+ */
+void
+send_slc(void)
+{
+       int i;
+
+       /*
+        * Send out list of triplets of special characters
+        * to client.  We only send info on the characters
+        * that are currently supported.
+        */
+       for (i = 1; i <= NSLC; i++) {
+               if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
+                       continue;
+               add_slc((unsigned char)i, slctab[i].current.flag,
+                                                       slctab[i].current.val);
+       }
+
+}  /* end of send_slc */
+
+/*
+ * default_slc
+ *
+ * Set pty special characters to all the defaults.
+ */
+void
+default_slc(void)
+{
+       int i;
+
+       for (i = 1; i <= NSLC; i++) {
+               slctab[i].current.val = slctab[i].defset.val;
+               if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE))
+                       slctab[i].current.flag = SLC_NOSUPPORT;
+               else
+                       slctab[i].current.flag = slctab[i].defset.flag;
+               if (slctab[i].sptr) {
+                       *(slctab[i].sptr) = slctab[i].defset.val;
+               }
+       }
+       slcchange = 1;
+
+}  /* end of default_slc */
+#endif /* LINEMODE */
+
+/*
+ * get_slc_defaults
+ *
+ * Initialize the slc mapping table.
+ */
+void
+get_slc_defaults(void)
+{
+       int i;
+
+       init_termbuf();
+
+       for (i = 1; i <= NSLC; i++) {
+               slctab[i].defset.flag =
+                       spcset(i, &slctab[i].defset.val, &slctab[i].sptr);
+               slctab[i].current.flag = SLC_NOSUPPORT;
+               slctab[i].current.val = 0;
+       }
+
+}  /* end of get_slc_defaults */
+
+#ifdef LINEMODE
+/*
+ * add_slc
+ *
+ * Add an slc triplet to the slc buffer.
+ */
+void
+add_slc(char func, char flag, cc_t val)
+{
+
+       if ((*slcptr++ = (unsigned char)func) == 0xff)
+               *slcptr++ = 0xff;
+
+       if ((*slcptr++ = (unsigned char)flag) == 0xff)
+               *slcptr++ = 0xff;
+
+       if ((*slcptr++ = (unsigned char)val) == 0xff)
+               *slcptr++ = 0xff;
+
+}  /* end of add_slc */
+
+/*
+ * start_slc
+ *
+ * Get ready to process incoming slc's and respond to them.
+ *
+ * The parameter getit is non-zero if it is necessary to grab a copy
+ * of the terminal control structures.
+ */
+void
+start_slc(int getit)
+{
+
+       slcchange = 0;
+       if (getit)
+               init_termbuf();
+       (void)snprintf((char *)slcbuf, sizeof slcbuf, "%c%c%c%c",
+                                       IAC, SB, TELOPT_LINEMODE, LM_SLC);
+       slcptr = slcbuf + 4;
+
+}  /* end of start_slc */
+
+/*
+ * end_slc
+ *
+ * Finish up the slc negotiation.  If something to send, then send it.
+ */
+int
+end_slc(unsigned char **bufp)
+{
+       int len;
+
+       /*
+        * If a change has occurred, store the new terminal control
+        * structures back to the terminal driver.
+        */
+       if (slcchange) {
+               set_termbuf();
+       }
+
+       /*
+        * If the pty state has not yet been fully processed and there is a
+        * deferred slc request from the client, then do not send any
+        * sort of slc negotiation now.  We will respond to the client's
+        * request very soon.
+        */
+       if (def_slcbuf && (terminit() == 0)) {
+               return(0);
+       }
+
+       if (slcptr > (slcbuf + 4)) {
+               if (bufp) {
+                       *bufp = &slcbuf[4];
+                       return(slcptr - slcbuf - 4);
+               } else {
+                       (void) snprintf((char *)slcptr,
+                           sizeof(slcbuf) - (slcptr - slcbuf), "%c%c",
+                           IAC, SE);
+                       slcptr += 2;
+                       len = slcptr - slcbuf;
+                       writenet(slcbuf, len);
+                       netflush();  /* force it out immediately */
+                       DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2););
+               }
+       }
+       return (0);
+
+}  /* end of end_slc */
+
+/*
+ * process_slc
+ *
+ * Figure out what to do about the client's slc
+ */
+void
+process_slc(u_int func, u_int flag, cc_t val)
+{
+       int hislevel, mylevel, ack;
+
+       /*
+        * Ensure that we know something about this function
+        */
+       if (func > NSLC) {
+               add_slc(func, SLC_NOSUPPORT, 0);
+               return;
+       }
+
+       /*
+        * Process the special case requests of 0 SLC_DEFAULT 0
+        * and 0 SLC_VARIABLE 0.  Be a little forgiving here, don't
+        * worry about whether the value is actually 0 or not.
+        */
+       if (func == 0) {
+               if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
+                       default_slc();
+                       send_slc();
+               } else if (flag == SLC_VARIABLE) {
+                       send_slc();
+               }
+               return;
+       }
+
+       /*
+        * Appears to be a function that we know something about.  So
+        * get on with it and see what we know.
+        */
+
+       hislevel = flag & SLC_LEVELBITS;
+       mylevel = slctab[func].current.flag & SLC_LEVELBITS;
+       ack = flag & SLC_ACK;
+       /*
+        * ignore the command if:
+        * the function value and level are the same as what we already have;
+        * or the level is the same and the ack bit is set
+        */
+       if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
+               return;
+       } else if (ack) {
+               /*
+                * If we get here, we got an ack, but the levels don't match.
+                * This shouldn't happen.  If it does, it is probably because
+                * we have sent two requests to set a variable without getting
+                * a response between them, and this is the first response.
+                * So, ignore it, and wait for the next response.
+                */
+               return;
+       } else {
+               change_slc(func, flag, val);
+       }
+
+}  /* end of process_slc */
+
+/*
+ * change_slc
+ *
+ * Process a request to change one of our special characters.
+ * Compare client's request with what we are capable of supporting.
+ */
+void
+change_slc(int func, int flag, cc_t val)
+{
+       int hislevel, mylevel;
+
+       hislevel = flag & SLC_LEVELBITS;
+       mylevel = slctab[func].defset.flag & SLC_LEVELBITS;
+       /*
+        * If client is setting a function to NOSUPPORT
+        * or DEFAULT, then we can easily and directly
+        * accommodate the request.
+        */
+       if (hislevel == SLC_NOSUPPORT) {
+               slctab[func].current.flag = flag;
+               slctab[func].current.val = (cc_t)_POSIX_VDISABLE;
+               flag |= SLC_ACK;
+               add_slc(func, flag, val);
+               return;
+       }
+       if (hislevel == SLC_DEFAULT) {
+               /*
+                * Special case here.  If client tells us to use
+                * the default on a function we don't support, then
+                * return NOSUPPORT instead of what we may have as a
+                * default level of DEFAULT.
+                */
+               if (mylevel == SLC_DEFAULT) {
+                       slctab[func].current.flag = SLC_NOSUPPORT;
+               } else {
+                       slctab[func].current.flag = slctab[func].defset.flag;
+               }
+               slctab[func].current.val = slctab[func].defset.val;
+               add_slc(func, slctab[func].current.flag,
+                                               slctab[func].current.val);
+               return;
+       }
+
+       /*
+        * Client wants us to change to a new value or he
+        * is telling us that he can't change to our value.
+        * Some of the slc's we support and can change,
+        * some we do support but can't change,
+        * and others we don't support at all.
+        * If we can change it then we have a pointer to
+        * the place to put the new value, so change it,
+        * otherwise, continue the negotiation.
+        */
+       if (slctab[func].sptr) {
+               /*
+                * We can change this one.
+                */
+               slctab[func].current.val = val;
+               *(slctab[func].sptr) = val;
+               slctab[func].current.flag = flag;
+               flag |= SLC_ACK;
+               slcchange = 1;
+               add_slc(func, flag, val);
+       } else {
+               /*
+               * It is not possible for us to support this
+               * request as he asks.
+               *
+               * If our level is DEFAULT, then just ack whatever was
+               * sent.
+               *
+               * If he can't change and we can't change,
+               * then degenerate to NOSUPPORT.
+               *
+               * Otherwise we send our level back to him, (CANTCHANGE
+               * or NOSUPPORT) and if CANTCHANGE, send
+               * our value as well.
+               */
+               if (mylevel == SLC_DEFAULT) {
+                       slctab[func].current.flag = flag;
+                       slctab[func].current.val = val;
+                       flag |= SLC_ACK;
+               } else if (hislevel == SLC_CANTCHANGE &&
+                                   mylevel == SLC_CANTCHANGE) {
+                       flag &= ~SLC_LEVELBITS;
+                       flag |= SLC_NOSUPPORT;
+                       slctab[func].current.flag = flag;
+               } else {
+                       flag &= ~SLC_LEVELBITS;
+                       flag |= mylevel;
+                       slctab[func].current.flag = flag;
+                       if (mylevel == SLC_CANTCHANGE) {
+                               slctab[func].current.val =
+                                       slctab[func].defset.val;
+                               val = slctab[func].current.val;
+                       }
+               }
+               add_slc(func, flag, val);
+       }
+
+}  /* end of change_slc */
+
+#if VEOF == VMIN
+cc_t oldeofc = '\004';
+#endif
+
+/*
+ * check_slc
+ *
+ * Check the special characters in use and notify the client if any have
+ * changed.  Only those characters that are capable of being changed are
+ * likely to have changed.  If a local change occurs, kick the support level
+ * and flags up to the defaults.
+ */
+void
+check_slc(void)
+{
+       int i;
+
+       for (i = 1; i <= NSLC; i++) {
+#if VEOF == VMIN
+               /*
+                * In a perfect world this would be a neat little
+                * function.  But in this world, we should not notify
+                * client of changes to the VEOF char when
+                * ICANON is off, because it is not representing
+                * a special character.
+                */
+               if (i == SLC_EOF) {
+                       if (!tty_isediting())
+                               continue;
+                       else if (slctab[i].sptr)
+                               oldeofc = *(slctab[i].sptr);
+               }
+#endif /* VEOF == VMIN */
+               if (slctab[i].sptr &&
+                               (*(slctab[i].sptr) != slctab[i].current.val)) {
+                       slctab[i].current.val = *(slctab[i].sptr);
+                       if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE)
+                               slctab[i].current.flag = SLC_NOSUPPORT;
+                       else
+                               slctab[i].current.flag = slctab[i].defset.flag;
+                       add_slc((unsigned char)i, slctab[i].current.flag,
+                                               slctab[i].current.val);
+               }
+       }
+}  /* check_slc */
+
+/*
+ * do_opt_slc
+ *
+ * Process an slc option buffer.  Defer processing of incoming slc's
+ * until after the terminal state has been processed.  Save the first slc
+ * request that comes along, but discard all others.
+ *
+ * ptr points to the beginning of the buffer, len is the length.
+ */
+void
+do_opt_slc(unsigned char *ptr, int len)
+{
+       unsigned char func, flag;
+       cc_t val;
+       unsigned char *end = ptr + len;
+
+       if (terminit()) {  /* go ahead */
+               while (ptr < end) {
+                       func = *ptr++;
+                       if (ptr >= end) break;
+                       flag = *ptr++;
+                       if (ptr >= end) break;
+                       val = (cc_t)*ptr++;
+
+                       process_slc((u_int)func, (u_int)flag, val);
+
+               }
+       } else {
+               /*
+                * save this slc buffer if it is the first, otherwise dump
+                * it.
+                */
+               if (def_slcbuf == (unsigned char *)0) {
+                       def_slclen = len;
+                       def_slcbuf = (unsigned char *)malloc((unsigned)len);
+                       if (def_slcbuf == (unsigned char *)0)
+                               return;  /* too bad */
+                       memcpy(def_slcbuf, ptr, len);
+               }
+       }
+
+}  /* end of do_opt_slc */
+
+/*
+ * deferslc
+ *
+ * Do slc stuff that was deferred.
+ */
+void
+deferslc(void)
+{
+       if (def_slcbuf) {
+               start_slc(1);
+               do_opt_slc(def_slcbuf, def_slclen);
+               (void) end_slc(0);
+               free(def_slcbuf);
+               def_slcbuf = (unsigned char *)0;
+               def_slclen = 0;
+       }
+
+}  /* end of deferslc */
+
+#endif /* LINEMODE */
diff --git a/libexec/telnetd/state.c b/libexec/telnetd/state.c
new file mode 100644 (file)
index 0000000..7021ff3
--- /dev/null
@@ -0,0 +1,1680 @@
+/*     $NetBSD: state.c,v 1.29 2012/01/09 16:36:48 christos Exp $      */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)state.c    8.5 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: state.c,v 1.29 2012/01/09 16:36:48 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "telnetd.h"
+
+static int envvarok(char *);
+
+int    not42 = 1;
+
+/*
+ * Buffer for sub-options, and macros
+ * for suboptions buffer manipulations
+ */
+unsigned char subbuffer[4096], *subpointer= subbuffer, *subend= subbuffer;
+
+#define        SB_CLEAR()      subpointer = subbuffer
+#define        SB_TERM()       { subend = subpointer; SB_CLEAR(); }
+#define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
+                               *subpointer++ = (c); \
+                       }
+#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
+
+
+/*
+ * State for recv fsm
+ */
+#define        TS_DATA         0       /* base state */
+#define        TS_IAC          1       /* look for double IAC's */
+#define        TS_CR           2       /* CR-LF ->'s CR */
+#define        TS_SB           3       /* throw away begin's... */
+#define        TS_SE           4       /* ...end's (suboption negotiation) */
+#define        TS_WILL         5       /* will option negotiation */
+#define        TS_WONT         6       /* wont " */
+#define        TS_DO           7       /* do " */
+#define        TS_DONT         8       /* dont " */
+
+void
+telrcv(void)
+{
+       int c;
+       static int state = TS_DATA;
+
+       while (ncc > 0) {
+               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:
+                       state = TS_DATA;
+                       /* Strip off \n or \0 after a \r */
+                       if ((c == 0) || (c == '\n')) {
+                               break;
+                       }
+                       /* FALL THROUGH */
+
+               case TS_DATA:
+                       if (c == IAC) {
+                               state = TS_IAC;
+                               break;
+                       }
+                       /*
+                        * We now map \r\n ==> \r for pragmatic reasons.
+                        * Many client implementations send \r\n when
+                        * the user hits the CarriageReturn key.
+                        *
+                        * We USED to map \r\n ==> \n, since \r\n says
+                        * that we want to be in column 1 of the next
+                        * printable line, and \n is the standard
+                        * unix way of saying that (\r is only good
+                        * if CRMOD is set, which it normally is).
+                        */
+                       if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
+#ifndef __minix
+                               int nc = *netip;
+#endif /* !__minix */
+#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 (linemode && (ncc > 0) && (('\n' == nc) ||
+                                        ((0 == nc) && tty_iscrnl())) ) {
+                                       netip++; ncc--;
+                                       c = '\n';
+                               } else
+#endif
+                               {
+#ifdef ENCRYPTION
+                                       if (decrypt_input)
+                                               (void)(*decrypt_input)(-1);
+#endif /* ENCRYPTION */
+                                       state = TS_CR;
+                               }
+                       }
+                       *pfrontp++ = c;
+                       break;
+
+               case TS_IAC:
+gotiac:                        switch (c) {
+
+                       /*
+                        * Send the process on the pty side an
+                        * interrupt.  Do this with a NULL or
+                        * interrupt char; depending on the tty mode.
+                        */
+                       case IP:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               interrupt();
+                               break;
+
+                       case BREAK:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               sendbrk();
+                               break;
+
+                       /*
+                        * Are You There?
+                        */
+                       case AYT:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               recv_ayt();
+                               break;
+
+                       /*
+                        * Abort Output
+                        */
+                       case AO:
+                           {
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               ptyflush();     /* half-hearted */
+                               init_termbuf();
+
+                               if (slctab[SLC_AO].sptr &&
+                                   *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
+                                   *pfrontp++ =
+                                       (unsigned char)*slctab[SLC_AO].sptr;
+                               }
+
+                               netclear();     /* clear buffer back */
+                               output_data("%c%c", IAC, DM);
+                               neturg = nfrontp - 1; /* off by one XXX */
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: send IAC", DM));
+                               break;
+                           }
+
+                       /*
+                        * Erase Character and
+                        * Erase Line
+                        */
+                       case EC:
+                       case EL:
+                           {
+                               cc_t ch;
+
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               ptyflush();     /* half-hearted */
+                               init_termbuf();
+                               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;
+                           }
+
+                       /*
+                        * Check for urgent data...
+                        */
+                       case DM:
+                               DIAG(TD_OPTIONS,
+                                       printoption("td: recv IAC", c));
+                               SYNCHing = stilloob(net);
+                               settimer(gotDM);
+                               break;
+
+
+                       /*
+                        * Begin option subnegotiation...
+                        */
+                       case SB:
+                               state = TS_SB;
+                               SB_CLEAR();
+                               continue;
+
+                       case WILL:
+                               state = TS_WILL;
+                               continue;
+
+                       case WONT:
+                               state = TS_WONT;
+                               continue;
+
+                       case DO:
+                               state = TS_DO;
+                               continue;
+
+                       case DONT:
+                               state = TS_DONT;
+                               continue;
+                       case EOR:
+                               if (his_state_is_will(TELOPT_EOR))
+                                       doeof();
+                               break;
+
+                       /*
+                        * Handle RFC 10xx Telnet linemode option additions
+                        * to command stream (EOF, SUSP, ABORT).
+                        */
+                       case xEOF:
+                               doeof();
+                               break;
+
+                       case SUSP:
+                               sendsusp();
+                               break;
+
+                       case ABORT:
+                               sendbrk();
+                               break;
+
+                       case IAC:
+                               *pfrontp++ = c;
+                               break;
+                       }
+                       state = TS_DATA;
+                       break;
+
+               case TS_SB:
+                       if (c == IAC) {
+                               state = TS_SE;
+                       } else {
+                               SB_ACCUM(c);
+                       }
+                       break;
+
+               case TS_SE:
+                       if (c != SE) {
+                               if (c != IAC) {
+                                       /*
+                                        * bad form of suboption negotiation.
+                                        * handle it in such a way as to avoid
+                                        * damage to local state.  Parse
+                                        * suboption buffer found so far,
+                                        * then treat remaining stream as
+                                        * another command sequence.
+                                        */
+
+                                       /* for DIAGNOSTICS */
+                                       SB_ACCUM(IAC);
+                                       SB_ACCUM(c);
+                                       subpointer -= 2;
+
+                                       SB_TERM();
+                                       suboption();
+                                       state = TS_IAC;
+                                       goto gotiac;
+                               }
+                               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;
+                       }
+                       break;
+
+               case TS_WILL:
+                       willoption(c);
+                       state = TS_DATA;
+                       continue;
+
+               case TS_WONT:
+                       wontoption(c);
+                       state = TS_DATA;
+                       continue;
+
+               case TS_DO:
+                       dooption(c);
+                       state = TS_DATA;
+                       continue;
+
+               case TS_DONT:
+                       dontoption(c);
+                       state = TS_DATA;
+                       continue;
+
+               default:
+                       syslog(LOG_ERR, "panic state=%d", state);
+                       printf("telnetd: panic state=%d\n", state);
+                       exit(1);
+               }
+       }
+}  /* end of telrcv */
+
+/*
+ * The will/wont/do/dont state machines are based on Dave Borman's
+ * Telnet option processing state machine.
+ *
+ * These correspond to the following states:
+ *     my_state = the last negotiated state
+ *     want_state = what I want the state to go to
+ *     want_resp = how many requests I have sent
+ * 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 {
+ *     want_state = new_state;
+ *     send new_state;
+ *     want_resp++;
+ * }
+ *
+ * When receiving new_state:
+ *
+ * if (want_resp) {
+ *     want_resp--;
+ *     if (want_resp && (new_state == my_state))
+ *             want_resp--;
+ * }
+ * if ((want_resp == 0) && (new_state != want_state)) {
+ *     if (ok_to_switch_to new_state)
+ *             want_state = new_state;
+ *     else
+ *             want_resp++;
+ *     send want_state;
+ * }
+ * my_state = new_state;
+ *
+ * Note that new_state is implied in these functions by the function itself.
+ * will and do imply positive new_state, wont and dont imply negative.
+ *
+ * Finally, there is one catch.  If we send a negative response to a
+ * positive request, my_state will be the positive while want_state will
+ * remain negative.  my_state will revert to negative when the negative
+ * acknowlegment arrives from the peer.  Thus, my_state generally tells
+ * us not only the last negotiated state, but also tells us what the peer
+ * wants to be doing as well.  It is important to understand this difference
+ * as we may wish to be processing data streams based on our desired state
+ * (want_state) or based on what the peer thinks the state is (my_state).
+ *
+ * This all works fine because if the peer sends a positive request, the data
+ * that we receive prior to negative acknowlegment will probably be affected
+ * by the positive state, and we can process it as such (if we can; if we
+ * can't then it really doesn't matter).  If it is that important, then the
+ * peer probably should be buffering until this option state negotiation
+ * is complete.
+ *
+ */
+void
+send_do(int option, int 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) output_data("%c%c%c", IAC, DO, option);
+
+       DIAG(TD_OPTIONS, printoption("td: send do", option));
+}
+
+#ifdef LINEMODE
+extern void doclientstat(void);
+#endif
+#if 0
+#ifdef AUTHENTICATION
+extern void auth_request(void);        /* libtelnet */
+#endif
+#ifdef ENCRYPTION
+extern void encrypt_send_support(void);
+#endif /* ENCRYPTION */
+#endif
+
+void
+willoption(int option)
+{
+       int changeok = 0;
+       void (*func)(void) = 0;
+
+       /*
+        * process input from peer.
+        */
+
+       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:
+                       init_termbuf();
+                       tty_binaryin(1);
+                       set_termbuf();
+                       changeok++;
+                       break;
+
+               case TELOPT_ECHO:
+                       /*
+                        * See comments below for more info.
+                        */
+                       not42 = 0;      /* looks like a 4.2 system */
+                       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.
+                        */
+                       /*
+                        * Handle the linemode kludge stuff.
+                        * If we are not currently supporting any
+                        * linemode at all, then we assume that this
+                        * is the client telling us to use kludge
+                        * linemode in response to our query.  Set the
+                        * linemode type that is to be supported, note
+                        * that the client wishes to use linemode, and
+                        * eat the will TM as though it never arrived.
+                        */
+                       if (lmodetype < KLUDGE_LINEMODE) {
+                               lmodetype = KLUDGE_LINEMODE;
+                               clientstat(TELOPT_LINEMODE, WILL, 0);
+                               send_wont(TELOPT_SGA, 1);
+                       } else if (lmodetype == NO_AUTOKLUDGE) {
+                               lmodetype = KLUDGE_OK;
+                       }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       /*
+                        * We never respond to a WILL TM, and
+                        * we leave the state WONT.
+                        */
+                       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.
+                        */
+                       slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
+                       slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
+                       slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
+                       slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
+               case TELOPT_TTYPE:
+               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        KLUDGELINEMODE
+                       /*
+                        * Note client's desire to use linemode.
+                        */
+                       lmodetype = REAL_LINEMODE;
+# endif        /* KLUDGELINEMODE */
+                       func = doclientstat;
+                       changeok++;
+                       break;
+#endif /* LINEMODE */
+
+#ifdef AUTHENTICATION
+               case TELOPT_AUTHENTICATION:
+                       func = auth_request;
+                       changeok++;
+                       break;
+#endif
+
+#ifdef ENCRYPTION
+               case TELOPT_ENCRYPT:
+                       func = encrypt_send_support;
+                       changeok++;
+                       break;
+#endif /* ENCRYPTION */
+
+               default:
+                       break;
+               }
+               if (changeok) {
+                       set_his_want_state_will(option);
+                       send_do(option, 0);
+               } else {
+                       do_dont_resp[option]++;
+                       send_dont(option, 0);
+               }
+           } else {
+               /*
+                * Option processing that should happen when
+                * we receive conformation of a change in
+                * state that we had requested.
+                */
+               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
+                       /*
+                        * Note client's desire to use linemode.
+                        */
+                       lmodetype = REAL_LINEMODE;
+# endif        /* KLUDGELINEMODE */
+                       func = doclientstat;
+                       break;
+#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;
+               }
+           }
+       }
+       set_his_state_will(option);
+       if (func)
+               (*func)();
+}  /* end of willoption */
+
+void
+send_dont(int option, int init)
+{
+       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) output_data("%c%c%c", IAC, DONT, option);
+
+       DIAG(TD_OPTIONS, printoption("td: send dont", option));
+}
+
+void
+wontoption(int option)
+{
+       /*
+        * Process client input.
+        */
+
+       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:
+                       not42 = 1; /* doesn't seem to be a 4.2 system */
+                       break;
+
+               case TELOPT_BINARY:
+                       init_termbuf();
+                       tty_binaryin(0);
+                       set_termbuf();
+                       break;
+
+#ifdef LINEMODE
+               case TELOPT_LINEMODE:
+# ifdef        KLUDGELINEMODE
+                       /*
+                        * If real linemode is supported, then client is
+                        * asking to turn linemode off.
+                        */
+                       if (lmodetype != REAL_LINEMODE)
+                               break;
+                       /* XXX double-check this --thorpej */
+                       lmodetype = KLUDGE_LINEMODE;
+# endif        /* KLUDGELINEMODE */
+                       clientstat(TELOPT_LINEMODE, WONT, 0);
+                       break;
+#endif /* LINEMODE */
+
+               case TELOPT_TM:
+                       /*
+                        * 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
+                        * achieve this.
+                        */
+                       set_his_want_state_wont(TELOPT_TM);
+                       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.
+                        */
+                       slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
+                       slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
+                       slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
+                       slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
+                       break;
+
+#ifdef 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;
+
+               case TELOPT_TSPEED:
+                       settimer(tspeedsubopt);
+                       break;
+
+               case TELOPT_XDISPLOC:
+                       settimer(xdisplocsubopt);
+                       break;
+
+               case TELOPT_OLD_ENVIRON:
+                       settimer(oenvironsubopt);
+                       break;
+
+               case TELOPT_NEW_ENVIRON:
+                       settimer(environsubopt);
+                       break;
+
+               default:
+                       break;
+               }
+               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;
+
+#ifdef AUTHENTICATION
+               case TELOPT_AUTHENTICATION:
+                       auth_finished(0, AUTH_REJECT);
+                       break;
+#endif
+               default:
+                       break;
+               }
+           }
+       }
+       set_his_state_wont(option);
+
+}  /* end of wontoption */
+
+void
+send_will(int option, int 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) output_data("%c%c%c", IAC, WILL, option);
+
+       DIAG(TD_OPTIONS, printoption("td: send will", option));
+}
+
+#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(int option)
+{
+       int changeok = 0;
+
+       /*
+        * Process client input.
+        */
+
+       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
+# ifdef        KLUDGELINEMODE
+                       if (lmodetype == NO_LINEMODE)
+# else
+                       if (his_state_is_wont(TELOPT_LINEMODE))
+# endif
+#endif
+                       {
+                               init_termbuf();
+                               tty_setecho(1);
+                               set_termbuf();
+                       }
+                       changeok++;
+                       break;
+
+               case TELOPT_BINARY:
+                       init_termbuf();
+                       tty_binaryout(1);
+                       set_termbuf();
+                       changeok++;
+                       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 (lmodetype == KLUDGE_LINEMODE) {
+                               /*
+                                * 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);
+                               /*
+                                * 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;
+                       }
+#else
+                       turn_on_sga = 0;
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       changeok++;
+                       break;
+
+               case TELOPT_STATUS:
+                       changeok++;
+                       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_XDISPLOC:
+               case TELOPT_OLD_ENVIRON:
+               default:
+                       break;
+               }
+               if (changeok) {
+                       set_my_want_state_will(option);
+                       send_will(option, 0);
+               } else {
+                       will_wont_resp[option]++;
+                       send_wont(option, 0);
+               }
+       }
+       set_my_state_will(option);
+
+}  /* end of dooption */
+
+void
+send_wont(int option, int init)
+{
+       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) output_data("%c%c%c", IAC, WONT, option);
+
+       DIAG(TD_OPTIONS, printoption("td: send wont", option));
+}
+
+void
+dontoption(int option)
+{
+       /*
+        * Process client input.
+        */
+
+
+       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();
+                       tty_binaryout(0);
+                       set_termbuf();
+                       break;
+
+               case TELOPT_ECHO:       /* we should stop echoing */
+#ifdef LINEMODE
+# ifdef        KLUDGELINEMODE
+                       if ((lmodetype != REAL_LINEMODE) &&
+                           (lmodetype != KLUDGE_LINEMODE))
+# else
+                       if (his_state_is_wont(TELOPT_LINEMODE))
+# endif
+#endif
+                       {
+                               init_termbuf();
+                               tty_setecho(0);
+                               set_termbuf();
+                       }
+                       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 ((lmodetype == KLUDGE_LINEMODE) ||
+                           (lmodetype == KLUDGE_OK)) {
+                               /*
+                                * The client is asking us to turn
+                                * linemode on.
+                                */
+                               lmodetype = KLUDGE_LINEMODE;
+                               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.
+                                */
+                       }
+                       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;
+               }
+
+               set_my_want_state_wont(option);
+               if (my_state_is_will(option))
+                       send_wont(option, 0);
+       }
+       set_my_state_wont(option);
+
+}  /* 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 */
+
+/* envvarok(char*) */
+/* check that variable is safe to pass to login or shell */
+static int
+envvarok(char *varp)
+{
+
+       if (strcmp(varp, "TERMCAP") &&  /* to prevent a security hole */
+           strcmp(varp, "TERMINFO") && /* with tgetent */
+           strcmp(varp, "TERMPATH") &&
+           strcmp(varp, "HOME") &&     /* to prevent the tegetent bug  */
+           strncmp(varp, "LD_", strlen("LD_")) &&      /* most systems */
+           strncmp(varp, "_RLD_", strlen("_RLD_")) &&  /* IRIX */
+           strcmp(varp, "LIBPATH") &&                  /* AIX */
+           strcmp(varp, "ENV") &&
+           strcmp(varp, "BASH_ENV") &&
+           strcmp(varp, "IFS") &&
+           strncmp(varp, "KRB5", strlen("KRB5")) &&    /* Krb5 */
+           /*
+            * The above case is a catch-all for now.  Here are some of
+            * the specific ones we must avoid passing, at least until
+            * we can prove it can be done safely.  Keep this list
+            * around un case someone wants to remove the catch-all.
+            */
+           strcmp(varp, "KRB5_CONFIG") &&              /* Krb5 */
+           strcmp(varp, "KRB5CCNAME") &&               /* Krb5 */
+           strcmp(varp, "KRB5_KTNAME") &&              /* Krb5 */
+           strcmp(varp, "KRBTKFILE") &&                /* Krb4 */
+           strcmp(varp, "KRB_CONF") &&                 /* CNS 4 */
+           strcmp(varp, "KRB_REALMS") &&               /* CNS 4 */
+           strcmp(varp, "RESOLV_HOST_CONF"))           /* Linux */
+               return (1);
+       else {
+               syslog(LOG_INFO, "Rejected the attempt to modify the "
+                   "environment variable \"%s\"", varp);
+               return (0);
+       }
+}
+
+/*
+ * suboption()
+ *
+ *     Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ *     Currently we recognize:
+ *
+ *     Terminal type is
+ *     Linemode
+ *     Window size
+ *     Terminal speed
+ */
+void
+suboption(void)
+{
+    int subchar;
+
+    DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
+
+    subchar = SB_GET();
+    switch (subchar) {
+    case TELOPT_TSPEED: {
+       int xspeed, rspeed;
+
+       if (his_state_is_wont(TELOPT_TSPEED))   /* Ignore if option disabled */
+               break;
+
+       settimer(tspeedsubopt);
+
+       if (SB_EOF() || SB_GET() != TELQUAL_IS)
+               return;
+
+       xspeed = atoi((char *)subpointer);
+
+       while (SB_GET() != ',' && !SB_EOF());
+       if (SB_EOF())
+               return;
+
+       rspeed = atoi((char *)subpointer);
+       clientstat(TELOPT_TSPEED, xspeed, rspeed);
+
+       break;
+
+    }  /* end of case TELOPT_TSPEED */
+
+    case TELOPT_TTYPE: {               /* Yaaaay! */
+       char *p;
+
+       if (his_state_is_wont(TELOPT_TTYPE))    /* Ignore if option disabled */
+               break;
+       settimer(ttypesubopt);
+
+       if (SB_EOF() || SB_GET() != TELQUAL_IS) {
+           return;             /* ??? XXX but, this is the most robust */
+       }
+
+       p = terminaltype;
+
+       while ((p < (terminaltype + sizeof terminaltype-1)) &&
+                                                                   !SB_EOF()) {
+           int c;
+
+           c = SB_GET();
+           if (isupper(c)) {
+               c = tolower(c);
+           }
+           *p++ = c;    /* accumulate name */
+       }
+       *p = 0;
+       break;
+    }  /* end of case TELOPT_TTYPE */
+
+    case TELOPT_NAWS: {
+       int xwinsize, ywinsize;
+
+       if (his_state_is_wont(TELOPT_NAWS))     /* Ignore if option disabled */
+               break;
+
+       if (SB_EOF())
+               return;
+       xwinsize = SB_GET() << 8;
+       if (SB_EOF())
+               return;
+       xwinsize |= SB_GET();
+       if (SB_EOF())
+               return;
+       ywinsize = SB_GET() << 8;
+       if (SB_EOF())
+               return;
+       ywinsize |= SB_GET();
+       clientstat(TELOPT_NAWS, xwinsize, ywinsize);
+
+       break;
+
+    }  /* end of case TELOPT_NAWS */
+
+#ifdef LINEMODE
+    case TELOPT_LINEMODE: {
+       int request;
+
+       if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */
+               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 (request == LM_SLC) {  /* SLC is not preceded by WILL or WONT */
+               /*
+                * Process suboption buffer of slc's
+                */
+               start_slc(1);
+               do_opt_slc(subpointer, SB_LEN());
+               (void) end_slc(0);
+               break;
+       } else if (request == LM_MODE) {
+               if (SB_EOF())
+                   return;
+               useeditmode = SB_GET();  /* get mode flag */
+               clientstat(LM_MODE, 0, 0);
+               break;
+       }
+
+       if (SB_EOF())
+           break;
+       switch (SB_GET()) {  /* what suboption? */
+       case LM_FORWARDMASK:
+               /*
+                * According to spec, only server can send request for
+                * forwardmask, and client can only return a positive response.
+                * So don't worry about it.
+                */
+
+       default:
+               break;
+       }
+       break;
+    }  /* end of case TELOPT_LINEMODE */
+#endif
+    case TELOPT_STATUS: {
+       int mode;
+
+       if (SB_EOF())
+           break;
+       mode = SB_GET();
+       switch (mode) {
+       case TELQUAL_SEND:
+           if (my_state_is_will(TELOPT_STATUS))
+               send_status();
+           break;
+
+       case TELQUAL_IS:
+           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 */
+
+    case TELOPT_NEW_ENVIRON:
+    case TELOPT_OLD_ENVIRON: {
+       int c;
+       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;
+       }
+
+       if (subchar == TELOPT_NEW_ENVIRON) {
+           while (!SB_EOF()) {
+               c = SB_GET();
+               if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
+                       break;
+           }
+       } else
+       {
+#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) {
+               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, {output_data(
+                               "ENVIRON VALUE and VAR are reversed!\r\n");});
+
+               }
+           }
+           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 (envvarok(varp)) {
+                               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 (envvarok(varp)) {
+               if (valp)
+                       (void)setenv(varp, valp, 1);
+               else
+                       unsetenv(varp);
+       }
+       break;
+    }  /* end of case TELOPT_NEW_ENVIRON */
+#ifdef 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;
+    }  /* end of switch */
+
+}  /* end of suboption */
+
+#ifdef LINEMODE
+void
+doclientstat(void)
+{
+       clientstat(TELOPT_LINEMODE, WILL, 0);
+}
+#endif /* LINEMODE */
+
+void
+send_status(void)
+{
+#define        ADD(c) \
+       do { \
+               if (ep > ncp) \
+                       *ncp++ = c; \
+               else \
+                       goto trunc; \
+       } while (0)
+#define        ADD_DATA(c) \
+       do { \
+               ADD(c); if (c == SE || c == IAC) ADD(c); \
+       } while (0)
+
+       unsigned char statusbuf[256];
+       unsigned char *ep;
+       unsigned char *ncp;
+       unsigned char i;
+
+       ncp = statusbuf;
+       ep = statusbuf + sizeof(statusbuf);
+
+       netflush();     /* get rid of anything waiting to go out */
+
+       ADD(IAC);
+       ADD(SB);
+       ADD(TELOPT_STATUS);
+       ADD(TELQUAL_IS);
+
+       /*
+        * 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);
+               }
+               if (his_want_state_is_will(i)) {
+                       ADD(DO);
+                       ADD_DATA(i);
+               }
+       }
+
+       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
+       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);
+               ADD(SE);
+
+               ADD(SB);
+               ADD(TELOPT_LINEMODE);
+               ADD(LM_SLC);
+               start_slc(0);
+               send_slc();
+               len = end_slc(&cp);
+               for (cpe = cp + len; cp < cpe; cp++)
+                       ADD_DATA(*cp);
+               ADD(SE);
+       }
+#endif /* LINEMODE */
+
+       ADD(IAC);
+       ADD(SE);
+
+       writenet(statusbuf, ncp - statusbuf);
+       netflush();     /* Send it on its way */
+
+       DIAG(TD_OPTIONS,
+               {printsub('>', statusbuf, ncp - statusbuf); netflush();});
+       return;
+
+trunc:
+       /* XXX bark? */
+       return;
+#undef ADD
+#undef ADD_DATA
+}
+
+int
+output_data(const char *format, ...)
+{
+       va_list args;
+       size_t remaining, ret;
+
+       va_start(args, format);
+       remaining = BUFSIZ - (nfrontp - netobuf);
+       /* try a netflush() if the room is too low */
+       if (strlen(format) > remaining || BUFSIZ / 4 > remaining) {
+               netflush();
+               remaining = BUFSIZ - (nfrontp - netobuf);
+       }
+       ret = vsnprintf(nfrontp, remaining, format, args);
+       nfrontp += ((ret < remaining - 1) ? ret : remaining - 1);
+       va_end(args);
+       return ret;
+}
+
+int
+output_datalen(const char *buf, size_t l)
+{
+       size_t remaining;
+
+       remaining = BUFSIZ - (nfrontp - netobuf);
+       if (remaining < l) {
+               netflush();
+               remaining = BUFSIZ - (nfrontp - netobuf);
+       }
+       if (remaining < l)
+               return -1;
+       memmove(nfrontp, buf, l);
+       nfrontp += l;
+       return (int)l;
+}
diff --git a/libexec/telnetd/sys_term.c b/libexec/telnetd/sys_term.c
new file mode 100644 (file)
index 0000000..f4a91e8
--- /dev/null
@@ -0,0 +1,798 @@
+/*     $NetBSD: sys_term.c,v 1.47 2013/06/28 15:48:02 christos Exp $   */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)sys_term.c 8.4+1 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: sys_term.c,v 1.47 2013/06/28 15:48:02 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include "telnetd.h"
+#include "pathnames.h"
+
+#include <util.h>
+#include <vis.h>
+
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
+
+#define SCPYN(a, b)    (void) strncpy(a, b, sizeof(a))
+#define SCMPN(a, b)    strncmp(a, b, sizeof(a))
+
+struct termios termbuf, termbuf2;      /* pty control structure */
+
+void getptyslave(void);
+int cleanopen(char *);
+char **addarg(char **, const char *);
+void scrub_env(void);
+int getent(char *, char *);
+char *getstr(const char *, char **);
+#ifdef KRB5
+extern void kerberos5_cleanup(void);
+#endif
+
+/*
+ * init_termbuf()
+ * copy_termbuf(cp)
+ * set_termbuf()
+ *
+ * These three routines are used to get and set the "termbuf" structure
+ * to and from the kernel.  init_termbuf() gets the current settings.
+ * copy_termbuf() hands in a new "termbuf" to write to the kernel, and
+ * set_termbuf() writes the structure into the kernel.
+ */
+
+void
+init_termbuf(void)
+{
+       (void) tcgetattr(pty, &termbuf);
+       termbuf2 = termbuf;
+}
+
+#if    defined(LINEMODE) && defined(TIOCPKT_IOCTL)
+void
+copy_termbuf(char *cp, int len)
+{
+       if ((size_t)len > sizeof(termbuf))
+               len = sizeof(termbuf);
+       memmove((char *)&termbuf, cp, len);
+       termbuf2 = termbuf;
+}
+#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
+
+void
+set_termbuf(void)
+{
+       /*
+        * Only make the necessary changes.
+        */
+       if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
+               (void) tcsetattr(pty, TCSANOW, &termbuf);
+}
+
+
+/*
+ * spcset(func, valp, valpp)
+ *
+ * This function takes various special characters (func), and
+ * sets *valp to the current value of that character, and
+ * *valpp to point to where in the "termbuf" structure that
+ * value is kept.
+ *
+ * It returns the SLC_ level of support for this function.
+ */
+
+
+int
+spcset(int func, cc_t *valp, cc_t **valpp)
+{
+
+#define        setval(a, b)    *valp = termbuf.c_cc[a]; \
+                       *valpp = &termbuf.c_cc[a]; \
+                       return(b);
+#define        defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
+
+       switch(func) {
+       case SLC_EOF:
+               setval(VEOF, SLC_VARIABLE);
+       case SLC_EC:
+               setval(VERASE, SLC_VARIABLE);
+       case SLC_EL:
+               setval(VKILL, SLC_VARIABLE);
+       case SLC_IP:
+               setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+       case SLC_ABORT:
+               setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+       case SLC_XON:
+               setval(VSTART, SLC_VARIABLE);
+       case SLC_XOFF:
+               setval(VSTOP, SLC_VARIABLE);
+       case SLC_EW:
+               setval(VWERASE, SLC_VARIABLE);
+       case SLC_RP:
+               setval(VREPRINT, SLC_VARIABLE);
+       case SLC_LNEXT:
+               setval(VLNEXT, SLC_VARIABLE);
+       case SLC_AO:
+               setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
+       case SLC_SUSP:
+               setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
+       case SLC_FORW1:
+               setval(VEOL, SLC_VARIABLE);
+       case SLC_FORW2:
+               setval(VEOL2, SLC_VARIABLE);
+       case SLC_AYT:
+               setval(VSTATUS, SLC_VARIABLE);
+
+       case SLC_BRK:
+       case SLC_SYNCH:
+       case SLC_EOR:
+               defval(0);
+
+       default:
+               *valp = 0;
+               *valpp = 0;
+               return(SLC_NOSUPPORT);
+       }
+}
+
+
+/*
+ * getpty()
+ *
+ * Allocate a pty.  As a side effect, the external character
+ * array "line" contains the name of the slave side.
+ *
+ * Returns the file descriptor of the opened pty.
+ */
+#ifndef        __GNUC__
+char *line = NULL16STR;
+#else
+static char Xline[] = NULL16STR;
+char *line = Xline;
+#endif
+
+
+static int ptyslavefd; /* for cleanopen() */
+
+int
+getpty(int *ptynum)
+{
+       int ptyfd;
+
+       ptyfd = openpty(ptynum, &ptyslavefd, line, NULL, NULL);
+       if (ptyfd == 0)
+               return *ptynum;
+       ptyslavefd = -1;
+       return (-1);
+}
+
+#ifdef LINEMODE
+/*
+ * tty_flowmode()      Find out if flow control is enabled or disabled.
+ * tty_linemode()      Find out if linemode (external processing) is enabled.
+ * tty_setlinemod(on)  Turn on/off linemode.
+ * tty_isecho()                Find out if echoing is turned on.
+ * tty_setecho(on)     Enable/disable character echoing.
+ * tty_israw()         Find out if terminal is in RAW mode.
+ * tty_binaryin(on)    Turn on/off BINARY on input.
+ * tty_binaryout(on)   Turn on/off BINARY on output.
+ * tty_isediting()     Find out if line editing is enabled.
+ * tty_istrapsig()     Find out if signal trapping is enabled.
+ * tty_setedit(on)     Turn on/off line editing.
+ * tty_setsig(on)      Turn on/off signal trapping.
+ * tty_issofttab()     Find out if tab expansion is enabled.
+ * tty_setsofttab(on)  Turn on/off soft tab expansion.
+ * tty_islitecho()     Find out if typed control chars are echoed literally
+ * tty_setlitecho()    Turn on/off literal echo of control chars
+ * tty_tspeed(val)     Set transmit speed to val.
+ * tty_rspeed(val)     Set receive speed to val.
+ */
+
+
+int
+tty_linemode(void)
+{
+       return(termbuf.c_lflag & EXTPROC);
+}
+
+void
+tty_setlinemode(int on)
+{
+       set_termbuf();
+       (void) ioctl(pty, TIOCEXT, (char *)&on);
+       init_termbuf();
+}
+#endif /* LINEMODE */
+
+int
+tty_isecho(void)
+{
+       return (termbuf.c_lflag & ECHO);
+}
+
+int
+tty_flowmode(void)
+{
+       return((termbuf.c_iflag & IXON) ? 1 : 0);
+}
+
+int
+tty_restartany(void)
+{
+       return((termbuf.c_iflag & IXANY) ? 1 : 0);
+}
+
+void
+tty_setecho(int on)
+{
+       if (on)
+               termbuf.c_lflag |= ECHO;
+       else
+               termbuf.c_lflag &= ~ECHO;
+}
+
+int
+tty_israw(void)
+{
+       return(!(termbuf.c_lflag & ICANON));
+}
+
+void
+tty_binaryin(int on)
+{
+       if (on) {
+               termbuf.c_iflag &= ~ISTRIP;
+       } else {
+               termbuf.c_iflag |= ISTRIP;
+       }
+}
+
+void
+tty_binaryout(int on)
+{
+       if (on) {
+               termbuf.c_cflag &= ~(CSIZE|PARENB);
+               termbuf.c_cflag |= CS8;
+               termbuf.c_oflag &= ~OPOST;
+       } else {
+               termbuf.c_cflag &= ~CSIZE;
+               termbuf.c_cflag |= CS7|PARENB;
+               termbuf.c_oflag |= OPOST;
+       }
+}
+
+int
+tty_isbinaryin(void)
+{
+       return(!(termbuf.c_iflag & ISTRIP));
+}
+
+int
+tty_isbinaryout(void)
+{
+       return(!(termbuf.c_oflag&OPOST));
+}
+
+#ifdef LINEMODE
+int
+tty_isediting(void)
+{
+       return(termbuf.c_lflag & ICANON);
+}
+
+int
+tty_istrapsig(void)
+{
+       return(termbuf.c_lflag & ISIG);
+}
+
+void
+tty_setedit(int on)
+{
+       if (on)
+               termbuf.c_lflag |= ICANON;
+       else
+               termbuf.c_lflag &= ~ICANON;
+}
+
+void
+tty_setsig(int on)
+{
+       if (on)
+               termbuf.c_lflag |= ISIG;
+       else
+               termbuf.c_lflag &= ~ISIG;
+}
+#endif /* LINEMODE */
+
+int
+tty_issofttab(void)
+{
+# ifdef        OXTABS
+       return (termbuf.c_oflag & OXTABS);
+# endif
+# ifdef        TABDLY
+       return ((termbuf.c_oflag & TABDLY) == TAB3);
+# endif
+}
+
+void
+tty_setsofttab(int on)
+{
+       if (on) {
+# ifdef        OXTABS
+               termbuf.c_oflag |= OXTABS;
+# endif
+# ifdef        TABDLY
+               termbuf.c_oflag &= ~TABDLY;
+               termbuf.c_oflag |= TAB3;
+# endif
+       } else {
+# ifdef        OXTABS
+               termbuf.c_oflag &= ~OXTABS;
+# endif
+# ifdef        TABDLY
+               termbuf.c_oflag &= ~TABDLY;
+               termbuf.c_oflag |= TAB0;
+# endif
+       }
+}
+
+int
+tty_islitecho(void)
+{
+# ifdef        ECHOCTL
+       return (!(termbuf.c_lflag & ECHOCTL));
+# endif
+# ifdef        TCTLECH
+       return (!(termbuf.c_lflag & TCTLECH));
+# endif
+# if   !defined(ECHOCTL) && !defined(TCTLECH)
+       return (0);     /* assumes ctl chars are echoed '^x' */
+# endif
+}
+
+void
+tty_setlitecho(int on)
+{
+# ifdef        ECHOCTL
+       if (on)
+               termbuf.c_lflag &= ~ECHOCTL;
+       else
+               termbuf.c_lflag |= ECHOCTL;
+# endif
+# ifdef        TCTLECH
+       if (on)
+               termbuf.c_lflag &= ~TCTLECH;
+       else
+               termbuf.c_lflag |= TCTLECH;
+# endif
+}
+
+int
+tty_iscrnl(void)
+{
+       return (termbuf.c_iflag & ICRNL);
+}
+
+void
+tty_tspeed(int val)
+{
+       cfsetospeed(&termbuf, val);
+}
+
+void
+tty_rspeed(int val)
+{
+       cfsetispeed(&termbuf, val);
+}
+
+
+
+
+/*
+ * getptyslave()
+ *
+ * Open the slave side of the pty, and do any initialization
+ * that is necessary.  The return value is a file descriptor
+ * for the slave side.
+ */
+extern int def_tspeed, def_rspeed;
+       extern int def_row, def_col;
+
+void
+getptyslave(void)
+{
+       int t = -1;
+
+#ifdef LINEMODE
+       int waslm;
+#endif
+       struct winsize ws;
+       /*
+        * Opening the slave side may cause initilization of the
+        * kernel tty structure.  We need remember the state of
+        *      if linemode was turned on
+        *      terminal window size
+        *      terminal speed
+        * so that we can re-set them if we need to.
+        */
+#ifdef LINEMODE
+       waslm = tty_linemode();
+#endif
+
+       /*
+        * Make sure that we don't have a controlling tty, and
+        * that we are the session (process group) leader.
+        */
+       t = open(_PATH_TTY, O_RDWR);
+       if (t >= 0) {
+               (void) ioctl(t, TIOCNOTTY, (char *)0);
+               (void) close(t);
+       }
+
+
+
+       t = cleanopen(line);
+       if (t < 0)
+               fatalperror(net, line);
+
+
+       /*
+        * set up the tty modes as we like them to be.
+        */
+       init_termbuf();
+       if (def_row || def_col) {
+               memset((char *)&ws, 0, sizeof(ws));
+               ws.ws_col = def_col;
+               ws.ws_row = def_row;
+               (void)ioctl(t, TIOCSWINSZ, (char *)&ws);
+       }
+
+       /*
+        * Settings for sgtty based systems
+        */
+
+       /*
+        * Settings for all other termios/termio based
+        * systems, other than 4.4BSD.  In 4.4BSD the
+        * kernel does the initial terminal setup.
+        */
+       tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
+       tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
+#ifdef LINEMODE
+       if (waslm)
+               tty_setlinemode(1);
+#endif /* LINEMODE */
+
+       /*
+        * Set the tty modes, and make this our controlling tty.
+        */
+       set_termbuf();
+       if (login_tty(t) == -1)
+               fatalperror(net, "login_tty");
+       if (net > 2)
+               (void) close(net);
+       if (pty > 2) {
+               (void) close(pty);
+               pty = -1;
+       }
+}
+
+/*
+ * Open the specified slave side of the pty,
+ * making sure that we have a clean tty.
+ */
+int
+cleanopen(char *ttyline)
+{
+       return ptyslavefd;
+}
+
+/*
+ * startslave(host)
+ *
+ * Given a hostname, do whatever
+ * is necessary to startup the login process on the slave side of the pty.
+ */
+
+/* ARGSUSED */
+void
+startslave(char *host, int autologin, char *autoname)
+{
+       int i;
+
+#ifdef AUTHENTICATION
+       if (!autoname || !autoname[0])
+               autologin = 0;
+
+       if (autologin < auth_level) {
+               fatal(net, "Authorization failed");
+               exit(1);
+       }
+#endif
+
+
+       if ((i = fork()) < 0)
+               fatalperror(net, "fork");
+       if (i) {
+       } else {
+               getptyslave();
+               start_login(host, autologin, autoname);
+               /*NOTREACHED*/
+       }
+}
+
+char   *envinit[3];
+
+void
+init_env(void)
+{
+       char **envp;
+
+       envp = envinit;
+       if ((*envp = getenv("TZ")))
+               *envp++ -= 3;
+       *envp = 0;
+       environ = envinit;
+}
+
+
+/*
+ * start_login(host)
+ *
+ * Assuming that we are now running as a child processes, this
+ * function will turn us into the login process.
+ */
+extern char *gettyname;
+
+void
+start_login(char *host, int autologin, char *name)
+{
+       char **argv;
+#define        TABBUFSIZ       512
+       char    defent[TABBUFSIZ];
+       char    defstrs[TABBUFSIZ];
+#undef TABBUFSIZ
+       const char *loginprog = NULL;
+       extern struct sockaddr_storage from;
+       char buf[sizeof(from) * 4 + 1];
+
+       scrub_env();
+
+       /*
+        * -a : pass on the address of the host.
+        * -h : pass on name of host.
+        *      WARNING:  -h and -a are accepted by login
+        *      if and only if getuid() == 0.
+        * -p : don't clobber the environment (so terminal type stays set).
+        *
+        * -f : force this login, he has already been authenticated
+        */
+       argv = addarg(0, "login");
+
+       argv = addarg(argv, "-a");
+       (void)strvisx(buf, (const char *)(const void *)&from, sizeof(from),
+           VIS_WHITE);
+       argv = addarg(argv, buf);
+
+       argv = addarg(argv, "-h");
+       argv = addarg(argv, host);
+
+       argv = addarg(argv, "-p");
+#ifdef LINEMODE
+       /*
+        * Set the environment variable "LINEMODE" to either
+        * "real" or "kludge" if we are operating in either
+        * real or kludge linemode.
+        */
+       if (lmodetype == REAL_LINEMODE)
+               setenv("LINEMODE", "real", 1);
+# ifdef KLUDGELINEMODE
+       else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK)
+               setenv("LINEMODE", "kludge", 1);
+# endif
+#endif
+#ifdef SECURELOGIN
+       /*
+        * don't worry about the -f that might get sent.
+        * A -s is supposed to override it anyhow.
+        */
+       if (require_secure_login)
+               argv = addarg(argv, "-s");
+#endif
+#ifdef AUTHENTICATION
+       if (auth_level >= 0 && autologin == AUTH_VALID) {
+               argv = addarg(argv, "-f");
+               argv = addarg(argv, "--");
+               argv = addarg(argv, name);
+       } else
+#endif
+       if (getenv("USER")) {
+               argv = addarg(argv, "--");
+               argv = addarg(argv, getenv("USER"));
+               /*
+                * Assume that login will set the USER variable
+                * correctly.  For SysV systems, this means that
+                * USER will no longer be set, just LOGNAME by
+                * login.  (The problem is that if the auto-login
+                * fails, and the user then specifies a different
+                * account name, he can get logged in with both
+                * LOGNAME and USER in his environment, but the
+                * USER value will be wrong.
+                */
+               unsetenv("USER");
+       }
+        if (getent(defent, gettyname) == 1) {
+                char *cp = defstrs;
+
+                loginprog = getstr("lo", &cp);
+        }
+        if (loginprog == NULL)
+                loginprog = _PATH_LOGIN;
+       closelog();
+       /*
+        * This sleep(1) is in here so that telnetd can
+        * finish up with the tty.  There's a race condition
+        * the login banner message gets lost...
+        */
+       sleep(1);
+        execv(loginprog, argv);
+
+        syslog(LOG_ERR, "%s: %m", loginprog);
+        fatalperror(net, loginprog);
+       /*NOTREACHED*/
+}
+
+char **
+addarg(char **argv, const char *val)
+{
+       char **cpp;
+       char **nargv;
+
+       if (argv == NULL) {
+               /*
+                * 10 entries, a leading length, and a null
+                */
+               argv = malloc(sizeof(*argv) * 12);
+               if (argv == NULL)
+                       return(NULL);
+               *argv++ = (char *)10;
+               *argv = (char *)0;
+       }
+       for (cpp = argv; *cpp; cpp++)
+               ;
+       if (cpp == &argv[(long)argv[-1]]) {
+               --argv;
+               nargv = realloc(argv, sizeof(*argv) * ((long)(*argv) + 10 + 2));
+               if (nargv == NULL) {
+                       fatal(net, "not enough memory");
+                       /*NOTREACHED*/
+               }
+               argv = nargv;
+               *argv = (char *)((long)(*argv) + 10);
+               argv++;
+               cpp = &argv[(long)argv[-1] - 10];
+       }
+       *cpp++ = __UNCONST(val);
+       *cpp = 0;
+       return(argv);
+}
+
+/*
+ * scrub_env()
+ *
+ * We only accept the environment variables listed below.
+ */
+
+void
+scrub_env(void)
+{
+       static const char *reject[] = {
+               "TERMCAP=/",
+               NULL
+       };
+
+       static const char *acceptstr[] = {
+               "XAUTH=", "XAUTHORITY=", "DISPLAY=",
+               "TERM=",
+               "EDITOR=",
+               "PAGER=",
+               "LOGNAME=",
+               "POSIXLY_CORRECT=",
+               "TERMCAP=",
+               "PRINTER=",
+               NULL
+       };
+
+       char **cpp, **cpp2;
+       const char **p;
+
+       for (cpp2 = cpp = environ; *cpp; cpp++) {
+               int reject_it = 0;
+
+               for(p = reject; *p; p++)
+                       if(strncmp(*cpp, *p, strlen(*p)) == 0) {
+                               reject_it = 1;
+                               break;
+                       }
+               if (reject_it)
+                       continue;
+
+               for(p = acceptstr; *p; p++)
+                       if(strncmp(*cpp, *p, strlen(*p)) == 0)
+                               break;
+               if(*p != NULL)
+                       *cpp2++ = *cpp;
+       }
+       *cpp2 = NULL;
+}
+
+/*
+ * cleanup()
+ *
+ * This is the routine to call when we are all through, to
+ * clean up anything that needs to be cleaned up.
+ */
+/* ARGSUSED */
+void
+cleanup(int sig)
+{
+       char *p, c;
+
+       p = line + sizeof(_PATH_DEV) - 1;
+#ifdef SUPPORT_UTMP
+       if (logout(p))
+               logwtmp(p, "", "");
+#endif
+#ifdef SUPPORT_UTMPX
+       if (logoutx(p, 0, DEAD_PROCESS))
+               logwtmpx(p, "", "", 0, DEAD_PROCESS);
+#endif
+       (void)chmod(line, 0666);
+       (void)chown(line, 0, 0);
+       c = *p; *p = 'p';
+       (void)chmod(line, 0666);
+       (void)chown(line, 0, 0);
+       *p = c;
+       if (ttyaction(line, "telnetd", "root"))
+               syslog(LOG_ERR, "%s: ttyaction failed", line);
+       (void) shutdown(net, 2);
+       exit(1);
+}
diff --git a/libexec/telnetd/telnetd.8 b/libexec/telnetd/telnetd.8
new file mode 100644 (file)
index 0000000..96d3ff9
--- /dev/null
@@ -0,0 +1,591 @@
+.\"    $NetBSD: telnetd.8,v 1.31 2009/04/08 13:36:32 joerg Exp $
+.\"
+.\" Copyright (c) 1983, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" 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. 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.
+.\"
+.\"    from: @(#)telnetd.8     8.3 (Berkeley) 3/1/94
+.\"
+.Dd July 17, 2004
+.Dt TELNETD 8
+.Os
+.Sh NAME
+.Nm telnetd
+.Nd DARPA
+.Tn TELNET
+protocol server
+.Sh SYNOPSIS
+.Nm /usr/libexec/telnetd
+.Op Fl Uhlkns46
+.Op Fl D Ar debugmode
+.Op Fl S Ar tos
+.Op Fl X Ar authtype
+.Op Fl a Ar authmode
+.Op Fl edebug
+.Op Fl g Ar gettyent
+.Op Fl u Ar len
+.Op Fl debug Op Ar port
+.Sh DESCRIPTION
+The
+.Nm
+command is a server which supports the
+.Tn DARPA
+standard
+.Tn TELNET
+virtual terminal protocol.
+.Nm
+is normally invoked by the internet server (see
+.Xr inetd 8 )
+for requests to connect to the
+.Tn TELNET
+port as indicated by the
+.Pa /etc/services
+file (see
+.Xr services 5 ) .
+The
+.Fl debug
+option may be used to start up
+.Nm
+manually, instead of through
+.Xr inetd 8 .
+If started up this way,
+.Ar port
+may be specified to run
+.Nm
+on an alternate
+.Tn TCP
+port number.
+.Pp
+The
+.Nm
+command accepts the following options:
+.Bl -tag -width "-a authmode"
+.It Fl a Ar authmode
+This option may be used for specifying what mode should
+be used for authentication.
+Note that this option is only useful if
+.Nm
+has been compiled with support for the
+.Dv AUTHENTICATION
+option.
+There are several valid values for
+.Ar authmode :
+.Bl -tag -width debug
+.It debug
+Turns on authentication debugging code.
+.It user
+Only allow connections when the remote user
+can provide valid authentication information
+to identify the remote user,
+and is allowed access to the specified account
+without providing a password.
+.It valid
+Only allow connections when the remote user
+can provide valid authentication information
+to identify the remote user.
+The
+.Xr login 1
+command will provide any additional user verification
+needed if the remote user is not allowed automatic
+access to the specified account.
+.It other
+Only allow connections that supply some authentication information.
+This option is currently not supported
+by any of the existing authentication mechanisms,
+and is thus the same as specifying
+.Fl a
+.Cm valid .
+.It none
+This is the default state.
+Authentication information is not required.
+If no or insufficient authentication information
+is provided, then the
+.Xr login 1
+program will provide the necessary user
+verification.
+.It off
+This disables the authentication code.
+All user verification will happen through the
+.Xr login 1
+program.
+.El
+.It Fl D Ar debugmode
+This option may be used for debugging purposes.
+This allows
+.Nm
+to print out debugging information
+to the connection, allowing the user to see what
+.Nm
+is doing.
+There are several possible values for
+.Ar debugmode :
+.Bl -tag -width exercise
+.It Cm options
+Prints information about the negotiation of
+.Tn TELNET
+options.
+.It Cm report
+Prints the
+.Cm options
+information, plus some additional information
+about what processing is going on.
+.It Cm netdata
+Displays the data stream received by
+.Nm .
+.It Cm ptydata
+Displays data written to the pty.
+.It Cm exercise
+Has not been implemented yet.
+.El
+.It Fl debug
+Enables debugging on each socket created by
+.Nm
+(see
+.Dv SO_DEBUG
+in
+.Xr socket 2 ) .
+.It Fl edebug
+If
+.Nm
+has been compiled with support for data encryption, then the
+.Fl edebug
+option may be used to enable encryption debugging code.
+.It Fl g Ar gettyent
+Specifies which entry from
+.Pa /etc/gettytab
+should be used to get banner strings, login program and
+other information.
+The default entry is
+.Dv default .
+.It Fl h
+Disables the printing of host-specific information before
+login has been completed.
+.It Fl k
+This option is only useful if
+.Nm
+has been compiled with both linemode and kludge linemode support.
+If the
+.Fl k
+option is specified,
+then if the remote client does not support the
+.Dv LINEMODE
+option, then
+.Nm
+will operate in character at a time mode.
+It will still support kludge linemode, but will only
+go into kludge linemode if the remote client requests it.
+(This is done by by the client sending
+.Dv DONT SUPPRESS-GO-AHEAD
+and
+.Dv DONT ECHO . )
+The
+.Fl k
+option is most useful when there are remote clients
+that do not support kludge linemode, but pass the heuristic
+(if they respond with
+.Dv WILL TIMING-MARK
+in response to a
+.Dv DO TIMING-MARK )
+for kludge linemode support.
+.It Fl l
+Specifies line mode.
+Tries to force clients to use line-at-a-time mode.
+If the
+.Dv LINEMODE
+option is not supported, it will go
+into kludge linemode.
+.It Fl n
+Disable
+.Dv TCP
+keep-alives.
+Normally
+.Nm
+enables the
+.Tn TCP
+keep-alive mechanism to probe connections that
+have been idle for some period of time to determine
+if the client is still there, so that idle connections
+from machines that have crashed or can no longer
+be reached may be cleaned up.
+.It Fl s
+This option is only enabled if
+.Nm
+is compiled with support for secure logins.
+It causes the
+.Fl s
+option to be passed on to
+.Xr login 1 ,
+and thus is only useful if
+.Xr login 1
+supports the
+.Fl s
+flag to indicate that only Kerberos or S/Key
+validated logins are allowed, and is
+usually useful for controlling remote logins
+from outside of a firewall.
+.It Fl S Ar tos
+This option sets the IP Type-of Service (TOS) option
+on the connection to the value tos, which may be a
+numeric TOS value or a symbolic TOS name found in the
+.Pa /etc/iptos
+file.
+This option has no effect on
+.Nx .
+.\"The option has no effect on systems that do not
+.\"support
+.\".Xr parsetos 3
+.\"routine and the
+.\".Pa /etc/iptos
+.\"file.
+.It Fl u Ar len
+This option is used to specify the size of the field
+in the
+.Dv utmp
+structure that holds the remote host name.
+If the resolved host name is longer than
+.Ar len ,
+the dotted decimal value will be used instead.
+This allows hosts with very long host names that
+overflow this field to still be uniquely identified.
+Specifying
+.Fl u0
+indicates that only dotted decimal addresses
+should be put into the
+.Pa utmp
+file.
+.It Fl U
+This option causes
+.Nm
+to refuse connections from addresses that
+cannot be mapped back into a symbolic name via the
+.Xr getnameinfo 3
+routine.
+.It Fl X Ar authtype
+This option is only valid if
+.Nm
+has been built with support for the authentication option.
+It disables the use of
+.Ar authtype
+authentication, and can be used to temporarily disable
+a specific authentication type without having to recompile
+.Nm .
+.It Fl 4
+.It Fl 6
+Specifies address family to be used on
+.Fl debug
+mode.
+During normal operation
+.Po
+called from
+.Xr inetd 8
+.Pc
+.Nm
+will use the file descriptor passed from
+.Xr inetd 8 .
+.El
+.Pp
+.Nm
+operates by allocating a pseudo-terminal device (see
+.Xr pty 4 )
+for a client, then creating a login process which has
+the slave side of the pseudo-terminal as
+.Dv stdin ,
+.Dv stdout
+and
+.Dv stderr .
+.Nm
+manipulates the master side of the pseudo-terminal,
+implementing the
+.Tn TELNET
+protocol and passing characters
+between the remote client and the login process.
+.Pp
+When a
+.Tn TELNET
+session is started up,
+.Nm
+sends
+.Tn TELNET
+options to the client side indicating
+a willingness to do the following
+.Tn TELNET
+options, which are described in more detail below:
+.Bd -literal -offset indent
+DO AUTHENTICATION
+WILL ENCRYPT
+DO TERMINAL TYPE
+DO TSPEED
+DO XDISPLOC
+DO NEW-ENVIRON
+DO ENVIRON
+WILL SUPPRESS GO AHEAD
+DO ECHO
+DO LINEMODE
+DO NAWS
+WILL STATUS
+DO LFLOW
+DO TIMING-MARK
+.Ed
+.Pp
+The pseudo-terminal allocated to the client is configured
+to operate in \*(lqcooked\*(rq mode, and with
+.Dv XTABS and
+.Dv CRMOD
+enabled (see
+.Xr tty 4 ) .
+.Pp
+.Nm
+has support for enabling locally the following
+.Tn TELNET
+options:
+.Bl -tag -width "DO AUTHENTICATION"
+.It "WILL ECHO"
+When the
+.Dv LINEMODE
+option is enabled, a
+.Dv WILL ECHO
+or
+.Dv WONT ECHO
+will be sent to the client to indicate the
+current state of terminal echoing.
+When terminal echo is not desired, a
+.Dv WILL ECHO
+is sent to indicate that
+.Tn telnetd
+will take care of echoing any data that needs to be
+echoed to the terminal, and then nothing is echoed.
+When terminal echo is desired, a
+.Dv WONT ECHO
+is sent to indicate that
+.Tn telnetd
+will not be doing any terminal echoing, so the
+client should do any terminal echoing that is needed.
+.It "WILL BINARY"
+Indicates that the client is willing to send a
+8 bits of data, rather than the normal 7 bits
+of the Network Virtual Terminal.
+.It "WILL SGA"
+Indicates that it will not be sending
+.Dv IAC GA ,
+go ahead, commands.
+.It "WILL STATUS"
+Indicates a willingness to send the client, upon
+request, of the current status of all
+.Tn TELNET
+options.
+.It "WILL TIMING-MARK"
+Whenever a
+.Dv DO TIMING-MARK
+command is received, it is always responded
+to with a
+.Dv WILL TIMING-MARK
+.It "WILL LOGOUT"
+When a
+.Dv DO LOGOUT
+is received, a
+.Dv WILL LOGOUT
+is sent in response, and the
+.Tn TELNET
+session is shut down.
+.It "WILL ENCRYPT"
+Only sent if
+.Nm
+is compiled with support for data encryption, and
+indicates a willingness to decrypt the data stream.
+.El
+.Pp
+.Nm
+has support for enabling remotely the following
+.Tn TELNET
+options:
+.Bl -tag -width "DO AUTHENTICATION"
+.It "DO BINARY"
+Sent to indicate that
+.Tn telnetd
+is willing to receive an 8 bit data stream.
+.It "DO LFLOW"
+Requests that the client handle flow control
+characters remotely.
+.It "DO ECHO"
+This is not really supported, but is sent to identify a
+.Bx 4.2
+.Xr telnet 1
+client, which will improperly respond with
+.Dv WILL ECHO .
+If a
+.Dv WILL ECHO
+is received, a
+.Dv DONT ECHO
+will be sent in response.
+.It "DO TERMINAL-TYPE"
+Indicates a desire to be able to request the
+name of the type of terminal that is attached
+to the client side of the connection.
+.It "DO SGA"
+Indicates that it does not need to receive
+.Dv IAC GA ,
+the go ahead command.
+.It "DO NAWS"
+Requests that the client inform the server when
+the window (display) size changes.
+.It "DO TERMINAL-SPEED"
+Indicates a desire to be able to request information
+about the speed of the serial line to which
+the client is attached.
+.It "DO XDISPLOC"
+Indicates a desire to be able to request the name
+of the X windows display that is associated with
+the telnet client.
+.It "DO NEW-ENVIRON"
+Indicates a desire to be able to request environment
+variable information, as described in RFC 1572.
+.It "DO ENVIRON"
+Indicates a desire to be able to request environment
+variable information, as described in RFC 1408.
+.It "DO LINEMODE"
+Only sent if
+.Nm
+is compiled with support for linemode, and
+requests that the client do line by line processing.
+.It "DO TIMING-MARK"
+Only sent if
+.Nm
+is compiled with support for both linemode and
+kludge linemode, and the client responded with
+.Dv WONT LINEMODE .
+If the client responds with
+.Dv WILL TM ,
+the it is assumed that the client supports kludge linemode.
+Note that the
+.Op Fl k
+option can be used to disable this.
+.It "DO AUTHENTICATION"
+Only sent if
+.Nm
+is compiled with support for authentication, and
+indicates a willingness to receive authentication
+information for automatic login.
+.It "DO ENCRYPT"
+Only sent if
+.Nm
+is compiled with support for data encryption, and
+indicates a willingness to decrypt the data stream.
+.El
+.Pp
+At the end of a login session,
+.Nm
+invokes the
+.Xr ttyaction 3
+facility with an action of "telnetd" and user "root"
+to execute site-specific commands.
+.Sh FILES
+.Bl -item -compact
+.It
+.Pa /etc/services
+.It
+.Pa /etc/iptos
+(if supported)
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr skey 1 ,
+.Xr telnet 1 ,
+.Xr ttyaction 3
+.Sh STANDARDS
+.Bl -tag -compact -width "RFC 1572  "
+.It RFC 854
+.Tn TELNET
+PROTOCOL SPECIFICATION
+.It RFC 855
+TELNET OPTION SPECIFICATIONS
+.It RFC 856
+TELNET BINARY TRANSMISSION
+.It RFC 857
+TELNET ECHO OPTION
+.It RFC 858
+TELNET SUPPRESS GO AHEAD OPTION
+.It RFC 859
+TELNET STATUS OPTION
+.It RFC 860
+TELNET TIMING MARK OPTION
+.It RFC 861
+TELNET EXTENDED OPTIONS - LIST OPTION
+.It RFC 885
+TELNET END OF RECORD OPTION
+.It RFC 1073
+Telnet Window Size Option
+.It RFC 1079
+Telnet Terminal Speed Option
+.It RFC 1091
+Telnet Terminal-Type Option
+.It RFC 1096
+Telnet X Display Location Option
+.It RFC 1123
+Requirements for Internet Hosts -- Application and Support
+.It RFC 1184
+Telnet Linemode Option
+.It RFC 1372
+Telnet Remote Flow Control Option
+.It RFC 1416
+Telnet Authentication Option
+.It RFC 1411
+Telnet Authentication: Kerberos Version 4
+.It RFC 1412
+Telnet Authentication: SPX
+.It RFC 1571
+Telnet Environment Option Interoperability Issues
+.It RFC 1572
+Telnet Environment Option
+.El
+.Sh BUGS
+Some
+.Tn TELNET
+commands are only partially implemented.
+.Pp
+Because of bugs in the original
+.Bx 4.2
+.Xr telnet 1 ,
+.Nm
+performs some dubious protocol exchanges to try to discover if the remote
+client is, in fact, a
+.Bx 4.2
+.Xr telnet 1 .
+.Pp
+Binary mode
+has no common interpretation except between similar operating systems
+.Po
+.Ux
+in this case
+.Pc .
+.Pp
+The terminal type name received from the remote client is converted to
+lower case.
+.Pp
+.Nm
+never sends
+.Tn TELNET
+.Dv IAC GA
+(go ahead) commands.
diff --git a/libexec/telnetd/telnetd.c b/libexec/telnetd/telnetd.c
new file mode 100644 (file)
index 0000000..eae43c3
--- /dev/null
@@ -0,0 +1,1159 @@
+/*     $NetBSD: telnetd.c,v 1.55 2014/02/27 18:20:21 joerg Exp $       */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ * 
+ * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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.
+ */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
+ The Regents of the University of California.  All rights reserved.");
+#if 0
+static char sccsid[] = "@(#)telnetd.c  8.4 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: telnetd.c,v 1.55 2014/02/27 18:20:21 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include "telnetd.h"
+#include "pathnames.h"
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <termcap.h>
+
+#include <limits.h>
+
+#ifdef KRB5
+#define        Authenticator   k5_Authenticator
+#include <krb5.h>
+#undef Authenticator
+#include <krb5/com_err.h>
+#endif
+
+#ifdef AUTHENTICATION
+int    auth_level = 0;
+#endif
+
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+#include <libtelnet/misc.h>
+#endif
+
+#ifdef SECURELOGIN
+int    require_secure_login = 0;
+#endif
+
+extern int require_hwpreauth;
+#ifdef KRB5
+extern krb5_context telnet_context;
+#endif
+int    registerd_host_only = 0;
+
+
+/*
+ * I/O data buffers,
+ * pointers, and counters.
+ */
+char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
+char   ptyibuf2[BUFSIZ];
+
+
+int    hostinfo = 1;                   /* do we print login banner? */
+
+
+static int debug = 0;
+int keepalive = 1;
+const char *gettyname = "default";
+char *progname;
+
+void usage(void) __dead;
+int getterminaltype(char *, size_t);
+int getent(char *, const char *);
+static void doit(struct sockaddr *) __dead;
+void _gettermname(void);
+int terminaltypeok(char *);
+char *getstr(const char *, char **);
+
+/*
+ * The string to pass to getopt().  We do it this way so
+ * that only the actual options that we support will be
+ * passed off to getopt().
+ */
+char valid_opts[] = {
+       'd', ':', 'g', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',
+       '4', '6',
+#ifdef AUTHENTICATION
+       'a', ':', 'X', ':',
+#endif
+#ifdef ENCRYPTION
+       'e', ':',
+#endif
+#ifdef DIAGNOSTICS
+       'D', ':',
+#endif
+#ifdef LINEMODE
+       'l',
+#endif
+#ifdef SECURELOGIN
+       's',
+#endif
+#ifdef KRB5
+       'R', ':', 'H',
+#endif
+       '\0'
+};
+
+int family = AF_INET;
+struct sockaddr_storage from;
+
+int
+main(int argc, char *argv[])
+{
+       socklen_t fromlen;
+       int on = 1;
+       int ch;
+#if    defined(IPPROTO_IP) && defined(IP_TOS)
+       int tos = -1;
+#endif
+
+       pfrontp = pbackp = ptyobuf;
+       netip = netibuf;
+       nfrontp = nbackp = netobuf;
+#ifdef ENCRYPTION
+       nclearto = 0;
+#endif /* ENCRYPTION */
+
+       progname = *argv;
+
+
+       while ((ch = getopt(argc, argv, valid_opts)) != -1) {
+               switch (ch) {
+
+#ifdef AUTHENTICATION
+               case 'a':
+                       /*
+                        * Check for required authentication level
+                        */
+                       if (strcmp(optarg, "debug") == 0) {
+                               auth_debug_mode = 1;
+                       } else if (strcasecmp(optarg, "none") == 0) {
+                               auth_level = 0;
+                       } else if (strcasecmp(optarg, "other") == 0) {
+                               auth_level = AUTH_OTHER;
+                       } else if (strcasecmp(optarg, "user") == 0) {
+                               auth_level = AUTH_USER;
+                       } else if (strcasecmp(optarg, "valid") == 0) {
+                               auth_level = AUTH_VALID;
+                       } else if (strcasecmp(optarg, "off") == 0) {
+                               /*
+                                * This hack turns off authentication
+                                */
+                               auth_level = -1;
+                       } else {
+                               fprintf(stderr,
+                           "telnetd: unknown authorization level for -a\n");
+                       }
+                       break;
+#endif /* AUTHENTICATION */
+
+
+               case 'd':
+                       if (strcmp(optarg, "ebug") == 0) {
+                               debug++;
+                               break;
+                       }
+                       usage();
+                       /* NOTREACHED */
+                       break;
+
+#ifdef DIAGNOSTICS
+               case 'D':
+                       /*
+                        * Check for desired diagnostics capabilities.
+                        */
+                       if (!strcmp(optarg, "report")) {
+                               diagnostic |= TD_REPORT|TD_OPTIONS;
+                       } else if (!strcmp(optarg, "exercise")) {
+                               diagnostic |= TD_EXERCISE;
+                       } else if (!strcmp(optarg, "netdata")) {
+                               diagnostic |= TD_NETDATA;
+                       } else if (!strcmp(optarg, "ptydata")) {
+                               diagnostic |= TD_PTYDATA;
+                       } else if (!strcmp(optarg, "options")) {
+                               diagnostic |= TD_OPTIONS;
+                       } else {
+                               usage();
+                               /* NOT REACHED */
+                       }
+                       break;
+#endif /* DIAGNOSTICS */
+
+#ifdef ENCRYPTION
+               case 'e':
+                       if (strcmp(optarg, "debug") == 0) {
+                               encrypt_debug_mode = 1;
+                               break;
+                       }
+                       usage();
+                       /* NOTREACHED */
+                       break;
+#endif /* ENCRYPTION */
+
+               case 'g':
+                       gettyname = optarg;
+                       break;
+
+               case 'h':
+                       hostinfo = 0;
+                       break;
+
+#ifdef KRB5
+               case 'H':
+                   {
+                       require_hwpreauth = 1;
+                       break;
+                   }
+#endif /* KRB5 */
+
+
+#ifdef LINEMODE
+               case 'l':
+                       alwayslinemode = 1;
+                       break;
+#endif /* LINEMODE */
+
+               case 'k':
+#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
+                       lmodetype = NO_AUTOKLUDGE;
+#else
+                       /* ignore -k option if built without kludge linemode */
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       break;
+
+               case 'n':
+                       keepalive = 0;
+                       break;
+
+
+#ifdef KRB5
+               case 'R':
+                   {
+                       krb5_error_code retval;
+
+                       if (telnet_context == 0) {
+                               retval = krb5_init_context(&telnet_context);
+                               if (retval) {
+                                       com_err("telnetd", retval,
+                                           "while initializing krb5");
+                                       exit(1);
+                               }
+                       }
+                       krb5_set_default_realm(telnet_context, optarg);
+                       break;
+                   }
+#endif /* KRB5 */
+
+#ifdef SECURELOGIN
+               case 's':
+                       /* Secure login required */
+                       require_secure_login = 1;
+                       break;
+#endif /* SECURELOGIN */
+               case 'S':
+                       fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
+                                               "-S flag not supported\n");
+                       break;
+
+               case 'u':
+                       fprintf(stderr, "telnetd: -u option unneeded\n");
+                       break;
+
+               case 'U':
+                       registerd_host_only = 1;
+                       break;
+
+#ifdef AUTHENTICATION
+               case 'X':
+                       /*
+                        * Check for invalid authentication types
+                        */
+                       auth_disable_name(optarg);
+                       break;
+#endif /* AUTHENTICATION */
+
+               case '4':
+                       family = AF_INET;
+                       break;
+
+               case '6':
+                       family = AF_INET6;
+                       break;
+
+               default:
+                       fprintf(stderr, "telnetd: %c: unknown option\n", ch);
+                       /* FALLTHROUGH */
+               case '?':
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (debug) {
+           int s, ns, error;
+           socklen_t foo;
+           const char *service = "telnet";
+           struct addrinfo hints, *res;
+
+           if (argc > 1) {
+               usage();
+               /* NOT REACHED */
+           } else if (argc == 1)
+               service = *argv;
+
+           memset(&hints, 0, sizeof(hints));
+           hints.ai_flags = AI_PASSIVE;
+           hints.ai_family = family;
+           hints.ai_socktype = SOCK_STREAM;
+           hints.ai_protocol = 0;
+           error = getaddrinfo(NULL, service, &hints, &res);
+
+           if (error) {
+               fprintf(stderr, "tcp/%s: %s\n", service, gai_strerror(error));
+               exit(1);
+           }
+
+           s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+           if (s < 0) {
+               perror("telnetd: socket");
+               exit(1);
+           }
+           (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                               (char *)&on, sizeof(on));
+           if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
+               perror("bind");
+               exit(1);
+           }
+           if (listen(s, 1) < 0) {
+               perror("listen");
+               exit(1);
+           }
+           foo = res->ai_addrlen;
+           ns = accept(s, res->ai_addr, &foo);
+           if (ns < 0) {
+               perror("accept");
+               exit(1);
+           }
+           (void) dup2(ns, 0);
+           (void) close(ns);
+           (void) close(s);
+       } else if (argc > 0) {
+               usage();
+               /* NOT REACHED */
+       }
+
+       openlog("telnetd", LOG_PID, LOG_DAEMON);
+       fromlen = sizeof (from);
+       if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+               fprintf(stderr, "%s: ", progname);
+               perror("getpeername");
+               _exit(1);
+       }
+       if (keepalive &&
+           setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
+                       (char *)&on, sizeof (on)) < 0) {
+               syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+       }
+
+#if    defined(IPPROTO_IP) && defined(IP_TOS)
+       if (((struct sockaddr *)&from)->sa_family == AF_INET) {
+               if (tos < 0)
+                       tos = 020;      /* Low Delay bit */
+               if (tos
+                  && (setsockopt(0, IPPROTO_IP, IP_TOS,
+                                 (char *)&tos, sizeof(tos)) < 0)
+                  && (errno != ENOPROTOOPT) )
+                       syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+       }
+#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
+
+       net = 0;
+       doit((struct sockaddr *)&from);
+       /* NOTREACHED */
+#ifdef __GNUC__
+       exit(0);
+#endif
+}  /* end of main */
+
+void
+usage(void)
+{
+       fprintf(stderr, "Usage: telnetd");
+#ifdef AUTHENTICATION
+       fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t");
+#endif
+       fprintf(stderr, " [-debug]");
+#ifdef DIAGNOSTICS
+       fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
+#endif
+#ifdef ENCRYPTION
+       fprintf(stderr, " [-edebug]");
+#endif
+       fprintf(stderr, " [-h]");
+#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
+       fprintf(stderr, " [-k]");
+#endif
+#ifdef LINEMODE
+       fprintf(stderr, " [-l]");
+#endif
+       fprintf(stderr, " [-n]");
+       fprintf(stderr, "\n\t");
+#ifdef SECURELOGIN
+       fprintf(stderr, " [-s]");
+#endif
+#ifdef AUTHENTICATION
+       fprintf(stderr, " [-X auth-type]");
+#endif
+       fprintf(stderr, " [-u utmp_hostname_length] [-U]");
+       fprintf(stderr, " [port]\n");
+       exit(1);
+}
+
+/*
+ * getterminaltype
+ *
+ *     Ask the other end to send along its terminal type and speed.
+ * Output is the variable terminaltype filled in.
+ */
+static unsigned char ttytype_sbbuf[] = {
+       IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
+};
+
+int
+getterminaltype(char *name, size_t l)
+{
+    int retval = -1;
+
+    settimer(baseline);
+#ifdef AUTHENTICATION
+    /*
+     * Handle the Authentication option before we do anything else.
+     */
+    send_do(TELOPT_AUTHENTICATION, 1);
+    while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
+       ttloop();
+    if (his_state_is_will(TELOPT_AUTHENTICATION)) {
+       retval = auth_wait(name, l);
+    }
+#endif
+
+#ifdef ENCRYPTION
+    send_will(TELOPT_ENCRYPT, 1);
+#endif /* ENCRYPTION */
+    send_do(TELOPT_TTYPE, 1);
+    send_do(TELOPT_TSPEED, 1);
+    send_do(TELOPT_XDISPLOC, 1);
+    send_do(TELOPT_NEW_ENVIRON, 1);
+    send_do(TELOPT_OLD_ENVIRON, 1);
+    while (
+#ifdef ENCRYPTION
+          his_do_dont_is_changing(TELOPT_ENCRYPT) ||
+#endif /* ENCRYPTION */
+          his_will_wont_is_changing(TELOPT_TTYPE) ||
+          his_will_wont_is_changing(TELOPT_TSPEED) ||
+          his_will_wont_is_changing(TELOPT_XDISPLOC) ||
+          his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
+          his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
+       ttloop();
+    }
+    if (his_state_is_will(TELOPT_TSPEED)) {
+       static unsigned char sb[] =
+                       { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
+
+       output_datalen((const char *)sb, sizeof sb);
+       DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
+    }
+#ifdef ENCRYPTION
+    /*
+     * Wait for the negotiation of what type of encryption we can
+     * send with.  If autoencrypt is not set, this will just return.
+     */
+    if (his_state_is_will(TELOPT_ENCRYPT)) {
+       encrypt_wait();
+    }
+#endif /* ENCRYPTION */
+    if (his_state_is_will(TELOPT_XDISPLOC)) {
+       static unsigned char sb[] =
+                       { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
+
+       output_datalen((const char *)sb, sizeof sb);
+       DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
+    }
+    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
+       static unsigned char sb[] =
+                       { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
+
+       output_datalen((const char *)sb, sizeof sb);
+       DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
+    }
+    else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
+       static unsigned char sb[] =
+                       { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
+
+       output_datalen((const char *)sb, sizeof sb);
+       DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
+    }
+    if (his_state_is_will(TELOPT_TTYPE)) {
+
+       output_datalen((const char *)ttytype_sbbuf, sizeof ttytype_sbbuf);
+       DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
+                                       sizeof ttytype_sbbuf - 2););
+    }
+    if (his_state_is_will(TELOPT_TSPEED)) {
+       while (sequenceIs(tspeedsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_XDISPLOC)) {
+       while (sequenceIs(xdisplocsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
+       while (sequenceIs(environsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
+       while (sequenceIs(oenvironsubopt, baseline))
+           ttloop();
+    }
+    if (his_state_is_will(TELOPT_TTYPE)) {
+       char first[256], last[256];
+
+       while (sequenceIs(ttypesubopt, baseline))
+           ttloop();
+
+       /*
+        * If the other side has already disabled the option, then
+        * we have to just go with what we (might) have already gotten.
+        */
+       if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
+           (void) strlcpy(first, terminaltype, sizeof(first));
+           for(;;) {
+               /*
+                * Save the unknown name, and request the next name.
+                */
+               (void) strlcpy(last, terminaltype, sizeof(last));
+               _gettermname();
+               if (terminaltypeok(terminaltype))
+                   break;
+               if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
+                   his_state_is_wont(TELOPT_TTYPE)) {
+                   /*
+                    * We've hit the end.  If this is the same as
+                    * the first name, just go with it.
+                    */
+                   if (strncmp(first, terminaltype, sizeof(first)) == 0)
+                       break;
+                   /*
+                    * Get the terminal name one more time, so that
+                    * RFC1091 compliant telnets will cycle back to
+                    * the start of the list.
+                    */
+                    _gettermname();
+                   if (strncmp(first, terminaltype, sizeof(first)) != 0) {
+                       (void) strlcpy(terminaltype, first, sizeof(terminaltype));
+                   }
+                   break;
+               }
+           }
+       }
+    }
+    return(retval);
+}  /* end of getterminaltype */
+
+void
+_gettermname(void)
+{
+    /*
+     * If the client turned off the option,
+     * we can't send another request, so we
+     * just return.
+     */
+    if (his_state_is_wont(TELOPT_TTYPE))
+       return;
+    settimer(baseline);
+    output_datalen((const char *)ttytype_sbbuf, sizeof ttytype_sbbuf);
+    DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
+                                       sizeof ttytype_sbbuf - 2););
+    while (sequenceIs(ttypesubopt, baseline))
+       ttloop();
+}
+
+int
+terminaltypeok(char *s)
+{
+    char buf[1024];
+
+    /*
+     * tgetent() will return 1 if the type is known, and
+     * 0 if it is not known.  If it returns -1, it couldn't
+     * open the database.  But if we can't open the database,
+     * it won't help to say we failed, because we won't be
+     * able to verify anything else.  So, we treat -1 like 1.
+     */
+    if (tgetent(buf, s) == 0)
+       return(0);
+    return(1);
+}
+
+char *hostname;
+char host_name[MAXHOSTNAMELEN + 1];
+char remote_host_name[MAXHOSTNAMELEN + 1];
+
+static void telnet(int, int) __dead;
+
+/*
+ * Get a pty, scan input lines.
+ */
+static void
+doit(struct sockaddr *who)
+{
+       char *host;
+       int error;
+       int level;
+       int ptynum;
+       int flags;
+       char user_name[256];
+
+       /*
+        * Find an available pty to use.
+        */
+       pty = getpty(&ptynum);
+       if (pty < 0)
+               fatal(net, "All network ports in use");
+
+       flags = registerd_host_only ? NI_NAMEREQD : 0;
+
+       /* get name of connected client */
+       error = getnameinfo(who, who->sa_len, remote_host_name,
+           sizeof(remote_host_name), NULL, 0, flags);
+
+       if (error) {
+               fatal(net, "Couldn't resolve your address into a host name.\r\n\
+        Please contact your net administrator");
+#ifdef __GNUC__
+               host = NULL;    /* XXX gcc */
+#endif
+       }
+
+       remote_host_name[sizeof(remote_host_name)-1] = 0;
+       host = remote_host_name;
+
+       (void)gethostname(host_name, sizeof(host_name));
+       host_name[sizeof(host_name) - 1] = '\0';
+       hostname = host_name;
+
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+       auth_encrypt_init(hostname, host, "TELNETD", 1);
+#endif
+
+       init_env();
+       /*
+        * get terminal type.
+        */
+       *user_name = 0;
+       level = getterminaltype(user_name, sizeof(user_name));
+       setenv("TERM", terminaltype[0] ? terminaltype : "network", 1);
+
+       /*
+        * Start up the login process on the slave side of the terminal
+        */
+       startslave(host, level, user_name);
+
+       telnet(net, pty);  /* begin server processing */
+       /*NOTREACHED*/
+}  /* end of doit */
+
+
+/*
+ * Main loop.  Select from pty and network, and
+ * hand data to telnet receiver finite state machine.
+ */
+static void
+telnet(int f, int p)
+{
+       int on = 1;
+#define        TABBUFSIZ       512
+       char    defent[TABBUFSIZ];
+       char    defstrs[TABBUFSIZ];
+#undef TABBUFSIZ
+       char *HE, *HN, *IF, *ptyibuf2ptr;
+       const char *IM;
+       struct pollfd set[2];
+
+       /*
+        * Initialize the slc mapping table.
+        */
+       get_slc_defaults();
+
+       /*
+        * Do some tests where it is desireable to wait for a response.
+        * Rather than doing them slowly, one at a time, do them all
+        * at once.
+        */
+       if (my_state_is_wont(TELOPT_SGA))
+               send_will(TELOPT_SGA, 1);
+       /*
+        * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
+        * because 4.2 clients are unable to deal with TCP urgent data.
+        *
+        * To find out, we send out a "DO ECHO".  If the remote system
+        * answers "WILL ECHO" it is probably a 4.2 client, and we note
+        * that fact ("WILL ECHO" ==> that the client will echo what
+        * WE, the server, sends it; it does NOT mean that the client will
+        * echo the terminal input).
+        */
+       send_do(TELOPT_ECHO, 1);
+
+#ifdef LINEMODE
+       if (his_state_is_wont(TELOPT_LINEMODE)) {
+               /* Query the peer for linemode support by trying to negotiate
+                * the linemode option.
+                */
+               linemode = 0;
+               editmode = 0;
+               send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
+       }
+#endif /* LINEMODE */
+
+       /*
+        * Send along a couple of other options that we wish to negotiate.
+        */
+       send_do(TELOPT_NAWS, 1);
+       send_will(TELOPT_STATUS, 1);
+       flowmode = 1;           /* default flow control state */
+       restartany = -1;        /* uninitialized... */
+       send_do(TELOPT_LFLOW, 1);
+
+       /*
+        * Spin, waiting for a response from the DO ECHO.  However,
+        * some REALLY DUMB telnets out there might not respond
+        * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
+        * telnets so far seem to respond with WONT for a DO that
+        * they don't understand...) because by the time we get the
+        * response, it will already have processed the DO ECHO.
+        * Kludge upon kludge.
+        */
+       while (his_will_wont_is_changing(TELOPT_NAWS))
+               ttloop();
+
+       /*
+        * But...
+        * The client might have sent a WILL NAWS as part of its
+        * startup code; if so, we'll be here before we get the
+        * response to the DO ECHO.  We'll make the assumption
+        * that any implementation that understands about NAWS
+        * is a modern enough implementation that it will respond
+        * to our DO ECHO request; hence we'll do another spin
+        * waiting for the ECHO option to settle down, which is
+        * what we wanted to do in the first place...
+        */
+       if (his_want_state_is_will(TELOPT_ECHO) &&
+           his_state_is_will(TELOPT_NAWS)) {
+               while (his_will_wont_is_changing(TELOPT_ECHO))
+                       ttloop();
+       }
+       /*
+        * On the off chance that the telnet client is broken and does not
+        * respond to the DO ECHO we sent, (after all, we did send the
+        * DO NAWS negotiation after the DO ECHO, and we won't get here
+        * until a response to the DO NAWS comes back) simulate the
+        * receipt of a will echo.  This will also send a WONT ECHO
+        * to the client, since we assume that the client failed to
+        * respond because it believes that it is already in DO ECHO
+        * mode, which we do not want.
+        */
+       if (his_want_state_is_will(TELOPT_ECHO)) {
+               DIAG(TD_OPTIONS,
+                       {output_data("td: simulating recv\r\n");});
+               willoption(TELOPT_ECHO);
+       }
+
+       /*
+        * Finally, to clean things up, we turn on our echo.  This
+        * will break stupid 4.2 telnets out of local terminal echo.
+        */
+
+       if (my_state_is_wont(TELOPT_ECHO))
+               send_will(TELOPT_ECHO, 1);
+
+       /*
+        * Turn on packet mode
+        */
+       (void) ioctl(p, TIOCPKT, (char *)&on);
+
+#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
+       /*
+        * Continuing line mode support.  If client does not support
+        * real linemode, attempt to negotiate kludge linemode by sending
+        * the do timing mark sequence.
+        */
+       if (lmodetype < REAL_LINEMODE)
+               send_do(TELOPT_TM, 1);
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+
+       /*
+        * Call telrcv() once to pick up anything received during
+        * terminal type negotiation, 4.2/4.3 determination, and
+        * linemode negotiation.
+        */
+       telrcv();
+
+       (void) ioctl(f, FIONBIO, (char *)&on);
+       (void) ioctl(p, FIONBIO, (char *)&on);
+
+       (void) setsockopt(f, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof on);
+
+       (void) signal(SIGTSTP, SIG_IGN);
+       /*
+        * Ignoring SIGTTOU keeps the kernel from blocking us
+        * in ttioct() in /sys/tty.c.
+        */
+       (void) signal(SIGTTOU, SIG_IGN);
+
+       (void) signal(SIGCHLD, cleanup);
+
+
+       {
+               int t;
+               t = open(_PATH_TTY, O_RDWR);
+               if (t >= 0) {
+                       (void) ioctl(t, TIOCNOTTY, (char *)0);
+                       (void) close(t);
+               }
+       }
+
+
+       /*
+        * 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.
+        */
+
+       if (getent(defent, gettyname) == 1) {
+               char *cp=defstrs;
+
+               HE = getstr("he", &cp);
+               HN = getstr("hn", &cp);
+               IM = getstr("im", &cp);
+               IF = getstr("if", &cp);
+               if (HN && *HN)
+                       (void)strlcpy(host_name, HN, sizeof(host_name));
+               if (IM == 0)
+                       IM = "";
+       } else {
+               IM = DEFAULT_IM;
+               HE = 0;
+               IF = NULL;
+       }
+       edithost(HE, host_name);
+       ptyibuf2ptr = ptyibuf2;
+       if (hostinfo) {
+               if (IF) {
+                       char buf[_POSIX2_LINE_MAX];
+                       FILE *fd;
+
+                       if ((fd = fopen(IF, "r")) != NULL) {
+                               while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
+                                       ptyibuf2ptr = putf(buf, ptyibuf2ptr);
+                               fclose(fd);
+                       }
+               }
+               if (*IM)
+                       ptyibuf2ptr = putf(IM, ptyibuf2ptr);
+       }
+
+       if (pcc)
+               strncpy(ptyibuf2ptr, ptyip, pcc+1);
+       ptyip = ptyibuf2;
+       pcc = strlen(ptyip);
+#ifdef LINEMODE
+       /*
+        * Last check to make sure all our states are correct.
+        */
+       init_termbuf();
+       localstat();
+#endif /* LINEMODE */
+
+       DIAG(TD_REPORT,
+               {output_data("td: Entering processing loop\r\n");});
+
+
+       set[0].fd = f;
+       set[1].fd = p;
+       for (;;) {
+               int c;
+
+               if (ncc < 0 && pcc < 0)
+                       break;
+
+               /*
+                * Never look for input if there's still
+                * stuff in the corresponding output buffer
+                */
+               set[0].events = 0;
+               set[1].events = 0;
+               if (nfrontp - nbackp || pcc > 0)
+                       set[0].events |= POLLOUT;
+               else
+                       set[1].events |= POLLIN;
+               if (pfrontp - pbackp || ncc > 0)
+                       set[1].events |= POLLOUT;
+               else
+                       set[0].events |= POLLIN;
+               if (!SYNCHing)
+                       set[0].events |= POLLPRI;
+
+               if ((c = poll(set, 2, INFTIM)) < 1) {
+                       if (c == -1) {
+                               if (errno == EINTR) {
+                                       continue;
+                               }
+                       }
+                       sleep(5);
+                       continue;
+               }
+
+               /*
+                * Any urgent data?
+                */
+               if (set[0].revents & POLLPRI) {
+                   SYNCHing = 1;
+               }
+
+               /*
+                * Something to read from the network...
+                */
+               if (set[0].revents & POLLIN) {
+                   ncc = read(f, netibuf, sizeof (netibuf));
+                   if (ncc < 0 && errno == EWOULDBLOCK)
+                       ncc = 0;
+                   else {
+                       if (ncc <= 0) {
+                           break;
+                       }
+                       netip = netibuf;
+                   }
+                   DIAG((TD_REPORT | TD_NETDATA),
+                           {output_data("td: netread %d chars\r\n", ncc);});
+                   DIAG(TD_NETDATA, printdata("nd", netip, ncc));
+               }
+
+               /*
+                * Something to read from the pty...
+                */
+               if (set[1].revents & POLLIN) {
+                       pcc = read(p, ptyibuf, BUFSIZ);
+                       /*
+                        * On some systems, if we try to read something
+                        * off the master side before the slave side is
+                        * opened, we get EIO.
+                        */
+                       if (pcc < 0 && (errno == EWOULDBLOCK ||
+                                       errno == EAGAIN ||
+                                       errno == EIO)) {
+                               pcc = 0;
+                       } else {
+                               if (pcc <= 0)
+                                       break;
+#ifdef LINEMODE
+                               /*
+                                * If ioctl from pty, pass it through net
+                                */
+                               if (ptyibuf[0] & TIOCPKT_IOCTL) {
+                                       copy_termbuf(ptyibuf+1, pcc-1);
+                                       localstat();
+                                       pcc = 1;
+                               }
+#endif /* LINEMODE */
+                               if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
+                                       netclear();     /* clear buffer back */
+                                       /*
+                                        * There are client telnets on some
+                                        * operating systems get screwed up
+                                        * royally if we send them urgent
+                                        * mode data.
+                                        */
+                                       output_data("%c%c", IAC, DM);
+                                       neturg = nfrontp - 1; /* off by one XXX */
+                                       DIAG(TD_OPTIONS,
+                                           printoption("td: send IAC", DM));
+                               }
+                               if (his_state_is_will(TELOPT_LFLOW) &&
+                                   (ptyibuf[0] &
+                                    (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
+                                       int newflow =
+                                           ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
+                                       if (newflow != flowmode) {
+                                               flowmode = newflow;
+                                               (void) output_data(
+                                                       "%c%c%c%c%c%c",
+                                                       IAC, SB, TELOPT_LFLOW,
+                                                       flowmode ? LFLOW_ON
+                                                                : LFLOW_OFF,
+                                                       IAC, SE);
+                                               DIAG(TD_OPTIONS, printsub('>',
+                                                   (unsigned char *)nfrontp - 4,
+                                                   4););
+                                       }
+                               }
+                               pcc--;
+                               ptyip = ptyibuf+1;
+                       }
+               }
+
+               while (pcc > 0) {
+                       if ((&netobuf[BUFSIZ] - nfrontp) < 2)
+                               break;
+                       c = *ptyip++ & 0377, pcc--;
+                       if (c == IAC)
+                               output_data("%c", c);
+                       output_data("%c", c);
+                       if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
+                               if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
+                                       output_data("%c", *ptyip++ & 0377);
+                                       pcc--;
+                               } else
+                                       output_datalen("\0", 1);
+                       }
+               }
+
+               if ((set[0].revents & POLLOUT) && (nfrontp - nbackp) > 0)
+                       netflush();
+               if (ncc > 0)
+                       telrcv();
+               if ((set[1].revents & POLLOUT) && (pfrontp - pbackp) > 0)
+                       ptyflush();
+       }
+       cleanup(0);
+}  /* end of telnet */
+
+/*
+ * Send interrupt to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write intr char.
+ */
+void
+interrupt(void)
+{
+       ptyflush();     /* half-hearted */
+
+       (void) ioctl(pty, TIOCSIG, (char *)SIGINT);
+}
+
+/*
+ * Send quit to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write quit char.
+ */
+void
+sendbrk(void)
+{
+       ptyflush();     /* half-hearted */
+       (void) ioctl(pty, TIOCSIG, (char *)SIGQUIT);
+}
+
+void
+sendsusp(void)
+{
+       ptyflush();     /* half-hearted */
+       (void) ioctl(pty, TIOCSIG, (char *)SIGTSTP);
+}
+
+/*
+ * When we get an AYT, if ^T is enabled, use that.  Otherwise,
+ * just send back "[Yes]".
+ */
+void
+recv_ayt(void)
+{
+       if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
+               (void) ioctl(pty, TIOCSIG, (char *)SIGINFO);
+               return;
+       }
+       (void) output_data("\r\n[Yes]\r\n");
+}
+
+void
+doeof(void)
+{
+       init_termbuf();
+
+#if    defined(LINEMODE) && (VEOF == VMIN)
+       if (!tty_isediting()) {
+               extern char oldeofc;
+               *pfrontp++ = oldeofc;
+               return;
+       }
+#endif
+       *pfrontp++ = slctab[SLC_EOF].sptr ?
+                       (unsigned char)*slctab[SLC_EOF].sptr : '\004';
+}
diff --git a/libexec/telnetd/telnetd.h b/libexec/telnetd/telnetd.h
new file mode 100644 (file)
index 0000000..a349336
--- /dev/null
@@ -0,0 +1,45 @@
+/*     $NetBSD: telnetd.h,v 1.8 2003/08/07 09:46:52 agc Exp $  */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ *
+ *     from: @(#)telnetd.h     8.1 (Berkeley) 6/4/93
+ */
+
+
+#include <defs.h>
+#include <ext.h>
+
+#ifdef DIAGNOSTICS
+#define        DIAG(a,b)       if (diagnostic & (a)) b
+#else
+#define        DIAG(a,b)
+#endif
+
+/* other external variables */
+extern char **environ;
diff --git a/libexec/telnetd/termstat.c b/libexec/telnetd/termstat.c
new file mode 100644 (file)
index 0000000..feb782c
--- /dev/null
@@ -0,0 +1,620 @@
+/*     $NetBSD: termstat.c,v 1.14 2005/02/06 05:58:21 perry Exp $      */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)termstat.c 8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: termstat.c,v 1.14 2005/02/06 05:58:21 perry Exp $");
+#endif
+#endif /* not lint */
+
+#include "telnetd.h"
+
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
+
+/*
+ * local variables
+ */
+int def_tspeed = -1, def_rspeed = -1;
+int def_row = 0, def_col = 0;
+#ifdef LINEMODE
+static int _terminit = 0;
+#endif /* LINEMODE */
+
+
+#ifdef LINEMODE
+/*
+ * localstat
+ *
+ * This function handles all management of linemode.
+ *
+ * Linemode allows the client to do the local editing of data
+ * and send only complete lines to the server.  Linemode state is
+ * based on the state of the pty driver.  If the pty is set for
+ * external processing, then we can use linemode.  Further, if we
+ * can use real linemode, then we can look at the edit control bits
+ * in the pty to determine what editing the client should do.
+ *
+ * Linemode support uses the following state flags to keep track of
+ * current and desired linemode state.
+ *     alwayslinemode : true if -l was specified on the telnetd
+ *     command line.  It means to have linemode on as much as
+ *     possible.
+ *
+ *     lmodetype: signifies whether the client can
+ *     handle real linemode, or if use of kludgeomatic linemode
+ *     is preferred.  It will be set to one of the following:
+ *             REAL_LINEMODE : use linemode option
+ *             NO_KLUDGE : don't initiate kludge linemode.
+ *             KLUDGE_LINEMODE : use kludge linemode
+ *             NO_LINEMODE : client is ignorant of linemode
+ *
+ *     linemode, uselinemode : linemode is true if linemode
+ *     is currently on, uselinemode is the state that we wish
+ *     to be in.  If another function wishes to turn linemode
+ *     on or off, it sets or clears uselinemode.
+ *
+ *     editmode, useeditmode : like linemode/uselinemode, but
+ *     these contain the edit mode states (edit and trapsig).
+ *
+ * The state variables correspond to some of the state information
+ * in the pty.
+ *     linemode:
+ *             In real linemode, this corresponds to whether the pty
+ *             expects external processing of incoming data.
+ *             In kludge linemode, this more closely corresponds to the
+ *             whether normal processing is on or not.  (ICANON in
+ *             system V, or COOKED mode in BSD.)
+ *             If the -l option was specified (alwayslinemode), then
+ *             an attempt is made to force external processing on at
+ *             all times.
+ *
+ * The following heuristics are applied to determine linemode
+ * handling within the server.
+ *     1) Early on in starting up the server, an attempt is made
+ *        to negotiate the linemode option.  If this succeeds
+ *        then lmodetype is set to REAL_LINEMODE and all linemode
+ *        processing occurs in the context of the linemode option.
+ *     2) If the attempt to negotiate the linemode option failed,
+ *        and the "-k" (don't initiate kludge linemode) isn't set,
+ *        then we try to use kludge linemode.  We test for this
+ *        capability by sending "do Timing Mark".  If a positive
+ *        response comes back, then we assume that the client
+ *        understands kludge linemode (ech!) and the
+ *        lmodetype flag is set to KLUDGE_LINEMODE.
+ *     3) Otherwise, linemode is not supported at all and
+ *        lmodetype remains set to NO_LINEMODE (which happens
+ *        to be 0 for convenience).
+ *     4) At any time a command arrives that implies a higher
+ *        state of linemode support in the client, we move to that
+ *        linemode support.
+ *
+ * A short explanation of kludge linemode is in order here.
+ *     1) The heuristic to determine support for kludge linemode
+ *        is to send a do timing mark.  We assume that a client
+ *        that supports timing marks also supports kludge linemode.
+ *        A risky proposition at best.
+ *     2) Further negotiation of linemode is done by changing the
+ *        the server's state regarding SGA.  If server will SGA,
+ *        then linemode is off, if server won't SGA, then linemode
+ *        is on.
+ */
+void
+localstat(void)
+{
+       int need_will_echo = 0;
+
+
+       /*
+        * Check for state of BINARY options.
+        */
+       if (tty_isbinaryin()) {
+               if (his_want_state_is_wont(TELOPT_BINARY))
+                       send_do(TELOPT_BINARY, 1);
+       } else {
+               if (his_want_state_is_will(TELOPT_BINARY))
+                       send_dont(TELOPT_BINARY, 1);
+       }
+
+       if (tty_isbinaryout()) {
+               if (my_want_state_is_wont(TELOPT_BINARY))
+                       send_will(TELOPT_BINARY, 1);
+       } else {
+               if (my_want_state_is_will(TELOPT_BINARY))
+                       send_wont(TELOPT_BINARY, 1);
+       }
+
+       /*
+        * Check for changes to flow control if client supports it.
+        */
+       flowstat();
+
+       /*
+        * Check linemode on/off state
+        */
+       uselinemode = tty_linemode();
+
+       /*
+        * If alwayslinemode is on, and pty is changing to turn it off, then
+        * force linemode back on.
+        */
+       if (alwayslinemode && linemode && !uselinemode) {
+               uselinemode = 1;
+               tty_setlinemode(uselinemode);
+       }
+
+#ifdef ENCRYPTION
+       /*
+        * If the terminal is not echoing, but editing is enabled,
+        * something like password input is going to happen, so
+        * if we the other side is not currently sending encrypted
+        * data, ask the other side to start encrypting.
+        */
+       if (his_state_is_will(TELOPT_ENCRYPT)) {
+               static int enc_passwd = 0;
+               if (uselinemode && !tty_isecho() && tty_isediting()
+                   && (enc_passwd == 0) && !decrypt_input) {
+                       encrypt_send_request_start();
+                       enc_passwd = 1;
+               } else if (enc_passwd) {
+                       encrypt_send_request_end();
+                       enc_passwd = 0;
+               }
+       }
+#endif /* ENCRYPTION */
+
+       /*
+        * Do echo mode handling as soon as we know what the
+        * linemode is going to be.
+        * If the pty has echo turned off, then tell the client that
+        * the server will echo.  If echo is on, then the server
+        * will echo if in character mode, but in linemode the
+        * client should do local echoing.  The state machine will
+        * not send anything if it is unnecessary, so don't worry
+        * about that here.
+        *
+        * If we need to send the WILL ECHO (because echo is off),
+        * then delay that until after we have changed the MODE.
+        * This way, when the user is turning off both editing
+        * and echo, the client will get editing turned off first.
+        * This keeps the client from going into encryption mode
+        * and then right back out if it is doing auto-encryption
+        * when passwords are being typed.
+        */
+       if (uselinemode) {
+               if (tty_isecho())
+                       send_wont(TELOPT_ECHO, 1);
+               else
+                       need_will_echo = 1;
+#ifdef KLUDGELINEMODE
+               if (lmodetype == KLUDGE_OK)
+                       lmodetype = KLUDGE_LINEMODE;
+#endif
+       }
+
+       /*
+        * If linemode is being turned off, send appropriate
+        * command and then we're all done.
+        */
+        if (!uselinemode && linemode) {
+# ifdef        KLUDGELINEMODE
+               if (lmodetype == REAL_LINEMODE) {
+# endif        /* KLUDGELINEMODE */
+                       send_dont(TELOPT_LINEMODE, 1);
+# ifdef        KLUDGELINEMODE
+               } else if (lmodetype == KLUDGE_LINEMODE)
+                       send_will(TELOPT_SGA, 1);
+# endif        /* KLUDGELINEMODE */
+               send_will(TELOPT_ECHO, 1);
+               linemode = uselinemode;
+               goto done;
+       }
+
+# ifdef        KLUDGELINEMODE
+       /*
+        * If using real linemode check edit modes for possible later use.
+        * If we are in kludge linemode, do the SGA negotiation.
+        */
+       if (lmodetype == REAL_LINEMODE) {
+# endif        /* KLUDGELINEMODE */
+               useeditmode = 0;
+               if (tty_isediting())
+                       useeditmode |= MODE_EDIT;
+               if (tty_istrapsig())
+                       useeditmode |= MODE_TRAPSIG;
+               if (tty_issofttab())
+                       useeditmode |= MODE_SOFT_TAB;
+               if (tty_islitecho())
+                       useeditmode |= MODE_LIT_ECHO;
+# ifdef        KLUDGELINEMODE
+       } else if (lmodetype == KLUDGE_LINEMODE) {
+               if (tty_isediting() && uselinemode)
+                       send_wont(TELOPT_SGA, 1);
+               else
+                       send_will(TELOPT_SGA, 1);
+       }
+# endif        /* KLUDGELINEMODE */
+
+       /*
+        * Negotiate linemode on if pty state has changed to turn it on.
+        * Send appropriate command and send along edit mode, then all done.
+        */
+       if (uselinemode && !linemode) {
+# ifdef        KLUDGELINEMODE
+               if (lmodetype == KLUDGE_LINEMODE) {
+                       send_wont(TELOPT_SGA, 1);
+               } else if (lmodetype == REAL_LINEMODE) {
+# endif        /* KLUDGELINEMODE */
+                       send_do(TELOPT_LINEMODE, 1);
+                       /* send along edit modes */
+                       (void) output_data("%c%c%c%c%c%c%c", IAC, SB,
+                               TELOPT_LINEMODE, LM_MODE, useeditmode,
+                               IAC, SE);
+                       editmode = useeditmode;
+# ifdef        KLUDGELINEMODE
+               }
+# endif        /* KLUDGELINEMODE */
+               linemode = uselinemode;
+               goto done;
+       }
+
+# ifdef        KLUDGELINEMODE
+       /*
+        * None of what follows is of any value if not using
+        * real linemode.
+        */
+       if (lmodetype < REAL_LINEMODE)
+               goto done;
+# endif        /* KLUDGELINEMODE */
+
+       if (linemode && his_state_is_will(TELOPT_LINEMODE)) {
+               /*
+                * If edit mode changed, send edit mode.
+                */
+                if (useeditmode != editmode) {
+                       /*
+                        * Send along appropriate edit mode mask.
+                        */
+                       (void) output_data("%c%c%c%c%c%c%c", IAC, SB,
+                               TELOPT_LINEMODE, LM_MODE, useeditmode,
+                               IAC, SE);
+                       editmode = useeditmode;
+               }
+
+
+               /*
+                * Check for changes to special characters in use.
+                */
+               start_slc(0);
+               check_slc();
+               (void) end_slc(0);
+       }
+
+done:
+       if (need_will_echo)
+               send_will(TELOPT_ECHO, 1);
+       /*
+        * Some things should be deferred until after the pty state has
+        * been set by the local process.  Do those things that have been
+        * deferred now.  This only happens once.
+        */
+       if (_terminit == 0) {
+               _terminit = 1;
+               defer_terminit();
+       }
+
+       netflush();
+       set_termbuf();
+       return;
+
+}  /* end of localstat */
+#endif /* LINEMODE */
+
+/*
+ * flowstat
+ *
+ * Check for changes to flow control
+ */
+void
+flowstat(void)
+{
+       if (his_state_is_will(TELOPT_LFLOW)) {
+               if (tty_flowmode() != flowmode) {
+                       flowmode = tty_flowmode();
+                       (void) output_data("%c%c%c%c%c%c",
+                                       IAC, SB, TELOPT_LFLOW,
+                                       flowmode ? LFLOW_ON : LFLOW_OFF,
+                                       IAC, SE);
+               }
+               if (tty_restartany() != restartany) {
+                       restartany = tty_restartany();
+                       (void) output_data("%c%c%c%c%c%c",
+                                       IAC, SB, TELOPT_LFLOW,
+                                       restartany ? LFLOW_RESTART_ANY
+                                                  : LFLOW_RESTART_XON,
+                                       IAC, SE);
+               }
+       }
+}
+
+/*
+ * clientstat
+ *
+ * Process linemode related requests from the client.
+ * Client can request a change to only one of linemode, editmode or slc's
+ * at a time, and if using kludge linemode, then only linemode may be
+ * affected.
+ */
+void
+clientstat(int code, int parm1, int parm2)
+{
+
+       /*
+        * Get a copy of terminal characteristics.
+        */
+       init_termbuf();
+
+       /*
+        * Process request from client. code tells what it is.
+        */
+       switch (code) {
+#ifdef LINEMODE
+       case TELOPT_LINEMODE:
+               /*
+                * Don't do anything unless client is asking us to change
+                * modes.
+                */
+               uselinemode = (parm1 == WILL);
+               if (uselinemode != linemode) {
+# ifdef        KLUDGELINEMODE
+                       /*
+                        * If using kludge linemode, make sure that
+                        * we can do what the client asks.
+                        * We can not turn off linemode if alwayslinemode
+                        * and the ICANON bit is set.
+                        */
+                       if (lmodetype == KLUDGE_LINEMODE) {
+                               if (alwayslinemode && tty_isediting()) {
+                                       uselinemode = 1;
+                               }
+                       }
+
+                       /*
+                        * Quit now if we can't do it.
+                        */
+                       if (uselinemode == linemode)
+                               return;
+
+                       /*
+                        * If using real linemode and linemode is being
+                        * turned on, send along the edit mode mask.
+                        */
+                       if (lmodetype == REAL_LINEMODE && uselinemode)
+# else /* KLUDGELINEMODE */
+                       if (uselinemode)
+# endif        /* KLUDGELINEMODE */
+                       {
+                               useeditmode = 0;
+                               if (tty_isediting())
+                                       useeditmode |= MODE_EDIT;
+                               if (tty_istrapsig())
+                                       useeditmode |= MODE_TRAPSIG;
+                               if (tty_issofttab())
+                                       useeditmode |= MODE_SOFT_TAB;
+                               if (tty_islitecho())
+                                       useeditmode |= MODE_LIT_ECHO;
+                               (void) output_data("%c%c%c%c%c%c%c", IAC,
+                                       SB, TELOPT_LINEMODE, LM_MODE,
+                                                       useeditmode, IAC, SE);
+                               editmode = useeditmode;
+                       }
+
+
+                       tty_setlinemode(uselinemode);
+
+                       linemode = uselinemode;
+
+                       if (!linemode)
+                               send_will(TELOPT_ECHO, 1);
+               }
+               break;
+
+       case LM_MODE:
+           {
+               int ack, changed;
+
+               /*
+                * Client has sent along a mode mask.  If it agrees with
+                * what we are currently doing, ignore it; if not, it could
+                * be viewed as a request to change.  Note that the server
+                * will change to the modes in an ack if it is different from
+                * what we currently have, but we will not ack the ack.
+                */
+                useeditmode &= MODE_MASK;
+                ack = (useeditmode & MODE_ACK);
+                useeditmode &= ~MODE_ACK;
+
+                if ((changed = (useeditmode ^ editmode))) {
+                       /*
+                        * This check is for a timing problem.  If the
+                        * state of the tty has changed (due to the user
+                        * application) we need to process that info
+                        * before we write in the state contained in the
+                        * ack!!!  This gets out the new MODE request,
+                        * and when the ack to that command comes back
+                        * we'll set it and be in the right mode.
+                        */
+                       if (ack)
+                               localstat();
+                       if (changed & MODE_EDIT)
+                               tty_setedit(useeditmode & MODE_EDIT);
+
+                       if (changed & MODE_TRAPSIG)
+                               tty_setsig(useeditmode & MODE_TRAPSIG);
+
+                       if (changed & MODE_SOFT_TAB)
+                               tty_setsofttab(useeditmode & MODE_SOFT_TAB);
+
+                       if (changed & MODE_LIT_ECHO)
+                               tty_setlitecho(useeditmode & MODE_LIT_ECHO);
+
+                       set_termbuf();
+
+                       if (!ack) {
+                               (void) output_data("%c%c%c%c%c%c%c", IAC,
+                                       SB, TELOPT_LINEMODE, LM_MODE,
+                                       useeditmode|MODE_ACK,
+                                       IAC, SE);
+                       }
+
+                       editmode = useeditmode;
+               }
+
+               break;
+
+           }  /* end of case LM_MODE */
+#endif /* LINEMODE */
+
+       case TELOPT_NAWS:
+           {
+               struct winsize ws;
+
+               def_col = parm1;
+               def_row = parm2;
+#ifdef LINEMODE
+               /*
+                * Defer changing window size until after terminal is
+                * initialized.
+                */
+               if (terminit() == 0)
+                       return;
+#endif /* LINEMODE */
+
+               /*
+                * Change window size as requested by client.
+                */
+
+               ws.ws_col = parm1;
+               ws.ws_row = parm2;
+               (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
+           }
+
+               break;
+
+       case TELOPT_TSPEED:
+           {
+               def_tspeed = parm1;
+               def_rspeed = parm2;
+#ifdef LINEMODE
+               /*
+                * Defer changing the terminal speed.
+                */
+               if (terminit() == 0)
+                       return;
+#endif /* LINEMODE */
+               /*
+                * Change terminal speed as requested by client.
+                * We set the receive speed first, so that if we can't
+                * store separate receive and transmit speeds, the transmit
+                * speed will take precedence.
+                */
+               tty_rspeed(parm2);
+               tty_tspeed(parm1);
+               set_termbuf();
+
+               break;
+
+           }  /* end of case TELOPT_TSPEED */
+
+       default:
+               /* What? */
+               break;
+       }  /* end of switch */
+
+
+       netflush();
+
+}  /* end of clientstat */
+
+
+#ifdef LINEMODE
+/*
+ * defer_terminit
+ *
+ * Some things should not be done until after the login process has started
+ * and all the pty modes are set to what they are supposed to be.  This
+ * function is called when the pty state has been processed for the first time.
+ * It calls other functions that do things that were deferred in each module.
+ */
+void
+defer_terminit(void)
+{
+
+       /*
+        * local stuff that got deferred.
+        */
+       if (def_tspeed != -1) {
+               clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
+               def_tspeed = def_rspeed = 0;
+       }
+
+       if (def_col || def_row) {
+               struct winsize ws;
+
+               memset((char *)&ws, 0, sizeof(ws));
+               ws.ws_col = def_col;
+               ws.ws_row = def_row;
+               (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
+       }
+
+       /*
+        * The only other module that currently defers anything.
+        */
+       deferslc();
+
+}  /* end of defer_terminit */
+
+/*
+ * terminit
+ *
+ * Returns true if the pty state has been processed yet.
+ */
+int
+terminit(void)
+{
+       return(_terminit);
+
+}  /* end of terminit */
+#endif /* LINEMODE */
diff --git a/libexec/telnetd/utility.c b/libexec/telnetd/utility.c
new file mode 100644 (file)
index 0000000..53f6417
--- /dev/null
@@ -0,0 +1,1077 @@
+/*     $NetBSD: utility.c,v 1.32 2012/01/09 16:36:48 christos Exp $    */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)utility.c  8.4 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: utility.c,v 1.32 2012/01/09 16:36:48 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/utsname.h>
+#include <ctype.h>
+#define PRINTOPTIONS
+#include "telnetd.h"
+
+char *nextitem(char *);
+void putstr(char *);
+
+extern int not42;
+
+/*
+ * utility functions performing io related tasks
+ */
+
+/*
+ * ttloop
+ *
+ *     A small subroutine to flush the network output buffer, get some data
+ * from the network, and pass it through the telnet state machine.  We
+ * also flush the pty input buffer (by dropping its data) if it becomes
+ * too full.
+ */
+
+void
+ttloop(void)
+{
+
+    DIAG(TD_REPORT, {output_data("td: ttloop\r\n");});
+    if (nfrontp - nbackp) {
+       netflush();
+    }
+    ncc = read(net, netibuf, sizeof netibuf);
+    if (ncc < 0) {
+       syslog(LOG_ERR, "ttloop:  read: %m");
+       exit(1);
+    } else if (ncc == 0) {
+       syslog(LOG_INFO, "ttloop:  unexpected EOF from peer");
+       exit(1);
+    }
+    DIAG(TD_REPORT, {output_data("td: ttloop read %d chars\r\n", ncc);});
+    netip = netibuf;
+    telrcv();                  /* state machine */
+    if (ncc > 0) {
+       pfrontp = pbackp = ptyobuf;
+       telrcv();
+    }
+}  /* end of ttloop */
+
+/*
+ * Check a descriptor to see if out of band data exists on it.
+ */
+int
+stilloob(int s /* socket number */)
+{
+    struct pollfd set[1];
+    int value;
+
+    set[0].fd = s;
+    set[0].events = POLLPRI;
+    do {
+       value = poll(set, 1, 0);
+    } while ((value == -1) && (errno == EINTR));
+
+    if (value < 0) {
+       fatalperror(pty, "poll");
+    }
+    if (set[0].revents & POLLPRI) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+void
+ptyflush(void)
+{
+       int n;
+
+       if ((n = pfrontp - pbackp) > 0) {
+               DIAG((TD_REPORT | TD_PTYDATA),
+                       { output_data("td: ptyflush %d chars\r\n", n); });
+               DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
+               n = write(pty, pbackp, n);
+       }
+       if (n < 0) {
+               if (errno == EWOULDBLOCK || errno == EINTR)
+                       return;
+               cleanup(0);
+       }
+       pbackp += n;
+       if (pbackp == pfrontp)
+               pbackp = pfrontp = ptyobuf;
+}
+
+/*
+ * nextitem()
+ *
+ *     Return the address of the next "item" in the TELNET data
+ * stream.  This will be the address of the next character if
+ * the current address is a user data character, or it will
+ * be the address of the character following the TELNET command
+ * if the current address is a TELNET IAC ("I Am a Command")
+ * character.
+ */
+char *
+nextitem(char *current)
+{
+    if ((*current&0xff) != IAC) {
+       return current+1;
+    }
+    switch (*(current+1)&0xff) {
+    case DO:
+    case DONT:
+    case WILL:
+    case WONT:
+       return current+3;
+    case SB:           /* loop forever looking for the SE */
+       {
+           char *look = current+2;
+
+           for (;;) {
+               if ((*look++&0xff) == IAC) {
+                   if ((*look++&0xff) == SE) {
+                       return look;
+                   }
+               }
+           }
+       }
+    default:
+       return current+2;
+    }
+}  /* end of nextitem */
+
+
+/*
+ * netclear()
+ *
+ *     We are about to do a TELNET SYNCH operation.  Clear
+ * the path to the network.
+ *
+ *     Things are a bit tricky since we may have sent the first
+ * byte or so of a previous TELNET command into the network.
+ * So, we have to scan the network buffer from the beginning
+ * until we are up to where we want to be.
+ *
+ *     A side effect of what we do, just to keep things
+ * simple, is to clear the urgent data pointer.  The principal
+ * caller should be setting the urgent data pointer AFTER calling
+ * us in any case.
+ */
+void
+netclear(void)
+{
+    char *thisitem, *next;
+    char *good;
+#define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
+                               ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+#ifdef ENCRYPTION
+    thisitem = nclearto > netobuf ? nclearto : netobuf;
+#else /* ENCRYPTION */
+    thisitem = netobuf;
+#endif /* ENCRYPTION */
+
+    while ((next = nextitem(thisitem)) <= nbackp) {
+       thisitem = next;
+    }
+
+    /* Now, thisitem is first before/at boundary. */
+
+#ifdef ENCRYPTION
+    good = nclearto > netobuf ? nclearto : netobuf;
+#else /* ENCRYPTION */
+    good = netobuf;    /* where the good bytes go */
+#endif /* ENCRYPTION */
+
+    while (nfrontp > thisitem) {
+       if (wewant(thisitem)) {
+           int length;
+
+           next = thisitem;
+           do {
+               next = nextitem(next);
+           } while (wewant(next) && (nfrontp > next));
+           length = next-thisitem;
+           memmove(good, thisitem, length);
+           good += length;
+           thisitem = next;
+       } else {
+           thisitem = nextitem(thisitem);
+       }
+    }
+
+    nbackp = netobuf;
+    nfrontp = good;            /* next byte to be sent */
+    neturg = 0;
+}  /* end of netclear */
+
+/*
+ *  netflush
+ *             Send as much data as possible to the network,
+ *     handling requests for urgent data.
+ */
+void
+netflush(void)
+{
+    int n;
+
+    if ((n = nfrontp - nbackp) > 0) {
+       DIAG(TD_REPORT,
+           { output_data("td: netflush %d chars\r\n", n);
+             n = nfrontp - nbackp;     /* re-compute count */
+           });
+#ifdef ENCRYPTION
+       if (encrypt_output) {
+               char *s = nclearto ? nclearto : nbackp;
+               if (nfrontp - s > 0) {
+                       (*encrypt_output)((unsigned char *)s, nfrontp - s);
+                       nclearto = nfrontp;
+               }
+       }
+#endif /* ENCRYPTION */
+       /*
+        * if no urgent data, or if the other side appears to be an
+        * old 4.2 client (and thus unable to survive TCP urgent data),
+        * write the entire buffer in non-OOB mode.
+        */
+       if ((neturg == 0) || (not42 == 0)) {
+           n = write(net, nbackp, n);  /* normal write */
+       } else {
+           n = neturg - nbackp;
+           /*
+            * In 4.2 (and 4.3) systems, there is some question about
+            * what byte in a sendOOB operation is the "OOB" data.
+            * To make ourselves compatible, we only send ONE byte
+            * out of band, the one WE THINK should be OOB (though
+            * we really have more the TCP philosophy of urgent data
+            * rather than the Unix philosophy of OOB data).
+            */
+           if (n > 1) {
+               n = send(net, nbackp, n-1, 0);  /* send URGENT all by itself */
+           } else {
+               n = send(net, nbackp, n, MSG_OOB);      /* URGENT data */
+           }
+       }
+    }
+    if (n < 0) {
+       if (errno == EWOULDBLOCK || errno == EINTR)
+               return;
+       cleanup(0);
+    }
+    nbackp += n;
+#ifdef ENCRYPTION
+    if (nbackp > nclearto)
+       nclearto = 0;
+#endif /* ENCRYPTION */
+    if (nbackp >= neturg) {
+       neturg = 0;
+    }
+    if (nbackp == nfrontp) {
+       nbackp = nfrontp = netobuf;
+#ifdef ENCRYPTION
+       nclearto = 0;
+#endif /* ENCRYPTION */
+    }
+    return;
+}  /* end of netflush */
+
+
+/*
+ * writenet
+ *
+ * Just a handy little function to write a bit of raw data to the net.
+ * It will force a transmit of the buffer if necessary
+ *
+ * arguments
+ *    ptr - A pointer to a character string to write
+ *    len - How many bytes to write
+ */
+void
+writenet(unsigned char *ptr, int len)
+{
+       /* flush buffer if no room for new data) */
+       if ((&netobuf[BUFSIZ] - nfrontp) < len) {
+               /* if this fails, don't worry, buffer is a little big */
+               netflush();
+       }
+
+       memmove(nfrontp, ptr, len);
+       nfrontp += len;
+
+}  /* end of writenet */
+
+
+/*
+ * miscellaneous functions doing a variety of little jobs follow ...
+ */
+void
+fatal(int f, const char *msg)
+{
+       char buf[BUFSIZ];
+
+       (void)snprintf(buf, sizeof buf, "telnetd: %s.\r\n", msg);
+#ifdef ENCRYPTION
+       if (encrypt_output) {
+               /*
+                * Better turn off encryption first....
+                * Hope it flushes...
+                */
+               encrypt_send_end();
+               netflush();
+       }
+#endif /* ENCRYPTION */
+       (void)write(f, buf, (int)strlen(buf));
+       sleep(1);       /*XXX*/
+       exit(1);
+}
+
+void
+fatalperror(f, msg)
+       int f;
+       const char *msg;
+{
+       char buf[BUFSIZ];
+
+       (void)snprintf(buf, sizeof buf, "%s: %s", msg, strerror(errno));
+       fatal(f, buf);
+}
+
+char editedhost[MAXHOSTNAMELEN];
+
+void
+edithost(const char *pat, const char *host)
+{
+       char *res = editedhost;
+
+       if (!pat)
+               pat = "";
+       while (*pat) {
+               switch (*pat) {
+
+               case '#':
+                       if (*host)
+                               host++;
+                       break;
+
+               case '@':
+                       if (*host)
+                               *res++ = *host++;
+                       break;
+
+               default:
+                       *res++ = *pat;
+                       break;
+               }
+               if (res == &editedhost[sizeof editedhost - 1]) {
+                       *res = '\0';
+                       return;
+               }
+               pat++;
+       }
+       if (*host)
+               (void) strncpy(res, host,
+                   sizeof editedhost - (res - editedhost) -1);
+       else
+               *res = '\0';
+       editedhost[sizeof editedhost - 1] = '\0';
+}
+
+static char *putlocation;
+
+void
+putstr(char *s)
+{
+
+       while (*s)
+               putchr(*s++);
+}
+
+void
+putchr(int cc)
+{
+       *putlocation++ = cc;
+}
+
+/*
+ * This is split on two lines so that SCCS will not see the M
+ * between two % signs and expand it...
+ */
+static const char fmtstr[] = { "%l:%M\
+%p on %A, %d %B %Y" };
+
+char *
+putf(const char *cp, char *where)
+{
+       char *slash;
+       time_t t;
+       char db[100];
+       struct utsname utsinfo;
+
+       uname(&utsinfo);
+
+       putlocation = where;
+
+       while (*cp) {
+               if (*cp != '%') {
+                       putchr(*cp++);
+                       continue;
+               }
+               switch (*++cp) {
+
+               case 't':
+                       if ((slash = strstr(line, "/pts/")) == NULL)
+                               slash = strrchr(line, '/');
+                       if (slash == (char *) 0)
+                               putstr(line);
+                       else
+                               putstr(&slash[1]);
+                       break;
+
+               case 'h':
+                       putstr(editedhost);
+                       break;
+
+               case 'd':
+                       (void)time(&t);
+                       (void)strftime(db, sizeof(db), fmtstr, localtime(&t));
+                       putstr(db);
+                       break;
+
+               case '%':
+                       putchr('%');
+                       break;
+
+               case 's':
+                       putstr(utsinfo.sysname);
+                       break;
+
+               case 'm':
+                       putstr(utsinfo.machine);
+                       break;
+
+               case 'r':
+                       putstr(utsinfo.release);
+                       break;
+
+               case 'v':
+                       putstr(utsinfo.version);
+                        break;
+               }
+               cp++;
+       }
+       
+       return (putlocation);
+}
+
+#ifdef DIAGNOSTICS
+/*
+ * Print telnet options and commands in plain text, if possible.
+ */
+void
+printoption(const char *fmt, int option)
+{
+       if (TELOPT_OK(option))
+               output_data("%s %s\r\n", fmt, TELOPT(option));
+       else if (TELCMD_OK(option))
+               output_data("%s %s\r\n", fmt, TELCMD(option));
+       else
+               output_data("%s %d\r\n", fmt, option);
+       return;
+}
+
+void
+printsub(
+    int           direction,   /* '<' or '>' */
+    unsigned char *pointer,    /* where suboption data sits */
+    int                   length)      /* length of suboption data */
+{
+    int i = 0;
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+    u_char buf[512];
+#endif
+
+       if (!(diagnostic & TD_OPTIONS))
+               return;
+
+       if (direction) {
+           output_data("td: %s suboption ",
+               direction == '<' ? "recv" : "send");
+           if (length >= 3) {
+               int j;
+
+               i = pointer[length - 2];
+               j = pointer[length - 1];
+
+               if (i != IAC || j != SE) {
+                   output_data("(terminated by ");
+                   if (TELOPT_OK(i))
+                       output_data("%s ", TELOPT(i));
+                   else if (TELCMD_OK(i))
+                       output_data("%s ", TELCMD(i));
+                   else
+                       output_data("%d ", i);
+                   if (TELOPT_OK(j))
+                       output_data("%s", TELOPT(j));
+                   else if (TELCMD_OK(j))
+                       output_data("%s", TELCMD(j));
+                   else
+                       output_data("%d", j);
+                   output_data(", not IAC SE!) ");
+               }
+           }
+           length -= 2;
+       }
+       if (length < 1) {
+           output_data("(Empty suboption??\?)");
+           return;
+       }
+       switch (pointer[0]) {
+       case TELOPT_TTYPE:
+           output_data("TERMINAL-TYPE ");
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               output_data("IS \"%.*s\"", length-2, (char *)pointer+2);
+               break;
+           case TELQUAL_SEND:
+               output_data("SEND");
+               break;
+           default:
+               output_data("- unknown qualifier %d (0x%x).",
+                   pointer[1], pointer[1]);
+           }
+           break;
+       case TELOPT_TSPEED:
+           output_data("TERMINAL-SPEED");
+           if (length < 2) {
+               output_data(" (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               output_data(" IS %.*s", length-2, (char *)pointer+2);
+               break;
+           default:
+               if (pointer[1] == 1)
+                   output_data(" SEND");
+               else
+                   output_data(" %d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++) {
+                   output_data(" ?%d?", pointer[i]);
+               }
+               break;
+           }
+           break;
+
+       case TELOPT_LFLOW:
+           output_data("TOGGLE-FLOW-CONTROL");
+           if (length < 2) {
+               output_data(" (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case LFLOW_OFF:
+               output_data(" OFF"); break;
+           case LFLOW_ON:
+               output_data(" ON"); break;
+           case LFLOW_RESTART_ANY:
+               output_data(" RESTART-ANY"); break;
+           case LFLOW_RESTART_XON:
+               output_data(" RESTART-XON"); break;
+           default:
+               output_data(" %d (unknown)", pointer[1]);
+           }
+           for (i = 2; i < length; i++)
+               output_data(" ?%d?", pointer[i]);
+           break;
+
+       case TELOPT_NAWS:
+           output_data("NAWS");
+           if (length < 2) {
+               output_data(" (empty suboption??\?)");
+               break;
+           }
+           if (length == 2) {
+               output_data(" ?%d?", pointer[1]);
+               break;
+           }
+           output_data(" %d %d (%d)",
+               pointer[1], pointer[2],
+               (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
+           if (length == 4) {
+               output_data(" ?%d?", pointer[3]);
+               break;
+           }
+           output_data(" %d %d (%d)", pointer[3], pointer[4],
+               (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
+           for (i = 5; i < length; i++) {
+               output_data(" ?%d?", pointer[i]);
+           }
+           break;
+
+       case TELOPT_LINEMODE:
+           output_data("LINEMODE ");
+           if (length < 2) {
+               output_data(" (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case WILL:
+               output_data("WILL ");
+               goto common;
+           case WONT:
+               output_data("WONT ");
+               goto common;
+           case DO:
+               output_data("DO ");
+               goto common;
+           case DONT:
+               output_data("DONT ");
+           common:
+               if (length < 3) {
+                   output_data("(no option??\?)");
+                   break;
+               }
+               switch (pointer[2]) {
+               case LM_FORWARDMASK:
+                   output_data("Forward Mask");
+                   for (i = 3; i < length; i++)
+                       output_data(" %x", pointer[i]);
+                   break;
+               default:
+                   output_data("%d (unknown)", pointer[2]);
+                   for (i = 3; i < length; i++)
+                       output_data(" %d", pointer[i]);
+                   break;
+               }
+               break;
+
+           case LM_SLC:
+               output_data("SLC");
+               for (i = 2; i < length - 2; i += 3) {
+                   if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
+                       output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC]));
+                   else
+                       output_data(" %d", pointer[i+SLC_FUNC]);
+                   switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
+                   case SLC_NOSUPPORT:
+                       output_data(" NOSUPPORT"); break;
+                   case SLC_CANTCHANGE:
+                       output_data(" CANTCHANGE"); break;
+                   case SLC_VARIABLE:
+                       output_data(" VARIABLE"); break;
+                   case SLC_DEFAULT:
+                       output_data(" DEFAULT"); break;
+                   }
+                   output_data("%s%s%s",
+                       pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
+                       pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
+                       pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
+                   if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
+                                               SLC_FLUSHOUT| SLC_LEVELBITS)) {
+                       output_data("(0x%x)", pointer[i+SLC_FLAGS]);
+                   }
+                   output_data(" %d;", pointer[i+SLC_VALUE]);
+                   if ((pointer[i+SLC_VALUE] == IAC) &&
+                       (pointer[i+SLC_VALUE+1] == IAC))
+                               i++;
+               }
+               for (; i < length; i++)
+                   output_data(" ?%d?", pointer[i]);
+               break;
+
+           case LM_MODE:
+               output_data("MODE ");
+               if (length < 3) {
+                   output_data("(no mode??\?)");
+                   break;
+               }
+               {
+                   char tbuf[32];
+
+                   (void)snprintf(tbuf, sizeof tbuf, "%s%s%s%s%s",
+                       pointer[2]&MODE_EDIT ? "|EDIT" : "",
+                       pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
+                       pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
+                       pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
+                       pointer[2]&MODE_ACK ? "|ACK" : "");
+                   output_data("%s", tbuf[1] ? &tbuf[1] : "0");
+               }
+               if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK))
+                   output_data(" (0x%x)", pointer[2]);
+               for (i = 3; i < length; i++)
+                   output_data(" ?0x%x?", pointer[i]);
+               break;
+           default:
+               output_data("%d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++)
+                   output_data(" %d", pointer[i]);
+           }
+           break;
+
+       case TELOPT_STATUS: {
+           const char *cp;
+           int j, k;
+
+           output_data("STATUS");
+
+           switch (pointer[1]) {
+           default:
+               if (pointer[1] == TELQUAL_SEND)
+                   output_data(" SEND");
+               else
+                   output_data(" %d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++)
+                   output_data(" ?%d?", pointer[i]);
+               break;
+           case TELQUAL_IS:
+               output_data(" IS\r\n");
+
+               for (i = 2; i < length; i++) {
+                   switch(pointer[i]) {
+                   case DO:    cp = "DO"; goto common2;
+                   case DONT:  cp = "DONT"; goto common2;
+                   case WILL:  cp = "WILL"; goto common2;
+                   case WONT:  cp = "WONT"; goto common2;
+                   common2:
+                       i++;
+                       if (TELOPT_OK(pointer[i]))
+                           output_data(" %s %s", cp, TELOPT(pointer[i]));
+                       else
+                           output_data(" %s %d", cp, pointer[i]);
+
+                       output_data("\r\n");
+                       break;
+
+                   case SB:
+                       output_data(" SB ");
+                       i++;
+                       j = k = i;
+                       while (j < length) {
+                           if (pointer[j] == SE) {
+                               if (j+1 == length)
+                                   break;
+                               if (pointer[j+1] == SE)
+                                   j++;
+                               else
+                                   break;
+                           }
+                           pointer[k++] = pointer[j++];
+                       }
+                       printsub(0, &pointer[i], k - i);
+                       if (i < length) {
+                           output_data(" SE");
+                           i = j;
+                       } else
+                           i = j - 1;
+
+                       output_data("\r\n");
+
+                       break;
+
+                   default:
+                       output_data(" %d", pointer[i]);
+                       break;
+                   }
+               }
+               break;
+           }
+           break;
+         }
+
+       case TELOPT_XDISPLOC:
+           output_data("X-DISPLAY-LOCATION ");
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               output_data("IS \"%.*s\"", length - 2, (char *)pointer + 2);
+               break;
+           case TELQUAL_SEND:
+               output_data("SEND");
+               break;
+           default:
+               output_data("- unknown qualifier %d (0x%x).",
+                   pointer[1], pointer[1]);
+           }
+           break;
+
+       case TELOPT_NEW_ENVIRON:
+           output_data("NEW-ENVIRON ");
+           goto env_common1;
+       case TELOPT_OLD_ENVIRON:
+           output_data("OLD-ENVIRON");
+       env_common1:
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               output_data("IS ");
+               goto env_common;
+           case TELQUAL_SEND:
+               output_data("SEND ");
+               goto env_common;
+           case TELQUAL_INFO:
+               output_data("INFO ");
+           env_common:
+               {
+                   static const char NQ[] = "\" ";
+                   const char *noquote = NQ;
+                   for (i = 2; i < length; i++ ) {
+                       switch (pointer[i]) {
+                       case NEW_ENV_VAR:
+                           output_data("%sVAR ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       case NEW_ENV_VALUE:
+                           output_data("%sVALUE ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       case ENV_ESC:
+                           output_data("%sESC ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       case ENV_USERVAR:
+                           output_data("%sUSERVAR ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       default:
+                           if (isprint(pointer[i]) && pointer[i] != '"') {
+                               if (*noquote) {
+                                   output_data("\"");
+                                   noquote = "";
+                               }
+                               output_data("%c", pointer[i]);
+                           } else {
+                               output_data("%s%03o ", noquote, pointer[i]);
+                               noquote = NQ;
+                           }
+                           break;
+                       }
+                   }
+                   if (!noquote)
+                       output_data("\"");
+                   break;
+               }
+           }
+           break;
+
+#ifdef AUTHENTICATION
+       case TELOPT_AUTHENTICATION:
+           output_data("AUTHENTICATION");
+
+           if (length < 2) {
+               output_data(" (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case TELQUAL_REPLY:
+           case TELQUAL_IS:
+               output_data(" %s ", (pointer[1] == TELQUAL_IS) ?
+                   "IS" : "REPLY");
+               if (AUTHTYPE_NAME_OK(pointer[2]))
+                   output_data("%s ", AUTHTYPE_NAME(pointer[2]));
+               else
+                   output_data("%d ", pointer[2]);
+               if (length < 3) {
+                   output_data("(partial suboption??\?)");
+                   break;
+               }
+               output_data("%s|%s",
+                       ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
+                       "CLIENT" : "SERVER",
+                       ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+                       "MUTUAL" : "ONE-WAY");
+
+               auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
+               output_data("%s", buf);
+               break;
+
+           case TELQUAL_SEND:
+               i = 2;
+               output_data(" SEND ");
+               while (i < length) {
+                   if (AUTHTYPE_NAME_OK(pointer[i]))
+                       output_data("%s ", AUTHTYPE_NAME(pointer[i]));
+                   else
+                       output_data("%d ", pointer[i]);
+                   if (++i >= length) {
+                       output_data("(partial suboption??\?)");
+                       break;
+                   }
+                   output_data("%s|%s ",
+                       ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
+                                                       "CLIENT" : "SERVER",
+                       ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+                                                       "MUTUAL" : "ONE-WAY");
+                   ++i;
+               }
+               break;
+
+           case TELQUAL_NAME:
+               i = 2;
+               output_data(" NAME \"");
+               while (i < length) {
+                   if (isprint(pointer[i]))
+                       output_data("%c", pointer[i++]);
+                   else
+                       output_data("\"%03o\"",pointer[i++]);
+               }
+               output_data("\"");
+               break;
+
+           default:
+               for (i = 2; i < length; i++)
+                   output_data(" ?%d?", pointer[i]);
+               break;
+           }
+           break;
+#endif
+
+#ifdef ENCRYPTION
+       case TELOPT_ENCRYPT:
+           output_data("ENCRYPT");
+           if (length < 2) {
+               output_data(" (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case ENCRYPT_START:
+               output_data(" START");
+               break;
+
+           case ENCRYPT_END:
+               output_data(" END");
+               break;
+
+           case ENCRYPT_REQSTART:
+               output_data(" REQUEST-START");
+               break;
+
+           case ENCRYPT_REQEND:
+               output_data(" REQUEST-END");
+               break;
+
+           case ENCRYPT_IS:
+           case ENCRYPT_REPLY:
+               output_data(" %s ", (pointer[1] == ENCRYPT_IS) ?
+                   "IS" : "REPLY");
+               if (length < 3) {
+                       output_data(" (partial suboption??\?)");
+                       break;
+               }
+               if (ENCTYPE_NAME_OK(pointer[2]))
+                       output_data("%s ", ENCTYPE_NAME(pointer[2]));
+               else
+                       output_data(" %d (unknown)", pointer[2]);
+
+               encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
+               output_data("%s", buf);
+               break;
+
+           case ENCRYPT_SUPPORT:
+               i = 2;
+               output_data(" SUPPORT ");
+               while (i < length) {
+                       if (ENCTYPE_NAME_OK(pointer[i]))
+                               output_data("%s ", ENCTYPE_NAME(pointer[i]));
+                       else
+                               output_data("%d ", pointer[i]);
+                       i++;
+               }
+               break;
+
+           case ENCRYPT_ENC_KEYID:
+               output_data(" ENC_KEYID");
+               goto encommon;
+
+           case ENCRYPT_DEC_KEYID:
+               output_data(" DEC_KEYID");
+               goto encommon;
+
+           default:
+               output_data(" %d (unknown)", pointer[1]);
+           encommon:
+               for (i = 2; i < length; i++)
+                       output_data(" %d", pointer[i]);
+               break;
+           }
+           break;
+#endif /* ENCRYPTION */
+
+       default:
+           if (TELOPT_OK(pointer[0]))
+               output_data("%s (unknown)", TELOPT(pointer[0]));
+           else
+               output_data("%d (unknown)", pointer[i]);
+           for (i = 1; i < length; i++)
+               output_data(" %d", pointer[i]);
+           break;
+       }
+       output_data("\r\n");
+}
+
+/*
+ * Dump a data buffer in hex and ascii to the output data stream.
+ */
+void
+printdata(const char *tag, char *ptr, int cnt)
+{
+       int i;
+       char xbuf[30];
+
+       while (cnt) {
+               /* flush net output buffer if no room for new data) */
+               if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
+                       netflush();
+               }
+
+               /* add a line of output */
+               output_data("%s: ", tag);
+               for (i = 0; i < 20 && cnt; i++) {
+                       output_data("%02x", *ptr);
+                       if (isprint((unsigned char)*ptr)) {
+                               xbuf[i] = *ptr;
+                       } else {
+                               xbuf[i] = '.';
+                       }
+                       if (i % 2)
+                               output_data(" ");
+                       cnt--;
+                       ptr++;
+               }
+               xbuf[i] = '\0';
+               output_data(" %s\r\n", xbuf);
+       }
+}
+#endif /* DIAGNOSTICS */