]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD telnet(1) 62/3462/1
authorDavid van Moolenbroek <david@minix3.org>
Wed, 15 Feb 2017 17:41:07 +0000 (17:41 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 9 Mar 2017 23:40:16 +0000 (23:40 +0000)
Change-Id: Ib58b43cc9baabe183a59410212827f65ec117277

44 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
lib/Makefile
lib/libtelnet/Makefile [new file with mode: 0644]
lib/libtelnet/auth-proto.h [new file with mode: 0644]
lib/libtelnet/auth.c [new file with mode: 0644]
lib/libtelnet/auth.h [new file with mode: 0644]
lib/libtelnet/enc-proto.h [new file with mode: 0644]
lib/libtelnet/enc_des.c [new file with mode: 0644]
lib/libtelnet/encrypt.c [new file with mode: 0644]
lib/libtelnet/encrypt.h [new file with mode: 0644]
lib/libtelnet/forward.c [new file with mode: 0644]
lib/libtelnet/genget.c [new file with mode: 0644]
lib/libtelnet/getent.c [new file with mode: 0644]
lib/libtelnet/kerberos5.c [new file with mode: 0644]
lib/libtelnet/key-proto.h [new file with mode: 0644]
lib/libtelnet/misc-proto.h [new file with mode: 0644]
lib/libtelnet/misc.c [new file with mode: 0644]
lib/libtelnet/misc.h [new file with mode: 0644]
lib/libtelnet/pk.c [new file with mode: 0644]
lib/libtelnet/pk.h [new file with mode: 0644]
lib/libtelnet/shlib_version [new file with mode: 0644]
lib/libtelnet/spx.c [new file with mode: 0644]
lib/libtelnet/sra.c [new file with mode: 0644]
usr.bin/Makefile
usr.bin/telnet/Makefile [new file with mode: 0644]
usr.bin/telnet/README [new file with mode: 0644]
usr.bin/telnet/authenc.c [new file with mode: 0644]
usr.bin/telnet/commands.c [new file with mode: 0644]
usr.bin/telnet/defines.h [new file with mode: 0644]
usr.bin/telnet/externs.h [new file with mode: 0644]
usr.bin/telnet/general.h [new file with mode: 0644]
usr.bin/telnet/main.c [new file with mode: 0644]
usr.bin/telnet/network.c [new file with mode: 0644]
usr.bin/telnet/ring.c [new file with mode: 0644]
usr.bin/telnet/ring.h [new file with mode: 0644]
usr.bin/telnet/sys_bsd.c [new file with mode: 0644]
usr.bin/telnet/telnet.1 [new file with mode: 0644]
usr.bin/telnet/telnet.c [new file with mode: 0644]
usr.bin/telnet/terminal.c [new file with mode: 0644]
usr.bin/telnet/tn3270.c [new file with mode: 0644]
usr.bin/telnet/types.h [new file with mode: 0644]
usr.bin/telnet/utilities.c [new file with mode: 0644]

index b4c34051728a5d660fb71b1c8b76058859604552..6c02eb5cc4e7d084e2960700d98b347ac4861ecb 100644 (file)
 ./usr/bin/tcpdp                                         minix-base      obsolete
 ./usr/bin/tcpstat                                       minix-base      obsolete
 ./usr/bin/tee                                           minix-base
+./usr/bin/telnet                                        minix-base
 ./usr/bin/term                                          minix-base
 ./usr/bin/termcap                                       minix-base
 ./usr/bin/texi2dvi                                      minix-base
index 949bb1c01c9a92f7360b1df93648efbde3131bff..abfe3e6244c1a5ccc22d4c6c7cc309ab10d762b5 100644 (file)
 ./usr/libdata/debug/usr/bin/tcpdp.debug                 minix-debug     debug,obsolete
 ./usr/libdata/debug/usr/bin/tcpstat.debug               minix-debug     debug,obsolete
 ./usr/libdata/debug/usr/bin/tee.debug                   minix-debug     debug
+./usr/libdata/debug/usr/bin/telnet.debug                minix-debug     debug
 ./usr/libdata/debug/usr/bin/term.debug                  minix-debug     debug
 ./usr/libdata/debug/usr/bin/termcap.debug               minix-debug     debug
 ./usr/libdata/debug/usr/bin/texindex.debug              minix-debug     debug
index ebe1ab2bb3a28fa4c3672271770a24b1fc73ba84..c8019ee63ac8c070bd7768f120e4feebee5d291e 100644 (file)
 ./usr/man/man1/tail.1                                   minix-man
 ./usr/man/man1/tar.1                                    minix-man
 ./usr/man/man1/tee.1                                    minix-man
-./usr/man/man1/telnet.1                                 minix-man       obsolete
+./usr/man/man1/telnet.1                                 minix-man
 ./usr/man/man1/template.1                               minix-man       obsolete
 ./usr/man/man1/term.1                                   minix-man
 ./usr/man/man1/termcap.1                                minix-man
index d46144741503a7b17839f2475b6eabd24f244d23..2ac7b44196b9a05af039a5b93e58e711c220e802 100644 (file)
@@ -80,7 +80,7 @@ SUBDIR+=      \
                libintl libkvm libm \
                libpci libprop \
                libpuffs librmt \
-               libterminfo \
+               libtelnet libterminfo \
                libutil libwrap libz
 
 .if !defined(BSD_MK_COMPAT_FILE)
diff --git a/lib/libtelnet/Makefile b/lib/libtelnet/Makefile
new file mode 100644 (file)
index 0000000..fde8b8d
--- /dev/null
@@ -0,0 +1,38 @@
+#      from: @(#)Makefile      8.2 (Berkeley) 12/15/93
+#      $NetBSD: Makefile,v 1.36 2012/08/10 12:20:10 joerg Exp $
+
+USE_FORT?= yes # network protocol library
+
+LIBISPRIVATE=  yes
+
+.include <bsd.own.mk>
+
+WARNS?=        5
+
+LIB=   telnet
+SRCS=  auth.c encrypt.c genget.c getent.c misc.c
+
+CPPFLAGS+= -DHAS_CGETENT
+CPPFLAGS+= -I${.CURDIR}
+
+.if ${MKCRYPTO} != "no"
+SRCS+= enc_des.c
+CPPFLAGS+= -DENCRYPTION -DAUTHENTICATION
+CPPFLAGS+= -DDES_ENCRYPTION
+.endif
+
+.if ${USE_KERBEROS} != "no"
+SRCS+= kerberos5.c
+CPPFLAGS+= -DKRB5
+.endif
+
+.if ${USE_PAM} != "no" && ${MKCRYPTO} != "no"
+SRCS+= sra.c pk.c
+CPPFLAGS+= -DSRA
+.endif
+
+.for f in auth enc_des kerberos5 pk
+COPTS.${f}.c+=  -Wno-pointer-sign
+.endfor
+
+.include <bsd.lib.mk>
diff --git a/lib/libtelnet/auth-proto.h b/lib/libtelnet/auth-proto.h
new file mode 100644 (file)
index 0000000..4262553
--- /dev/null
@@ -0,0 +1,103 @@
+/*     $NetBSD: auth-proto.h,v 1.15 2006/03/20 21:23:47 christos 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.
+ *
+ *     from: @(#)auth-proto.h  8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifndef _LIBTELNET_AUTH_PROTO_H_
+#define _LIBTELNET_AUTH_PROTO_H_
+#include <sys/cdefs.h>
+
+#ifdef AUTHENTICATION
+Authenticator *findauthenticator(int, int);
+
+void auth_init(const char *, int);
+int auth_cmd(int, char **);
+void auth_request(void);
+void auth_send(unsigned char *, int);
+void auth_send_retry(void);
+void auth_is(unsigned char *, int);
+void auth_reply(unsigned char *, int);
+void auth_disable_name(char *);
+void auth_gen_printsub(unsigned char *, int, unsigned char *, int);
+
+int getauthmask(char *, int *);
+int auth_enable(char *);
+int auth_disable(char *);
+int auth_onoff(char *, int);
+int auth_togdebug(int);
+int auth_status(char *);
+void auth_name(unsigned char *, int);
+int auth_sendname(unsigned char *, int);
+void auth_finished(Authenticator *, int);
+int auth_wait(char *, size_t);
+void auth_debug(int);
+void auth_printsub(unsigned char *, int, unsigned char *, int);
+
+#ifdef KRB5
+int kerberos5_init(Authenticator *, int);
+int kerberos5_send(Authenticator *);
+void kerberos5_is(Authenticator *, unsigned char *, int);
+void kerberos5_reply(Authenticator *, unsigned char *, int);
+int kerberos5_status(Authenticator *, char *, size_t, int);
+void kerberos5_printsub(unsigned char *, int, unsigned char *, int);
+#endif
+
+#ifdef SRA
+int sra_init(Authenticator *, int);
+int sra_send(Authenticator *);
+void sra_is(Authenticator *, unsigned char *, int);
+void sra_reply(Authenticator *, unsigned char *, int);
+int sra_status(Authenticator *, char *, size_t, int);
+void sra_printsub(unsigned char *, int, unsigned char *, int);
+#endif
+
+#endif /* AUTHENTICATION */
+#endif /* _LIBTELNET_AUTH_PROTO_H_ */
diff --git a/lib/libtelnet/auth.c b/lib/libtelnet/auth.c
new file mode 100644 (file)
index 0000000..3db6333
--- /dev/null
@@ -0,0 +1,617 @@
+/*     $NetBSD: auth.c,v 1.21 2012/03/21 05:33:27 matt 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[] = "@(#)auth.c     8.3 (Berkeley) 5/30/95"
+#else
+__RCSID("$NetBSD: auth.c,v 1.21 2012/03/21 05:33:27 matt Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+
+#ifdef AUTHENTICATION
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#define        AUTH_NAMES
+#include <arpa/telnet.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef NO_STRING_H
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+
+#include "encrypt.h"
+#include "auth.h"
+#include "misc-proto.h"
+#include "auth-proto.h"
+
+#define        typemask(x)             (1<<((x)-1))
+
+#ifdef RSA_ENCPWD
+extern rsaencpwd_init();
+extern rsaencpwd_send();
+extern rsaencpwd_is();
+extern rsaencpwd_reply();
+extern rsaencpwd_status();
+extern rsaencpwd_printsub();
+#endif
+
+int auth_debug_mode = 0;
+static         const char      *Name = "Noname";
+static int     Server = 0;
+static Authenticator   *authenticated = 0;
+static int     authenticating = 0;
+static int     validuser = 0;
+static unsigned char   _auth_send_data[256];
+static unsigned char   *auth_send_data;
+static int     auth_send_cnt = 0;
+
+static void auth_intr(int);
+
+/*
+ * Authentication types supported.  Plese note that these are stored
+ * in priority order, i.e. try the first one first.
+ */
+Authenticator authenticators[] = {
+#ifdef SPX
+       { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
+                               spx_init,
+                               spx_send,
+                               spx_is,
+                               spx_reply,
+                               spx_status,
+                               spx_printsub },
+       { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
+                               spx_init,
+                               spx_send,
+                               spx_is,
+                               spx_reply,
+                               spx_status,
+                               spx_printsub },
+#endif
+#ifdef KRB5
+# ifdef        ENCRYPTION
+       { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
+                               kerberos5_init,
+                               kerberos5_send,
+                               kerberos5_is,
+                               kerberos5_reply,
+                               kerberos5_status,
+                               kerberos5_printsub },
+# endif        /* ENCRYPTION */
+       { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
+                               kerberos5_init,
+                               kerberos5_send,
+                               kerberos5_is,
+                               kerberos5_reply,
+                               kerberos5_status,
+                               kerberos5_printsub },
+#endif
+#ifdef RSA_ENCPWD
+       { AUTHTYPE_RSA_ENCPWD, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
+                               rsaencpwd_init,
+                               rsaencpwd_send,
+                               rsaencpwd_is,
+                               rsaencpwd_reply,
+                               rsaencpwd_status,
+                               rsaencpwd_printsub },
+#endif
+#ifdef SRA
+       { AUTHTYPE_SRA, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
+                               sra_init,
+                               sra_send,
+                               sra_is,
+                               sra_reply,
+                               sra_status,
+                               sra_printsub },
+
+#endif
+       { 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static Authenticator NoAuth = { .type = 0 };
+
+static int     i_support = 0;
+static int     i_wont_support = 0;
+
+Authenticator *
+findauthenticator(int type, int way)
+{
+       Authenticator *ap = authenticators;
+
+       while (ap->type && (ap->type != type || ap->way != way))
+               ++ap;
+       return(ap->type ? ap : 0);
+}
+
+void
+auth_init(const char *name, int server)
+{
+       Authenticator *ap = authenticators;
+
+       Server = server;
+       Name = name;
+
+       i_support = 0;
+       authenticated = 0;
+       authenticating = 0;
+       while (ap->type) {
+               if (!ap->init || (*ap->init)(ap, server)) {
+                       i_support |= typemask(ap->type);
+                       if (auth_debug_mode)
+                               printf(">>>%s: I support auth type %d %d\r\n",
+                                       Name,
+                                       ap->type, ap->way);
+               }
+               else if (auth_debug_mode)
+                       printf(">>>%s: Init failed: auth type %d %d\r\n",
+                               Name, ap->type, ap->way);
+               ++ap;
+       }
+}
+
+void
+auth_disable_name(char *name)
+{
+       int x;
+       for (x = 0; x < AUTHTYPE_CNT; ++x) {
+               if (AUTHTYPE_NAME(x) && !strcasecmp(name, AUTHTYPE_NAME(x))) {
+                       i_wont_support |= typemask(x);
+                       break;
+               }
+       }
+}
+
+int
+getauthmask(char *type, int *maskp)
+{
+       register int x;
+
+       if (AUTHTYPE_NAME(0) && !strcasecmp(type, AUTHTYPE_NAME(0))) {
+               *maskp = -1;
+               return(1);
+       }
+
+       for (x = 1; x < AUTHTYPE_CNT; ++x) {
+               if (AUTHTYPE_NAME(x) && !strcasecmp(type, AUTHTYPE_NAME(x))) {
+                       *maskp = typemask(x);
+                       return(1);
+               }
+       }
+       return(0);
+}
+
+int
+auth_enable(char *type)
+{
+       return(auth_onoff(type, 1));
+}
+
+int
+auth_disable(char *type)
+{
+       return(auth_onoff(type, 0));
+}
+
+int
+auth_onoff(char *type, int on)
+{
+       int i, mask = -1;
+       Authenticator *ap;
+
+       if (!strcasecmp(type, "?") || !strcasecmp(type, "help")) {
+               printf("auth %s 'type'\n", on ? "enable" : "disable");
+               printf("Where 'type' is one of:\n");
+               printf("\t%s\n", AUTHTYPE_NAME(0));
+               mask = 0;
+               for (ap = authenticators; ap->type; ap++) {
+                       if ((mask & (i = typemask(ap->type))) != 0)
+                               continue;
+                       mask |= i;
+                       printf("\t%s\n", AUTHTYPE_NAME(ap->type));
+               }
+               return(0);
+       }
+
+       if (!getauthmask(type, &mask)) {
+               printf("%s: invalid authentication type\n", type);
+               return(0);
+       }
+       if (on)
+               i_wont_support &= ~mask;
+       else
+               i_wont_support |= mask;
+       return(1);
+}
+
+int
+auth_togdebug(int on)
+{
+       if (on < 0)
+               auth_debug_mode ^= 1;
+       else
+               auth_debug_mode = on;
+       printf("auth debugging %s\n", auth_debug_mode ? "enabled" : "disabled");
+       return(1);
+}
+
+int
+auth_status(char *s)
+{
+       Authenticator *ap;
+       int i, mask;
+
+       if (i_wont_support == -1)
+               printf("Authentication disabled\n");
+       else
+               printf("Authentication enabled\n");
+
+       mask = 0;
+       for (ap = authenticators; ap->type; ap++) {
+               if ((mask & (i = typemask(ap->type))) != 0)
+                       continue;
+               mask |= i;
+               printf("%s: %s\n", AUTHTYPE_NAME(ap->type),
+                       (i_wont_support & typemask(ap->type)) ?
+                                       "disabled" : "enabled");
+       }
+       return(1);
+}
+
+/*
+ * This routine is called by the server to start authentication
+ * negotiation.
+ */
+void
+auth_request(void)
+{
+       static unsigned char str_request[64] = { IAC, SB,
+                                                TELOPT_AUTHENTICATION,
+                                                TELQUAL_SEND, };
+       Authenticator *ap = authenticators;
+       unsigned char *e = str_request + 4;
+
+       if (!authenticating) {
+               authenticating = 1;
+               while (ap->type) {
+                       if (i_support & ~i_wont_support & typemask(ap->type)) {
+                               if (auth_debug_mode) {
+                                       printf(">>>%s: Sending type %d %d\r\n",
+                                               Name, ap->type, ap->way);
+                               }
+                               *e++ = ap->type;
+                               *e++ = ap->way;
+                       }
+                       ++ap;
+               }
+               *e++ = IAC;
+               *e++ = SE;
+               telnet_net_write(str_request, e - str_request);
+               printsub('>', &str_request[2], e - str_request - 2);
+       }
+}
+
+/*
+ * This is called when an AUTH SEND is received.
+ * It should never arrive on the server side (as only the server can
+ * send an AUTH SEND).
+ * You should probably respond to it if you can...
+ *
+ * If you want to respond to the types out of order (i.e. even
+ * if he sends  LOGIN KERBEROS and you support both, you respond
+ * with KERBEROS instead of LOGIN (which is against what the
+ * protocol says)) you will have to hack this code...
+ */
+void
+auth_send(unsigned char *data, int cnt)
+{
+       Authenticator *ap;
+       static unsigned char str_none[] = { IAC, SB, TELOPT_AUTHENTICATION,
+                                           TELQUAL_IS, AUTHTYPE_NULL, 0,
+                                           IAC, SE };
+       if (Server) {
+               if (auth_debug_mode) {
+                       printf(">>>%s: auth_send called!\r\n", Name);
+               }
+               return;
+       }
+
+       if (auth_debug_mode) {
+               printf(">>>%s: auth_send got:", Name);
+               printd(data, cnt); printf("\r\n");
+       }
+
+       /*
+        * Save the data, if it is new, so that we can continue looking
+        * at it if the authorization we try doesn't work
+        */
+       if (data < _auth_send_data ||
+           data > _auth_send_data + sizeof(_auth_send_data)) {
+               auth_send_cnt = (size_t)cnt > sizeof(_auth_send_data)
+                                       ? sizeof(_auth_send_data)
+                                       : (size_t)cnt;
+               memmove(_auth_send_data, data, auth_send_cnt);
+               auth_send_data = _auth_send_data;
+       } else {
+               /*
+                * This is probably a no-op, but we just make sure
+                */
+               auth_send_data = data;
+               auth_send_cnt = cnt;
+       }
+       while ((auth_send_cnt -= 2) >= 0) {
+               if (auth_debug_mode)
+                       printf(">>>%s: He supports %d\r\n",
+                               Name, *auth_send_data);
+               if ((i_support & ~i_wont_support) & typemask(*auth_send_data)) {
+                       ap = findauthenticator(auth_send_data[0],
+                                              auth_send_data[1]);
+                       if (ap && ap->send) {
+                               if (auth_debug_mode)
+                                       printf(">>>%s: Trying %d %d\r\n",
+                                               Name, auth_send_data[0],
+                                                       auth_send_data[1]);
+                               if ((*ap->send)(ap)) {
+                                       /*
+                                        * Okay, we found one we like
+                                        * and did it.
+                                        * we can go home now.
+                                        */
+                                       if (auth_debug_mode)
+                                               printf(">>>%s: Using type %d\r\n",
+                                                       Name, *auth_send_data);
+                                       auth_send_data += 2;
+                                       return;
+                               }
+                       }
+                       /* else
+                        *      just continue on and look for the
+                        *      next one if we didn't do anything.
+                        */
+               }
+               auth_send_data += 2;
+       }
+       telnet_net_write(str_none, sizeof(str_none));
+       printsub('>', &str_none[2], sizeof(str_none) - 2);
+       if (auth_debug_mode)
+               printf(">>>%s: Sent failure message\r\n", Name);
+       auth_finished(0, AUTH_REJECT);
+#ifdef KANNAN
+       /*
+        *  We requested strong authentication, however no mechanisms worked.
+        *  Therefore, exit on client end.
+        */
+       printf("Unable to securely authenticate user ... exit\n");
+       exit(0);
+#endif /* KANNAN */
+}
+
+void
+auth_send_retry(void)
+{
+       /*
+        * if auth_send_cnt <= 0 then auth_send will end up rejecting
+        * the authentication and informing the other side of this.
+        */
+       auth_send(auth_send_data, auth_send_cnt);
+}
+
+void
+auth_is(unsigned char *data, int cnt)
+{
+       Authenticator *ap;
+
+       if (cnt < 2)
+               return;
+
+       if (data[0] == AUTHTYPE_NULL) {
+               auth_finished(0, AUTH_REJECT);
+               return;
+       }
+
+       if ((ap = findauthenticator(data[0], data[1])) != NULL) {
+               if (ap->is)
+                       (*ap->is)(ap, data+2, cnt-2);
+       } else if (auth_debug_mode)
+               printf(">>>%s: Invalid authentication in IS: %d\r\n",
+                       Name, *data);
+}
+
+void
+auth_reply(unsigned char *data, int cnt)
+{
+       Authenticator *ap;
+
+       if (cnt < 2)
+               return;
+
+       if ((ap = findauthenticator(data[0], data[1])) != NULL) {
+               if (ap->reply)
+                       (*ap->reply)(ap, data+2, cnt-2);
+       } else if (auth_debug_mode)
+               printf(">>>%s: Invalid authentication in SEND: %d\r\n",
+                       Name, *data);
+}
+
+void
+auth_name(unsigned char *data, int cnt)
+{
+       unsigned char savename[256];
+
+       if (cnt < 1) {
+               if (auth_debug_mode)
+                       printf(">>>%s: Empty name in NAME\r\n", Name);
+               return;
+       }
+       if ((size_t)cnt > sizeof(savename) - 1) {
+               if (auth_debug_mode)
+                       printf(">>>%s: Name in NAME (%d) exceeds %ld length\r\n",
+                                       Name, cnt, (long)sizeof(savename)-1);
+               return;
+       }
+       memmove((void *)savename, (void *)data, cnt);
+       savename[cnt] = '\0';   /* Null terminate */
+       if (auth_debug_mode)
+               printf(">>>%s: Got NAME [%s]\r\n", Name, savename);
+       auth_encrypt_user(savename);
+}
+
+int
+auth_sendname(unsigned char *cp, int len)
+{
+       static unsigned char str_request[256+6]
+                       = { IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_NAME, };
+       register unsigned char *e = str_request + 4;
+       register unsigned char *ee = &str_request[sizeof(str_request)-2];
+
+       while (--len >= 0) {
+               if ((*e++ = *cp++) == IAC)
+                       *e++ = IAC;
+               if (e >= ee)
+                       return(0);
+       }
+       *e++ = IAC;
+       *e++ = SE;
+       telnet_net_write(str_request, e - str_request);
+       printsub('>', &str_request[2], e - &str_request[2]);
+       return(1);
+}
+
+void
+auth_finished(Authenticator *ap, int result)
+{
+       if (!(authenticated = ap))
+               authenticated = &NoAuth;
+       validuser = result;
+}
+
+       /* ARGSUSED */
+static void
+auth_intr(int sig)
+{
+       auth_finished(0, AUTH_REJECT);
+}
+
+int
+auth_wait(char *name, size_t l)
+{
+       if (auth_debug_mode)
+               printf(">>>%s: in auth_wait.\r\n", Name);
+
+       if (Server && !authenticating)
+               return(0);
+
+       (void) signal(SIGALRM, auth_intr);
+       alarm(30);
+       while (!authenticated)
+               if (telnet_spin())
+                       break;
+       alarm(0);
+       (void) signal(SIGALRM, SIG_DFL);
+
+       /*
+        * Now check to see if the user is valid or not
+        */
+       if (!authenticated || authenticated == &NoAuth)
+               return(AUTH_REJECT);
+
+       if (validuser == AUTH_VALID)
+               validuser = AUTH_USER;
+
+       if (authenticated->status)
+               validuser = (*authenticated->status)(authenticated,
+                                                    name, l, validuser);
+       return(validuser);
+}
+
+void
+auth_debug(int mode)
+{
+       auth_debug_mode = mode;
+}
+
+void
+auth_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+       Authenticator *ap;
+
+       if ((ap = findauthenticator(data[1], data[2])) && ap->printsub)
+               (*ap->printsub)(data, cnt, buf, buflen);
+       else
+               auth_gen_printsub(data, cnt, buf, buflen);
+}
+
+void
+auth_gen_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+       register unsigned char *cp;
+       unsigned char tbuf[16];
+
+       cnt -= 3;
+       data += 3;
+       buf[buflen-1] = '\0';
+       buf[buflen-2] = '*';
+       buflen -= 2;
+       for (; cnt > 0; cnt--, data++) {
+               snprintf((char *)tbuf, sizeof(tbuf), " %d", *data);
+               for (cp = tbuf; *cp && buflen > 0; --buflen)
+                       *buf++ = *cp++;
+               if (buflen <= 0)
+                       return;
+       }
+       *buf = '\0';
+}
+#endif
diff --git a/lib/libtelnet/auth.h b/lib/libtelnet/auth.h
new file mode 100644 (file)
index 0000000..aab6f93
--- /dev/null
@@ -0,0 +1,80 @@
+/*     $NetBSD: auth.h,v 1.11 2005/02/06 05:53:07 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.
+ *
+ *     from: @(#)auth.h        8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifndef        __AUTH__
+#define        __AUTH__
+
+#define        AUTH_REJECT     0       /* Rejected */
+#define        AUTH_UNKNOWN    1       /* We don't know who he is, but he's okay */
+#define        AUTH_OTHER      2       /* We know him, but not his name */
+#define        AUTH_USER       3       /* We know he name */
+#define        AUTH_VALID      4       /* We know him, and he needs no password */
+
+typedef struct XauthP {
+       int     type;
+       int     way;
+       int     (*init)(struct XauthP *, int);
+       int     (*send)(struct XauthP *);
+       void    (*is)(struct XauthP *, unsigned char *, int);
+       void    (*reply)(struct XauthP *, unsigned char *, int);
+       int     (*status)(struct XauthP *, char *, size_t, int);
+       void    (*printsub)(unsigned char *, int, unsigned char *, int);
+} Authenticator;
+
+#include "auth-proto.h"
+
+#define OPTS_FORWARD_CREDS     0x00000002
+#define OPTS_FORWARDABLE_CREDS 0x00000001
+
+extern int auth_debug_mode;
+#endif
diff --git a/lib/libtelnet/enc-proto.h b/lib/libtelnet/enc-proto.h
new file mode 100644 (file)
index 0000000..5e62de0
--- /dev/null
@@ -0,0 +1,140 @@
+/*     $NetBSD: enc-proto.h,v 1.9 2012/01/09 15:25:33 christos 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.
+ *
+ *     from: @(#)enc-proto.h   8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifdef ENCRYPTION
+#include <sys/cdefs.h>
+
+Encryptions *findencryption(int);
+Encryptions *finddecryption(int);
+void encrypt_init(const char *, int);
+void encrypt_list_types(void);
+int EncryptEnable(char *, char *);
+int EncryptDisable(char *, char *);
+int EncryptType(char *, char *);
+int EncryptStart(char *);
+int EncryptStartInput(void);
+int EncryptStartOutput(void);
+int EncryptStop(char *);
+int EncryptStopInput(void);
+int EncryptStopOutput(void);
+int EncryptStatus(void);
+void encrypt_send_support(void);
+int EncryptDebug(int);
+int EncryptVerbose(int);
+int EncryptAutoEnc(int);
+int EncryptAutoDec(int);
+void encrypt_support(unsigned char *, int);
+void encrypt_is(unsigned char *, int);
+void encrypt_reply(unsigned char *, int);
+void encrypt_start(unsigned char *, int);
+void encrypt_session_key(Session_Key *, int);
+void encrypt_end(void);
+void encrypt_request_end(void);
+void encrypt_request_start(unsigned char *, int);
+void encrypt_enc_keyid(unsigned char *, int);
+void encrypt_dec_keyid(unsigned char *, int);
+struct key_info;
+void encrypt_keyid(struct key_info *, unsigned char *, int);
+void encrypt_send_keyid(int, const unsigned char *, int, int);
+void encrypt_auto(int);
+void decrypt_auto(int);
+void encrypt_start_output(int);
+void encrypt_send_end(void);
+void encrypt_send_request_start(void);
+void encrypt_send_request_end(void);
+void encrypt_wait(void);
+void encrypt_debug(int);
+void encrypt_gen_printsub(unsigned char *, int, unsigned char *, int );
+void encrypt_printsub(unsigned char *, int, unsigned char *, int );
+
+#ifdef TELENTD
+void encrypt_wait(void);
+#else
+void printsub(int, unsigned char *, int);
+int encrypt_cmd(int, char **);
+void encrypt_display(void);
+#endif
+
+void krbdes_encrypt(unsigned char *, int);
+int krbdes_decrypt(int);
+int krbdes_is(unsigned char *, int);
+int krbdes_reply(unsigned char *, int);
+void krbdes_init(int);
+int krbdes_start(int, int);
+void krbdes_session(Session_Key *, int);
+void krbdes_printsub(unsigned char *, int, unsigned char *, int);
+
+void cfb64_encrypt(unsigned char *, int);
+int cfb64_decrypt(int);
+void cfb64_init(int);
+int cfb64_start(int, int);
+int cfb64_is(unsigned char *, int);
+int cfb64_reply(unsigned char *, int);
+void cfb64_session(Session_Key *, int);
+int cfb64_keyid(int, unsigned char *, int *);
+void cfb64_printsub(unsigned char *, int, unsigned char *, int);
+
+void ofb64_encrypt(unsigned char *, int);
+int ofb64_decrypt(int);
+void ofb64_init(int);
+int ofb64_start(int, int);
+int ofb64_is(unsigned char *, int);
+int ofb64_reply(unsigned char *, int);
+void ofb64_session(Session_Key *, int);
+int ofb64_keyid(int, unsigned char *, int *);
+void ofb64_printsub(unsigned char *, int, unsigned char *, int);
+
+void fb64_printsub(const unsigned char *, int, unsigned char *, int,
+    const unsigned char *);
+
+#endif /* ENCRYPTION */
diff --git a/lib/libtelnet/enc_des.c b/lib/libtelnet/enc_des.c
new file mode 100644 (file)
index 0000000..e1cde4d
--- /dev/null
@@ -0,0 +1,665 @@
+/*     $NetBSD: enc_des.c,v 1.16 2012/03/21 05:33:27 matt 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[] = "@(#)enc_des.c  8.3 (Berkeley) 5/30/95"; */
+#else
+__RCSID("$NetBSD: enc_des.c,v 1.16 2012/03/21 05:33:27 matt Exp $");
+#endif
+#endif /* not lint */
+
+#ifdef ENCRYPTION
+# ifdef        AUTHENTICATION
+#  ifdef DES_ENCRYPTION
+#include <arpa/telnet.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <des.h>
+#include "encrypt.h"
+#include "key-proto.h"
+#include "misc-proto.h"
+
+#define        CFB     0
+#define        OFB     1
+
+#define        NO_SEND_IV      1
+#define        NO_RECV_IV      2
+#define        NO_KEYID        4
+#define        IN_PROGRESS     (NO_SEND_IV|NO_RECV_IV|NO_KEYID)
+#define        SUCCESS         0
+#define        FAILED          -1
+
+
+struct fb {
+       Block krbdes_key;
+       Schedule krbdes_sched;
+       Block temp_feed;
+       unsigned char fb_feed[64];
+       int need_start;
+       int state[2];
+       int keyid[2];
+       int once;
+       struct stinfo {
+               Block           str_output;
+               Block           str_feed;
+               Block           str_iv;
+               Block           str_ikey;
+               Schedule        str_sched;
+               int             str_index;
+               int             str_flagshift;
+       } streams[2];
+};
+
+static struct fb fb[2];
+
+struct keyidlist {
+       const char      *keyid;
+       int     keyidlen;
+       char    *key;
+       int     keylen;
+       int     flags;
+} keyidlist [] = {
+       { "\0", 1, 0, 0, 0 },           /* default key of zero */
+       { 0, 0, 0, 0, 0 }
+};
+
+#define        KEYFLAG_MASK    03
+
+#define        KEYFLAG_NOINIT  00
+#define        KEYFLAG_INIT    01
+#define        KEYFLAG_OK      02
+#define        KEYFLAG_BAD     03
+
+#define        KEYFLAG_SHIFT   2
+
+#define        SHIFT_VAL(a,b)  (KEYFLAG_SHIFT*((a)+((b)*2)))
+
+#define        FB64_IV         1
+#define        FB64_IV_OK      2
+#define        FB64_IV_BAD     3
+
+
+void fb64_stream_iv(Block, struct stinfo *);
+void fb64_init(struct fb *);
+static int fb64_start(struct fb *, int, int);
+int fb64_is(unsigned char *, int, struct fb *);
+int fb64_reply(unsigned char *, int, struct fb *);
+static void fb64_session(Session_Key *, int, struct fb *);
+void fb64_stream_key(Block *, struct stinfo *);
+int fb64_keyid(int, unsigned char *, int *, struct fb *);
+
+void
+cfb64_init(int server)
+{
+       fb64_init(&fb[CFB]);
+       fb[CFB].fb_feed[4] = ENCTYPE_DES_CFB64;
+       fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, CFB);
+       fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, CFB);
+}
+
+void
+ofb64_init(int server)
+{
+       fb64_init(&fb[OFB]);
+       fb[OFB].fb_feed[4] = ENCTYPE_DES_OFB64;
+       fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, OFB);
+       fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, OFB);
+}
+
+void
+fb64_init(register struct fb *fbp)
+{
+       memset((void *)fbp, 0, sizeof(*fbp));
+       fbp->state[0] = fbp->state[1] = FAILED;
+       fbp->fb_feed[0] = IAC;
+       fbp->fb_feed[1] = SB;
+       fbp->fb_feed[2] = TELOPT_ENCRYPT;
+       fbp->fb_feed[3] = ENCRYPT_IS;
+}
+
+/*
+ * Returns:
+ *     -1: some error.  Negotiation is done, encryption not ready.
+ *      0: Successful, initial negotiation all done.
+ *      1: successful, negotiation not done yet.
+ *      2: Not yet.  Other things (like getting the key from
+ *         Kerberos) have to happen before we can continue.
+ */
+int
+cfb64_start(int dir, int server)
+{
+       return(fb64_start(&fb[CFB], dir, server));
+}
+
+int
+ofb64_start(int dir, int server)
+{
+       return(fb64_start(&fb[OFB], dir, server));
+}
+
+static int
+fb64_start(struct fb *fbp, int dir, int server)
+{
+       size_t x;
+       unsigned char *p;
+       register int state;
+
+       switch (dir) {
+       case DIR_DECRYPT:
+               /*
+                * This is simply a request to have the other side
+                * start output (our input).  He will negotiate an
+                * IV so we need not look for it.
+                */
+               state = fbp->state[dir-1];
+               if (state == FAILED)
+                       state = IN_PROGRESS;
+               break;
+
+       case DIR_ENCRYPT:
+               state = fbp->state[dir-1];
+               if (state == FAILED)
+                       state = IN_PROGRESS;
+               else if ((state & NO_SEND_IV) == 0)
+                       break;
+
+               if (!VALIDKEY(fbp->krbdes_key)) {
+                       fbp->need_start = 1;
+                       break;
+               }
+               state &= ~NO_SEND_IV;
+               state |= NO_RECV_IV;
+               if (encrypt_debug_mode)
+                       printf("Creating new feed\r\n");
+               /*
+                * Create a random feed and send it over.
+                */
+               des_new_random_key(&fbp->temp_feed);
+               des_ecb_encrypt(&fbp->temp_feed, &fbp->temp_feed,
+                               fbp->krbdes_sched, 1);
+               p = fbp->fb_feed + 3;
+               *p++ = ENCRYPT_IS;
+               p++;
+               *p++ = FB64_IV;
+               for (x = 0; x < sizeof(Block); ++x) {
+                       if ((*p++ = fbp->temp_feed[x]) == IAC)
+                               *p++ = IAC;
+               }
+               *p++ = IAC;
+               *p++ = SE;
+               printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
+               telnet_net_write(fbp->fb_feed, p - fbp->fb_feed);
+               break;
+       default:
+               return(FAILED);
+       }
+       return(fbp->state[dir-1] = state);
+}
+
+/*
+ * Returns:
+ *     -1: some error.  Negotiation is done, encryption not ready.
+ *      0: Successful, initial negotiation all done.
+ *      1: successful, negotiation not done yet.
+ */
+int
+cfb64_is(unsigned char *data, int cnt)
+{
+       return(fb64_is(data, cnt, &fb[CFB]));
+}
+int
+ofb64_is(unsigned char *data, int cnt)
+{
+       return(fb64_is(data, cnt, &fb[OFB]));
+}
+
+int
+fb64_is(unsigned char *data, int cnt, struct fb *fbp)
+{
+       unsigned char *p;
+       register int state = fbp->state[DIR_DECRYPT-1];
+
+       if (cnt-- < 1)
+               goto failure;
+
+       switch (*data++) {
+       case FB64_IV:
+               if (cnt != sizeof(Block)) {
+                       if (encrypt_debug_mode)
+                               printf("CFB64: initial vector failed on size\r\n");
+                       state = FAILED;
+                       goto failure;
+               }
+
+               if (encrypt_debug_mode)
+                       printf("CFB64: initial vector received\r\n");
+
+               if (encrypt_debug_mode)
+                       printf("Initializing Decrypt stream\r\n");
+
+               fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]);
+
+               p = fbp->fb_feed + 3;
+               *p++ = ENCRYPT_REPLY;
+               p++;
+               *p++ = FB64_IV_OK;
+               *p++ = IAC;
+               *p++ = SE;
+               printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
+               telnet_net_write(fbp->fb_feed, p - fbp->fb_feed);
+
+               state = fbp->state[DIR_DECRYPT-1] = IN_PROGRESS;
+               break;
+
+       default:
+               if (encrypt_debug_mode) {
+                       printf("Unknown option type: %d\r\n", *(data-1));
+                       printd(data, cnt);
+                       printf("\r\n");
+               }
+               /* FALL THROUGH */
+       failure:
+               /*
+                * We failed.  Send an FB64_IV_BAD option
+                * to the other side so it will know that
+                * things failed.
+                */
+               p = fbp->fb_feed + 3;
+               *p++ = ENCRYPT_REPLY;
+               p++;
+               *p++ = FB64_IV_BAD;
+               *p++ = IAC;
+               *p++ = SE;
+               printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
+               telnet_net_write(fbp->fb_feed, p - fbp->fb_feed);
+
+               break;
+       }
+       return(fbp->state[DIR_DECRYPT-1] = state);
+}
+
+/*
+ * Returns:
+ *     -1: some error.  Negotiation is done, encryption not ready.
+ *      0: Successful, initial negotiation all done.
+ *      1: successful, negotiation not done yet.
+ */
+int
+cfb64_reply(unsigned char *data, int cnt)
+{
+       return(fb64_reply(data, cnt, &fb[CFB]));
+}
+int
+ofb64_reply(unsigned char *data, int cnt)
+{
+       return(fb64_reply(data, cnt, &fb[OFB]));
+}
+
+int
+fb64_reply(unsigned char *data, int cnt, struct fb *fbp)
+{
+       register int state = fbp->state[DIR_ENCRYPT-1];
+
+       if (cnt-- < 1)
+               goto failure;
+
+       switch (*data++) {
+       case FB64_IV_OK:
+               fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+               if (state == FAILED)
+                       state = IN_PROGRESS;
+               state &= ~NO_RECV_IV;
+               encrypt_send_keyid(DIR_ENCRYPT, (const unsigned char *)"\0", 1, 1);
+               break;
+
+       case FB64_IV_BAD:
+               memset(fbp->temp_feed, 0, sizeof(Block));
+               fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+               state = FAILED;
+               break;
+
+       default:
+               if (encrypt_debug_mode) {
+                       printf("Unknown option type: %d\r\n", data[-1]);
+                       printd(data, cnt);
+                       printf("\r\n");
+               }
+               /* FALL THROUGH */
+       failure:
+               state = FAILED;
+               break;
+       }
+       return(fbp->state[DIR_ENCRYPT-1] = state);
+}
+
+void
+cfb64_session(Session_Key *key, int server)
+{
+       fb64_session(key, server, &fb[CFB]);
+}
+
+void
+ofb64_session( Session_Key *key, int server)
+{
+       fb64_session(key, server, &fb[OFB]);
+}
+
+static void
+fb64_session(Session_Key *key, int server, struct fb *fbp)
+{
+
+       if (!key || key->type != SK_DES) {
+               if (encrypt_debug_mode)
+                       printf("Can't set krbdes's session key (%d != %d)\r\n",
+                               key ? key->type : -1, SK_DES);
+               return;
+       }
+       memmove((void *)fbp->krbdes_key, (void *)key->data, sizeof(Block));
+
+       fb64_stream_key(&fbp->krbdes_key, &fbp->streams[DIR_ENCRYPT-1]);
+       fb64_stream_key(&fbp->krbdes_key, &fbp->streams[DIR_DECRYPT-1]);
+
+       if (fbp->once == 0) {
+               des_init_random_number_generator(&fbp->krbdes_key);
+               fbp->once = 1;
+       }
+       des_key_sched(&fbp->krbdes_key, fbp->krbdes_sched);
+       /*
+        * Now look to see if krbdes_start() was waiting for the key to
+        * show up.  If so, go ahead an call it now that we have the key.
+        */
+       if (fbp->need_start) {
+               fbp->need_start = 0;
+               fb64_start(fbp, DIR_ENCRYPT, server);
+       }
+}
+
+/*
+ * We only accept a keyid of 0.  If we get a keyid of
+ * 0, then mark the state as SUCCESS.
+ */
+int
+cfb64_keyid(int dir, unsigned char *kp, int *lenp)
+{
+       return(fb64_keyid(dir, kp, lenp, &fb[CFB]));
+}
+
+       int
+ofb64_keyid(int dir, unsigned char *kp, int *lenp)
+{
+       return(fb64_keyid(dir, kp, lenp, &fb[OFB]));
+}
+
+int
+fb64_keyid(int dir, unsigned char *kp, int *lenp, struct fb *fbp)
+{
+       register int state = fbp->state[dir-1];
+
+       if (*lenp != 1 || (*kp != '\0')) {
+               *lenp = 0;
+               return(state);
+       }
+
+       if (state == FAILED)
+               state = IN_PROGRESS;
+
+       state &= ~NO_KEYID;
+
+       return(fbp->state[dir-1] = state);
+}
+
+void
+fb64_printsub(const unsigned char *data, int cnt, unsigned char *buf,
+    int buflen, const unsigned char *type)
+{
+       char lbuf[32];
+       register int i;
+       char *cp;
+
+       buf[buflen-1] = '\0';           /* make sure it's NULL terminated */
+       buflen -= 1;
+
+       switch(data[2]) {
+       case FB64_IV:
+               snprintf(lbuf, sizeof(lbuf), "%s_IV", type);
+               cp = lbuf;
+               goto common;
+
+       case FB64_IV_OK:
+               snprintf(lbuf, sizeof(lbuf), "%s_IV_OK", type);
+               cp = lbuf;
+               goto common;
+
+       case FB64_IV_BAD:
+               snprintf(lbuf, sizeof(lbuf), "%s_IV_BAD", type);
+               cp = lbuf;
+               goto common;
+
+       default:
+               snprintf(lbuf, sizeof(lbuf), " %d (unknown)", data[2]);
+               cp = lbuf;
+       common:
+               for (; (buflen > 0) && (*buf = *cp++); buf++)
+                       buflen--;
+               for (i = 3; i < cnt; i++) {
+                       snprintf(lbuf, sizeof(lbuf), " %d", data[i]);
+                       for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
+                               buflen--;
+               }
+               break;
+       }
+}
+
+void
+cfb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+       fb64_printsub(data, cnt, buf, buflen, "CFB64");
+}
+
+       void
+ofb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+       fb64_printsub(data, cnt, buf, buflen, "OFB64");
+}
+
+void
+fb64_stream_iv(Block seed, struct stinfo *stp)
+{
+
+       memmove((void *)stp->str_iv, (void *)seed, sizeof(Block));
+       memmove((void *)stp->str_output, (void *)seed, sizeof(Block));
+
+       des_key_sched(&stp->str_ikey, stp->str_sched);
+
+       stp->str_index = sizeof(Block);
+}
+
+void
+fb64_stream_key(Block *key, struct stinfo *stp)
+{
+       memmove((void *)stp->str_ikey, (void *)key, sizeof(Block));
+       des_key_sched(key, stp->str_sched);
+
+       memmove((void *)stp->str_output, (void *)stp->str_iv, sizeof(Block));
+
+       stp->str_index = sizeof(Block);
+}
+
+/*
+ * DES 64 bit Cipher Feedback
+ *
+ *     key --->+-----+
+ *          +->| DES |--+
+ *          |  +-----+  |
+ *         |           v
+ *  INPUT --(--------->(+)+---> DATA
+ *          |             |
+ *         +-------------+
+ *
+ *
+ * Given:
+ *     iV: Initial vector, 64 bits (8 bytes) long.
+ *     Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ *     On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ *     V0 = DES(iV, key)
+ *     On = Dn ^ Vn
+ *     V(n+1) = DES(On, key)
+ */
+
+void
+cfb64_encrypt(unsigned char *s, int c)
+{
+       register struct stinfo *stp = &fb[CFB].streams[DIR_ENCRYPT-1];
+       register int idx;
+
+       idx = stp->str_index;
+       while (c-- > 0) {
+               if (idx == sizeof(Block)) {
+                       Block b;
+                       des_ecb_encrypt(&stp->str_output, &b, stp->str_sched, 1);
+                       memmove((void *)stp->str_feed, (void *)b, sizeof(Block));
+                       idx = 0;
+               }
+
+               /* On encryption, we store (feed ^ data) which is cypher */
+               *s = stp->str_output[idx] = (stp->str_feed[idx] ^ *s);
+               s++;
+               idx++;
+       }
+       stp->str_index = idx;
+}
+
+int
+cfb64_decrypt(int data)
+{
+       register struct stinfo *stp = &fb[CFB].streams[DIR_DECRYPT-1];
+       int idx;
+
+       if (data == -1) {
+               /*
+                * Back up one byte.  It is assumed that we will
+                * never back up more than one byte.  If we do, this
+                * may or may not work.
+                */
+               if (stp->str_index)
+                       --stp->str_index;
+               return(0);
+       }
+
+       idx = stp->str_index++;
+       if (idx == sizeof(Block)) {
+               Block b;
+               des_ecb_encrypt(&stp->str_output, &b, stp->str_sched, 1);
+               memmove((void *)stp->str_feed, (void *)b, sizeof(Block));
+               stp->str_index = 1;     /* Next time will be 1 */
+               idx = 0;                /* But now use 0 */
+       }
+
+       /* On decryption we store (data) which is cypher. */
+       stp->str_output[idx] = data;
+       return(data ^ stp->str_feed[idx]);
+}
+
+/*
+ * DES 64 bit Output Feedback
+ *
+ * key --->+-----+
+ *     +->| DES |--+
+ *     |  +-----+  |
+ *     +-----------+
+ *                 v
+ *  INPUT -------->(+) ----> DATA
+ *
+ * Given:
+ *     iV: Initial vector, 64 bits (8 bytes) long.
+ *     Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ *     On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ *     V0 = DES(iV, key)
+ *     V(n+1) = DES(Vn, key)
+ *     On = Dn ^ Vn
+ */
+void
+ofb64_encrypt(unsigned char *s, int c)
+{
+       register struct stinfo *stp = &fb[OFB].streams[DIR_ENCRYPT-1];
+       register int idx;
+
+       idx = stp->str_index;
+       while (c-- > 0) {
+               if (idx == sizeof(Block)) {
+                       Block b;
+                       des_ecb_encrypt(&stp->str_feed, &b, stp->str_sched, 1);
+                       memmove((void *)stp->str_feed, (void *)b, sizeof(Block));
+                       idx = 0;
+               }
+               *s++ ^= stp->str_feed[idx];
+               idx++;
+       }
+       stp->str_index = idx;
+}
+
+int
+ofb64_decrypt(int data)
+{
+       register struct stinfo *stp = &fb[OFB].streams[DIR_DECRYPT-1];
+       int idx;
+
+       if (data == -1) {
+               /*
+                * Back up one byte.  It is assumed that we will
+                * never back up more than one byte.  If we do, this
+                * may or may not work.
+                */
+               if (stp->str_index)
+                       --stp->str_index;
+               return(0);
+       }
+
+       idx = stp->str_index++;
+       if (idx == sizeof(Block)) {
+               Block b;
+               des_ecb_encrypt(&stp->str_feed, &b, stp->str_sched, 1);
+               memmove((void *)stp->str_feed, (void *)b, sizeof(Block));
+               stp->str_index = 1;     /* Next time will be 1 */
+               idx = 0;                /* But now use 0 */
+       }
+
+       return(data ^ stp->str_feed[idx]);
+}
+#  endif /* DES_ENCRYPTION */
+# endif        /* AUTHENTICATION */
+#endif /* ENCRYPTION */
diff --git a/lib/libtelnet/encrypt.c b/lib/libtelnet/encrypt.c
new file mode 100644 (file)
index 0000000..5aed35f
--- /dev/null
@@ -0,0 +1,960 @@
+/*     $NetBSD: encrypt.c,v 1.17 2012/03/21 05:33:27 matt 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>
+#if 0
+static char sccsid[] = "@(#)encrypt.c  8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: encrypt.c,v 1.17 2012/03/21 05:33:27 matt Exp $");
+#endif /* not lint */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifdef ENCRYPTION
+
+#include <stdio.h>
+#define        ENCRYPT_NAMES
+#include <arpa/telnet.h>
+
+#include "encrypt.h"
+#include "misc.h"
+
+#include <stdlib.h>
+#ifdef NO_STRING_H
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+
+/*
+ * These functions pointers point to the current routines
+ * for encrypting and decrypting data.
+ */
+void   (*encrypt_output)(unsigned char *, int);
+int    (*decrypt_input)(int);
+
+int encrypt_debug_mode = 0;
+static int decrypt_mode = 0;
+static int encrypt_mode = 0;
+static int encrypt_verbose = 0;
+static int autoencrypt = 0;
+static int autodecrypt = 0;
+static int havesessionkey = 0;
+static int Server = 0;
+static const char *Name = "Noname";
+
+#define        typemask(x)     ((x) > 0 ? 1 << ((x)-1) : 0)
+
+static long i_support_encrypt = typemask(ENCTYPE_DES_CFB64)
+                               | typemask(ENCTYPE_DES_OFB64);
+static long i_support_decrypt = typemask(ENCTYPE_DES_CFB64)
+                               | typemask(ENCTYPE_DES_OFB64);
+static long i_wont_support_encrypt = 0;
+static long i_wont_support_decrypt = 0;
+#define        I_SUPPORT_ENCRYPT       (i_support_encrypt & ~i_wont_support_encrypt)
+#define        I_SUPPORT_DECRYPT       (i_support_decrypt & ~i_wont_support_decrypt)
+
+static long remote_supports_encrypt = 0;
+static long remote_supports_decrypt = 0;
+
+static Encryptions encryptions[] = {
+#ifdef DES_ENCRYPTION
+    { "DES_CFB64",     ENCTYPE_DES_CFB64,
+                       cfb64_encrypt,
+                       cfb64_decrypt,
+                       cfb64_init,
+                       cfb64_start,
+                       cfb64_is,
+                       cfb64_reply,
+                       cfb64_session,
+                       cfb64_keyid,
+                       cfb64_printsub },
+    { "DES_OFB64",     ENCTYPE_DES_OFB64,
+                       ofb64_encrypt,
+                       ofb64_decrypt,
+                       ofb64_init,
+                       ofb64_start,
+                       ofb64_is,
+                       ofb64_reply,
+                       ofb64_session,
+                       ofb64_keyid,
+                       ofb64_printsub },
+#endif /* DES_ENCRYPTION */
+    { .name = 0 },
+};
+
+static unsigned char str_send[64] = { IAC, SB, TELOPT_ENCRYPT,
+                                        ENCRYPT_SUPPORT };
+static unsigned char str_suplen = 0;
+static unsigned char str_start[72] = { IAC, SB, TELOPT_ENCRYPT };
+static unsigned char str_end[] = { IAC, SB, TELOPT_ENCRYPT, 0, IAC, SE };
+
+Encryptions *
+findencryption(int type)
+{
+       Encryptions *ep = encryptions;
+
+       if (!(I_SUPPORT_ENCRYPT & remote_supports_decrypt & typemask(type)))
+               return(0);
+       while (ep->type && ep->type != type)
+               ++ep;
+       return(ep->type ? ep : 0);
+}
+
+Encryptions *
+finddecryption(int type)
+{
+       Encryptions *ep = encryptions;
+
+       if (!(I_SUPPORT_DECRYPT & remote_supports_encrypt & typemask(type)))
+               return(0);
+       while (ep->type && ep->type != type)
+               ++ep;
+       return(ep->type ? ep : 0);
+}
+
+#define        MAXKEYLEN 64
+
+static struct key_info {
+       unsigned char keyid[MAXKEYLEN];
+       int keylen;
+       int dir;
+       int *modep;
+       Encryptions *(*getcrypt)(int);
+} ki[2] = {
+       { { 0 }, 0, DIR_ENCRYPT, &encrypt_mode, findencryption },
+       { { 0 }, 0, DIR_DECRYPT, &decrypt_mode, finddecryption },
+};
+
+void
+encrypt_init(const char *name, int server)
+{
+       Encryptions *ep = encryptions;
+
+       Name = name;
+       Server = server;
+       i_support_encrypt = i_support_decrypt = 0;
+       remote_supports_encrypt = remote_supports_decrypt = 0;
+       encrypt_mode = 0;
+       decrypt_mode = 0;
+       encrypt_output = 0;
+       decrypt_input = 0;
+#ifdef notdef
+       encrypt_verbose = !server;
+#endif
+
+       str_suplen = 4;
+
+       while (ep->type) {
+               if (encrypt_debug_mode)
+                       printf(">>>%s: I will support %s\r\n",
+                               Name, ENCTYPE_NAME(ep->type));
+               i_support_encrypt |= typemask(ep->type);
+               i_support_decrypt |= typemask(ep->type);
+               if ((i_wont_support_decrypt & typemask(ep->type)) == 0)
+                       if ((str_send[str_suplen++] = ep->type) == IAC)
+                               str_send[str_suplen++] = IAC;
+               if (ep->init)
+                       (*ep->init)(Server);
+               ++ep;
+       }
+       str_send[str_suplen++] = IAC;
+       str_send[str_suplen++] = SE;
+}
+
+void
+encrypt_list_types(void)
+{
+       Encryptions *ep = encryptions;
+
+       printf("Valid encryption types:\n");
+       while (ep->type) {
+               printf("\t%s (%d)\r\n", ENCTYPE_NAME(ep->type), ep->type);
+               ++ep;
+       }
+}
+
+int
+EncryptEnable(char *type, char *mode)
+{
+       if (isprefix(type, "help") || isprefix(type, "?")) {
+               printf("Usage: encrypt enable <type> [input|output]\n");
+               encrypt_list_types();
+               return(0);
+       }
+       if (EncryptType(type, mode))
+               return(EncryptStart(mode));
+       return(0);
+}
+
+int
+EncryptDisable(char *type, char *mode)
+{
+       register Encryptions *ep;
+       int ret = 0;
+
+       if (isprefix(type, "help") || isprefix(type, "?")) {
+               printf("Usage: encrypt disable <type> [input|output]\n");
+               encrypt_list_types();
+       } else if ((ep = (Encryptions *)genget(type, (char **)encryptions,
+                                               sizeof(Encryptions))) == 0) {
+               printf("%s: invalid encryption type\n", type);
+       } else if (Ambiguous(ep)) {
+               printf("Ambiguous type '%s'\n", type);
+       } else {
+               if ((mode == 0) || (isprefix(mode, "input") ? 1 : 0)) {
+                       if (decrypt_mode == ep->type)
+                               EncryptStopInput();
+                       i_wont_support_decrypt |= typemask(ep->type);
+                       ret = 1;
+               }
+               if ((mode == 0) || (isprefix(mode, "output"))) {
+                       if (encrypt_mode == ep->type)
+                               EncryptStopOutput();
+                       i_wont_support_encrypt |= typemask(ep->type);
+                       ret = 1;
+               }
+               if (ret == 0)
+                       printf("%s: invalid encryption mode\n", mode);
+       }
+       return(ret);
+}
+
+int
+EncryptType(char *type, char *mode)
+{
+       register Encryptions *ep;
+       int ret = 0;
+
+       if (isprefix(type, "help") || isprefix(type, "?")) {
+               printf("Usage: encrypt type <type> [input|output]\n");
+               encrypt_list_types();
+       } else if ((ep = (Encryptions *)genget(type, (char **)encryptions,
+                                               sizeof(Encryptions))) == 0) {
+               printf("%s: invalid encryption type\n", type);
+       } else if (Ambiguous(ep)) {
+               printf("Ambiguous type '%s'\n", type);
+       } else {
+               if ((mode == 0) || isprefix(mode, "input")) {
+                       decrypt_mode = ep->type;
+                       i_wont_support_decrypt &= ~typemask(ep->type);
+                       ret = 1;
+               }
+               if ((mode == 0) || isprefix(mode, "output")) {
+                       encrypt_mode = ep->type;
+                       i_wont_support_encrypt &= ~typemask(ep->type);
+                       ret = 1;
+               }
+               if (ret == 0)
+                       printf("%s: invalid encryption mode\n", mode);
+       }
+       return(ret);
+}
+
+int
+EncryptStart(char *mode)
+{
+       register int ret = 0;
+       if (mode) {
+               if (isprefix(mode, "input"))
+                       return(EncryptStartInput());
+               if (isprefix(mode, "output"))
+                       return(EncryptStartOutput());
+               if (isprefix(mode, "help") || isprefix(mode, "?")) {
+                       printf("Usage: encrypt start [input|output]\n");
+                       return(0);
+               }
+               printf("%s: invalid encryption mode 'encrypt start ?' for help\n", mode);
+               return(0);
+       }
+       ret += EncryptStartInput();
+       ret += EncryptStartOutput();
+       return(ret);
+}
+
+int
+EncryptStartInput(void)
+{
+       if (decrypt_mode) {
+               encrypt_send_request_start();
+               return(1);
+       }
+       printf("No previous decryption mode, decryption not enabled\r\n");
+       return(0);
+}
+
+int
+EncryptStartOutput(void)
+{
+       if (encrypt_mode) {
+               encrypt_start_output(encrypt_mode);
+               return(1);
+       }
+       printf("No previous encryption mode, encryption not enabled\r\n");
+       return(0);
+}
+
+int
+EncryptStop(char *mode)
+{
+       int ret = 0;
+       if (mode) {
+               if (isprefix(mode, "input"))
+                       return(EncryptStopInput());
+               if (isprefix(mode, "output"))
+                       return(EncryptStopOutput());
+               if (isprefix(mode, "help") || isprefix(mode, "?")) {
+                       printf("Usage: encrypt stop [input|output]\n");
+                       return(0);
+               }
+               printf("%s: invalid encryption mode 'encrypt stop ?' for help\n", mode);
+               return(0);
+       }
+       ret += EncryptStopInput();
+       ret += EncryptStopOutput();
+       return(ret);
+}
+
+int
+EncryptStopInput(void)
+{
+       encrypt_send_request_end();
+       return(1);
+}
+
+int
+EncryptStopOutput(void)
+{
+       encrypt_send_end();
+       return(1);
+}
+
+void
+encrypt_display(void)
+{
+       if (encrypt_output)
+               printf("Currently encrypting output with %s\r\n",
+                       ENCTYPE_NAME(encrypt_mode));
+       if (decrypt_input)
+               printf("Currently decrypting input with %s\r\n",
+                       ENCTYPE_NAME(decrypt_mode));
+}
+
+int
+EncryptStatus(void)
+{
+       if (encrypt_output)
+               printf("Currently encrypting output with %s\r\n",
+                       ENCTYPE_NAME(encrypt_mode));
+       else if (encrypt_mode) {
+               printf("Currently output is clear text.\r\n");
+               printf("Last encryption mode was %s\r\n",
+                       ENCTYPE_NAME(encrypt_mode));
+       }
+       if (decrypt_input) {
+               printf("Currently decrypting input with %s\r\n",
+                       ENCTYPE_NAME(decrypt_mode));
+       } else if (decrypt_mode) {
+               printf("Currently input is clear text.\r\n");
+               printf("Last decryption mode was %s\r\n",
+                       ENCTYPE_NAME(decrypt_mode));
+       }
+       return 1;
+}
+
+void
+encrypt_send_support(void)
+{
+       if (str_suplen) {
+               /*
+                * If the user has requested that decryption start
+                * immediatly, then send a "REQUEST START" before
+                * we negotiate the type.
+                */
+               if (!Server && autodecrypt)
+                       encrypt_send_request_start();
+               telnet_net_write(str_send, str_suplen);
+               printsub('>', &str_send[2], str_suplen - 2);
+               str_suplen = 0;
+       }
+}
+
+int
+EncryptDebug(int on)
+{
+       if (on < 0)
+               encrypt_debug_mode ^= 1;
+       else
+               encrypt_debug_mode = on;
+       printf("Encryption debugging %s\r\n",
+               encrypt_debug_mode ? "enabled" : "disabled");
+       return(1);
+}
+
+int
+EncryptVerbose(int on)
+{
+       if (on < 0)
+               encrypt_verbose ^= 1;
+       else
+               encrypt_verbose = on;
+       printf("Encryption %s verbose\r\n",
+               encrypt_verbose ? "is" : "is not");
+       return(1);
+}
+
+int
+EncryptAutoEnc(int on)
+{
+       encrypt_auto(on);
+       printf("Automatic encryption of output is %s\r\n",
+               autoencrypt ? "enabled" : "disabled");
+       return(1);
+}
+
+int
+EncryptAutoDec(int on)
+{
+       decrypt_auto(on);
+       printf("Automatic decryption of input is %s\r\n",
+               autodecrypt ? "enabled" : "disabled");
+       return(1);
+}
+
+/*
+ * Called when ENCRYPT SUPPORT is received.
+ */
+void
+encrypt_support(unsigned char *typelist, int cnt)
+{
+       register int type, use_type = 0;
+       Encryptions *ep;
+
+       /*
+        * Forget anything the other side has previously told us.
+        */
+       remote_supports_decrypt = 0;
+
+       while (cnt-- > 0) {
+               type = *typelist++;
+               if (encrypt_debug_mode)
+                       printf(">>>%s: He is supporting %s (%d)\r\n",
+                               Name,
+                               ENCTYPE_NAME(type), type);
+               if ((type < ENCTYPE_CNT) &&
+                   (I_SUPPORT_ENCRYPT & typemask(type))) {
+                       remote_supports_decrypt |= typemask(type);
+                       if (use_type == 0)
+                               use_type = type;
+               }
+       }
+       if (use_type) {
+               ep = findencryption(use_type);
+               if (!ep)
+                       return;
+               type = ep->start ? (*ep->start)(DIR_ENCRYPT, Server) : 0;
+               if (encrypt_debug_mode)
+                       printf(">>>%s: (*ep->start)() returned %d\r\n",
+                                       Name, type);
+               if (type < 0)
+                       return;
+               encrypt_mode = use_type;
+               if (type == 0)
+                       encrypt_start_output(use_type);
+       }
+}
+
+void
+encrypt_is(unsigned char *data, int cnt)
+{
+       Encryptions *ep;
+       register int type, ret;
+
+       if (--cnt < 0)
+               return;
+       type = *data++;
+       if (type < ENCTYPE_CNT)
+               remote_supports_encrypt |= typemask(type);
+       if (!(ep = finddecryption(type))) {
+               if (encrypt_debug_mode)
+                       printf(">>>%s: Can't find type %s (%d) for initial negotiation\r\n",
+                               Name,
+                               ENCTYPE_NAME_OK(type)
+                                       ? ENCTYPE_NAME(type) : "(unknown)",
+                               type);
+               return;
+       }
+       if (!ep->is) {
+               if (encrypt_debug_mode)
+                       printf(">>>%s: No initial negotiation needed for type %s (%d)\r\n",
+                               Name,
+                               ENCTYPE_NAME_OK(type)
+                                       ? ENCTYPE_NAME(type) : "(unknown)",
+                               type);
+               ret = 0;
+       } else {
+               ret = (*ep->is)(data, cnt);
+               if (encrypt_debug_mode)
+                       printf("(*ep->is)(%p, %d) returned %s(%d)\n", data, cnt,
+                               (ret < 0) ? "FAIL " :
+                               (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret);
+       }
+       if (ret < 0) {
+               autodecrypt = 0;
+       } else {
+               decrypt_mode = type;
+               if (ret == 0 && autodecrypt)
+                       encrypt_send_request_start();
+       }
+}
+
+void
+encrypt_reply(unsigned char *data, int cnt)
+{
+       Encryptions *ep;
+       register int ret, type;
+
+       if (--cnt < 0)
+               return;
+       type = *data++;
+       if (!(ep = findencryption(type))) {
+               if (encrypt_debug_mode)
+                       printf(">>>%s: Can't find type %s (%d) for initial negotiation\r\n",
+                               Name,
+                               ENCTYPE_NAME_OK(type)
+                                       ? ENCTYPE_NAME(type) : "(unknown)",
+                               type);
+               return;
+       }
+       if (!ep->reply) {
+               if (encrypt_debug_mode)
+                       printf(">>>%s: No initial negotiation needed for type %s (%d)\r\n",
+                               Name,
+                               ENCTYPE_NAME_OK(type)
+                                       ? ENCTYPE_NAME(type) : "(unknown)",
+                               type);
+               ret = 0;
+       } else {
+               ret = (*ep->reply)(data, cnt);
+               if (encrypt_debug_mode)
+                       printf("(*ep->reply)(%p, %d) returned %s(%d)\n",
+                               data, cnt,
+                               (ret < 0) ? "FAIL " :
+                               (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret);
+       }
+       if (encrypt_debug_mode)
+               printf(">>>%s: encrypt_reply returned %d\n", Name, ret);
+       if (ret < 0) {
+               autoencrypt = 0;
+       } else {
+               encrypt_mode = type;
+               if (ret == 0 && autoencrypt)
+                       encrypt_start_output(type);
+       }
+}
+
+/*
+ * Called when a ENCRYPT START command is received.
+ */
+void
+encrypt_start(unsigned char *data, int cnt)
+{
+       Encryptions *ep;
+
+       if (!decrypt_mode) {
+               /*
+                * Something is wrong.  We should not get a START
+                * command without having already picked our
+                * decryption scheme.  Send a REQUEST-END to
+                * attempt to clear the channel...
+                */
+               printf("%s: Warning, Cannot decrypt input stream!!!\r\n", Name);
+               encrypt_send_request_end();
+               return;
+       }
+
+       if ((ep = finddecryption(decrypt_mode)) != NULL) {
+               decrypt_input = ep->input;
+               if (encrypt_verbose)
+                       printf("[ Input is now decrypted with type %s ]\r\n",
+                               ENCTYPE_NAME(decrypt_mode));
+               if (encrypt_debug_mode)
+                       printf(">>>%s: Start to decrypt input with type %s\r\n",
+                               Name, ENCTYPE_NAME(decrypt_mode));
+       } else {
+               printf("%s: Warning, Cannot decrypt type %s (%d)!!!\r\n",
+                               Name,
+                               ENCTYPE_NAME_OK(decrypt_mode)
+                                       ? ENCTYPE_NAME(decrypt_mode)
+                                       : "(unknown)",
+                               decrypt_mode);
+               encrypt_send_request_end();
+       }
+}
+
+void
+encrypt_session_key(Session_Key *key, int server)
+{
+       Encryptions *ep = encryptions;
+
+       havesessionkey = 1;
+
+       while (ep->type) {
+               if (ep->session)
+                       (*ep->session)(key, server);
+#ifdef notdef
+               if (!encrypt_output && autoencrypt && !server)
+                       encrypt_start_output(ep->type);
+               if (!decrypt_input && autodecrypt && !server)
+                       encrypt_send_request_start();
+#endif
+               ++ep;
+       }
+}
+
+/*
+ * Called when ENCRYPT END is received.
+ */
+void
+encrypt_end(void)
+{
+       decrypt_input = 0;
+       if (encrypt_debug_mode)
+               printf(">>>%s: Input is back to clear text\r\n", Name);
+       if (encrypt_verbose)
+               printf("[ Input is now clear text ]\r\n");
+}
+
+/*
+ * Called when ENCRYPT REQUEST-END is received.
+ */
+void
+encrypt_request_end(void)
+{
+       encrypt_send_end();
+}
+
+/*
+ * Called when ENCRYPT REQUEST-START is received.  If we receive
+ * this before a type is picked, then that indicates that the
+ * other side wants us to start encrypting data as soon as we
+ * can.
+ */
+void
+encrypt_request_start(unsigned char *data, int cnt)
+{
+       if (encrypt_mode == 0)  {
+               if (Server)
+                       autoencrypt = 1;
+               return;
+       }
+       encrypt_start_output(encrypt_mode);
+}
+
+static unsigned char str_keyid[(MAXKEYLEN*2)+5] = { IAC, SB, TELOPT_ENCRYPT };
+
+void
+encrypt_enc_keyid(unsigned char *keyid, int len)
+{
+       encrypt_keyid(&ki[1], keyid, len);
+}
+
+void
+encrypt_dec_keyid(unsigned char *keyid, int len)
+{
+       encrypt_keyid(&ki[0], keyid, len);
+}
+
+void
+encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len)
+{
+       Encryptions *ep;
+       int dir = kp->dir;
+       register int ret = 0;
+
+       if (!(ep = (*kp->getcrypt)(*kp->modep))) {
+               if (len == 0)
+                       return;
+               kp->keylen = 0;
+       } else if (len == 0) {
+               /*
+                * Empty option, indicates a failure.
+                */
+               if (kp->keylen == 0)
+                       return;
+               kp->keylen = 0;
+               if (ep->keyid)
+                       (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen);
+
+       } else if ((size_t)len > sizeof(kp->keyid)) {
+               return;
+       } else if ((len != kp->keylen) ||
+                  (memcmp(keyid, kp->keyid, len) != 0)) {
+               /*
+                * Length or contents are different
+                */
+               kp->keylen = len;
+               memmove(kp->keyid, keyid, len);
+               if (ep->keyid)
+                       (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen);
+       } else {
+               if (ep->keyid)
+                       ret = (*ep->keyid)(dir, kp->keyid, &kp->keylen);
+               if ((ret == 0) && (dir == DIR_ENCRYPT) && autoencrypt)
+                       encrypt_start_output(*kp->modep);
+               return;
+       }
+
+       encrypt_send_keyid(dir, kp->keyid, kp->keylen, 0);
+}
+
+void
+encrypt_send_keyid(int dir, const unsigned char *keyid, int keylen, int saveit)
+{
+       unsigned char *strp;
+
+       str_keyid[3] = (dir == DIR_ENCRYPT)
+                       ? ENCRYPT_ENC_KEYID : ENCRYPT_DEC_KEYID;
+       if (saveit) {
+               struct key_info *kp = &ki[(dir == DIR_ENCRYPT) ? 0 : 1];
+               memmove(kp->keyid, keyid, keylen);
+               kp->keylen = keylen;
+       }
+
+       for (strp = &str_keyid[4]; keylen > 0; --keylen) {
+               if ((*strp++ = *keyid++) == IAC)
+                       *strp++ = IAC;
+       }
+       *strp++ = IAC;
+       *strp++ = SE;
+       telnet_net_write(str_keyid, strp - str_keyid);
+       printsub('>', &str_keyid[2], strp - str_keyid - 2);
+}
+
+void
+encrypt_auto(int on)
+{
+       if (on < 0)
+               autoencrypt ^= 1;
+       else
+               autoencrypt = on ? 1 : 0;
+}
+
+void
+decrypt_auto(int on)
+{
+       if (on < 0)
+               autodecrypt ^= 1;
+       else
+               autodecrypt = on ? 1 : 0;
+}
+
+void
+encrypt_start_output(int type)
+{
+       Encryptions *ep;
+       register unsigned char *p;
+       register int i;
+
+       if (!(ep = findencryption(type))) {
+               if (encrypt_debug_mode) {
+                       printf(">>>%s: Can't encrypt with type %s (%d)\r\n",
+                               Name,
+                               ENCTYPE_NAME_OK(type)
+                                       ? ENCTYPE_NAME(type) : "(unknown)",
+                               type);
+               }
+               return;
+       }
+       if (ep->start) {
+               i = (*ep->start)(DIR_ENCRYPT, Server);
+               if (encrypt_debug_mode) {
+                       printf(">>>%s: Encrypt start: %s (%d) %s\r\n",
+                               Name,
+                               (i < 0) ? "failed" :
+                                       "initial negotiation in progress",
+                               i, ENCTYPE_NAME(type));
+               }
+               if (i)
+                       return;
+       }
+       p = str_start + 3;
+       *p++ = ENCRYPT_START;
+       for (i = 0; i < ki[0].keylen; ++i) {
+               if ((*p++ = ki[0].keyid[i]) == IAC)
+                       *p++ = IAC;
+       }
+       *p++ = IAC;
+       *p++ = SE;
+       telnet_net_write(str_start, p - str_start);
+       net_encrypt();
+       printsub('>', &str_start[2], p - &str_start[2]);
+       /*
+        * If we are already encrypting in some mode, then
+        * encrypt the ring (which includes our request) in
+        * the old mode, mark it all as "clear text" and then
+        * switch to the new mode.
+        */
+       encrypt_output = ep->output;
+       encrypt_mode = type;
+       if (encrypt_debug_mode)
+               printf(">>>%s: Started to encrypt output with type %s\r\n",
+                       Name, ENCTYPE_NAME(type));
+       if (encrypt_verbose)
+               printf("[ Output is now encrypted with type %s ]\r\n",
+                       ENCTYPE_NAME(type));
+}
+
+void
+encrypt_send_end(void)
+{
+       if (!encrypt_output)
+               return;
+
+       str_end[3] = ENCRYPT_END;
+       telnet_net_write(str_end, sizeof(str_end));
+       net_encrypt();
+       printsub('>', &str_end[2], sizeof(str_end) - 2);
+       /*
+        * Encrypt the output buffer now because it will not be done by
+        * netflush...
+        */
+       encrypt_output = 0;
+       if (encrypt_debug_mode)
+               printf(">>>%s: Output is back to clear text\r\n", Name);
+       if (encrypt_verbose)
+               printf("[ Output is now clear text ]\r\n");
+}
+
+void
+encrypt_send_request_start(void)
+{
+       register unsigned char *p;
+       register int i;
+
+       p = &str_start[3];
+       *p++ = ENCRYPT_REQSTART;
+       for (i = 0; i < ki[1].keylen; ++i) {
+               if ((*p++ = ki[1].keyid[i]) == IAC)
+                       *p++ = IAC;
+       }
+       *p++ = IAC;
+       *p++ = SE;
+       telnet_net_write(str_start, p - str_start);
+       printsub('>', &str_start[2], p - &str_start[2]);
+       if (encrypt_debug_mode)
+               printf(">>>%s: Request input to be encrypted\r\n", Name);
+}
+
+void
+encrypt_send_request_end(void)
+{
+       str_end[3] = ENCRYPT_REQEND;
+       telnet_net_write(str_end, sizeof(str_end));
+       printsub('>', &str_end[2], sizeof(str_end) - 2);
+
+       if (encrypt_debug_mode)
+               printf(">>>%s: Request input to be clear text\r\n", Name);
+}
+
+void
+encrypt_wait(void)
+{
+       if (encrypt_debug_mode)
+               printf(">>>%s: in encrypt_wait\r\n", Name);
+       if (!havesessionkey || !(I_SUPPORT_ENCRYPT & remote_supports_decrypt))
+               return;
+       while (autoencrypt && !encrypt_output)
+               if (telnet_spin())
+                       return;
+}
+
+void
+encrypt_debug(int mode)
+{
+       encrypt_debug_mode = mode;
+}
+
+void
+encrypt_gen_printsub(unsigned char *data, int cnt,
+       unsigned char *buf, int buflen)
+{
+       char tbuf[16], *cp;
+
+       cnt -= 2;
+       data += 2;
+       buf[buflen-1] = '\0';
+       buf[buflen-2] = '*';
+       buflen -= 2;
+       for (; cnt > 0; cnt--, data++) {
+               snprintf(tbuf, sizeof(tbuf), " %d", *data);
+               for (cp = tbuf; *cp && buflen > 0; --buflen)
+                       *buf++ = *cp++;
+               if (buflen <= 0)
+                       return;
+       }
+       *buf = '\0';
+}
+
+void
+encrypt_printsub(unsigned char *data, int cnt,
+       unsigned char *buf, int buflen)
+{
+       Encryptions *ep;
+       register int type = data[1];
+
+       for (ep = encryptions; ep->type && ep->type != type; ep++)
+               ;
+
+       if (ep->printsub)
+               (*ep->printsub)(data, cnt, buf, buflen);
+       else
+               encrypt_gen_printsub(data, cnt, buf, buflen);
+}
+#endif /* ENCRYPTION */
diff --git a/lib/libtelnet/encrypt.h b/lib/libtelnet/encrypt.h
new file mode 100644 (file)
index 0000000..570b83a
--- /dev/null
@@ -0,0 +1,101 @@
+/*     $NetBSD: encrypt.h,v 1.9 2012/01/09 15:25:33 christos 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.
+ *
+ *     from: @(#)encrypt.h     8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifdef ENCRYPTION
+#include <sys/cdefs.h>
+
+# ifndef __ENCRYPTION__
+# define __ENCRYPTION__
+
+#define        DIR_DECRYPT             1
+#define        DIR_ENCRYPT             2
+
+#define Block des_cblock
+typedef unsigned char *BlockT;
+#define Schedule des_key_schedule
+
+#define        VALIDKEY(key)   ( key[0] | key[1] | key[2] | key[3] | \
+                         key[4] | key[5] | key[6] | key[7])
+
+#define        SAMEKEY(k1, k2) (!bcmp((void *)k1, (void *)k2, sizeof(Block)))
+
+typedef        struct {
+       short           type;
+       int             length;
+       unsigned char   *data;
+} Session_Key;
+
+
+typedef struct {
+       const char      *name;
+       int     type;
+       void    (*output)(unsigned char *, int);
+       int     (*input)(int);
+       void    (*init)(int);
+       int     (*start)(int, int);
+       int     (*is)(unsigned char *, int);
+       int     (*reply)(unsigned char *, int);
+       void    (*session)(Session_Key *, int);
+       int     (*keyid)(int, unsigned char *, int *);
+       void    (*printsub)(unsigned char *, int, unsigned char *, int);
+} Encryptions;
+
+#define        SK_DES          1       /* Matched Kerberos v5 KEYTYPE_DES */
+
+#include "enc-proto.h"
+
+extern int encrypt_debug_mode;
+extern int (*decrypt_input)(int);
+extern void (*encrypt_output)(unsigned char *, int);
+# endif /* __ENCRYPTION__ */
+#endif /* ENCRYPTION */
diff --git a/lib/libtelnet/forward.c b/lib/libtelnet/forward.c
new file mode 100644 (file)
index 0000000..b97418d
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * appl/telnet/libtelnet/forward.c
+ */
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+/* General-purpose forwarding routines. These routines may be put into */
+/* libkrb5.a to allow widespread use */ 
+
+#if defined(KERBEROS) || defined(KRB5)
+#include <stdio.h>
+#include <netdb.h>
+#include "k5-int.h"
+extern char *line;             /* see sys_term.c */
+
+krb5_error_code rd_and_store_for_creds(krb5_context, krb5_auth_context, krb5_data *, krb5_ticket *);
+
+/* Decode, decrypt and store the forwarded creds in the local ccache. */
+krb5_error_code
+rd_and_store_for_creds(context, auth_context, inbuf, ticket)
+    krb5_context context;
+    krb5_auth_context auth_context;
+    krb5_data *inbuf;
+    krb5_ticket *ticket;
+{
+    krb5_creds **creds;
+    krb5_error_code retval;
+    char ccname[35];
+    krb5_ccache ccache = NULL;
+
+    if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) != 0) 
+       return(retval);
+
+    snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_p%d", getpid());
+    setenv(KRB5_ENV_CCNAME, ccname, 1);
+
+    if ((retval = krb5_cc_resolve(context, ccname, &ccache)) != 0)
+       goto cleanup;
+
+    if ((retval = krb5_cc_initialize(context, ccache, ticket->enc_part2->client)) != 0)
+       goto cleanup;
+
+    if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0) 
+       goto cleanup;
+
+cleanup:
+    krb5_free_creds(context, *creds);
+    return retval;
+}
+
+#endif /* defined(KRB5) && defined(FORWARD) */
diff --git a/lib/libtelnet/genget.c b/lib/libtelnet/genget.c
new file mode 100644 (file)
index 0000000..49010aa
--- /dev/null
@@ -0,0 +1,105 @@
+/*     $NetBSD: genget.c,v 1.13 2012/03/21 05:33:27 matt 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[] = "@(#)genget.c   8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: genget.c,v 1.13 2012/03/21 05:33:27 matt Exp $");
+#endif
+#endif /* not lint */
+
+
+#include <ctype.h>
+#include "misc.h"
+
+#define        LOWER(x) (isupper((unsigned char)x) ? tolower((unsigned char)x) : (x))
+/*
+ * The prefix function returns 0 if *s1 is not a prefix
+ * of *s2.  If *s1 exactly matches *s2, the negative of
+ * the length is returned.  If *s1 is a prefix of *s2,
+ * the length of *s1 is returned.
+ */
+int
+isprefix(char *s1, const char *s2)
+{
+       char *os1;
+       char c1, c2;
+
+       if (*s1 == '\0')
+               return(-1);
+       os1 = s1;
+       c1 = *s1;
+       c2 = *s2;
+       while (LOWER(c1) == LOWER(c2)) {
+               if (c1 == '\0')
+                       break;
+               c1 = *++s1;
+               c2 = *++s2;
+       }
+       return(*s1 ? 0 : (*s2 ? (s1 - os1) : (os1 - s1)));
+}
+
+static char *ambiguous;                /* special return value for command routines */
+
+char **
+genget( char   *name,          /* name to match */
+       char    **table,        /* name entry in table */
+       int     stlen)
+{
+       register char **c, **found;
+       register int n;
+
+       if (name == 0)
+           return 0;
+
+       found = 0;
+       for (c = table; *c != 0; c = (char **)((char *)c + stlen)) {
+               if ((n = isprefix(name, *c)) == 0)
+                       continue;
+               if (n < 0)              /* exact match */
+                       return(c);
+               if (found)
+                       return(&ambiguous);
+               found = c;
+       }
+       return(found);
+}
+
+/*
+ * Function call version of Ambiguous()
+ */
+int
+Ambiguous(void *s)
+{
+       return(s == &ambiguous);
+}
diff --git a/lib/libtelnet/getent.c b/lib/libtelnet/getent.c
new file mode 100644 (file)
index 0000000..566c792
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $NetBSD: getent.c,v 1.11 2012/03/21 05:33:27 matt 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[] = "@(#)getent.c   8.2 (Berkeley) 12/15/93";
+#else
+__RCSID("$NetBSD: getent.c,v 1.11 2012/03/21 05:33:27 matt Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "misc-proto.h"
+
+static char *area;
+
+int getent(char *, char *);
+char *getstr(char *, char **);
+
+/*ARGSUSED*/
+int
+getent(char *cp, char *name)
+{
+#ifdef HAS_CGETENT
+       const char *dba[2];
+
+       dba[0] = "/etc/gettytab";
+       dba[1] = 0;
+       return((cgetent(&area, dba, name) == 0) ? 1 : 0);
+#else
+       return(0);
+#endif
+}
+
+#ifndef        SOLARIS
+/*ARGSUSED*/
+char *
+getstr(char *id, char **cpp)
+{
+# ifdef        HAS_CGETENT
+       char *answer;
+       return((cgetstr(area, id, &answer) > 0) ? answer : 0);
+# else
+       return(0);
+# endif
+}
+#endif
diff --git a/lib/libtelnet/kerberos5.c b/lib/libtelnet/kerberos5.c
new file mode 100644 (file)
index 0000000..39ab144
--- /dev/null
@@ -0,0 +1,731 @@
+/*     $NetBSD: kerberos5.c,v 1.20 2014/04/26 22:10:40 joerg 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.
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifdef KRB5
+#include <arpa/telnet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <pwd.h>
+#define Authenticator k5_Authenticator
+#include <krb5.h>
+#undef Authenticator
+/* #include <roken.h> */
+
+#include "encrypt.h"
+#include "auth.h"
+#include "misc.h"
+
+extern int net;
+
+int forward_flags;     /* Flags get set in telnet/main.c on -f and -F */
+int got_forwarded_creds;/* Tell telnetd to pass -F or -f to login. */
+
+int require_hwpreauth;
+
+const char *get_krb5_err_text(krb5_context, krb5_error_code);
+void kerberos5_forward(Authenticator *);
+
+static unsigned char str_data[1024] = {IAC, SB, TELOPT_AUTHENTICATION, 0,
+                                      AUTHTYPE_KERBEROS_V5,};
+
+#define        KRB_AUTH                0       /* Authentication data follows */
+#define        KRB_REJECT              1       /* Rejected (reason might follow) */
+#define        KRB_ACCEPT              2       /* Accepted */
+#define        KRB_RESPONSE            3       /* Response for mutual auth. */
+
+#define KRB_FORWARD            4       /* Forwarded credentials follow */
+#define KRB_FORWARD_ACCEPT             5       /* Forwarded credentials accepted */
+#define KRB_FORWARD_REJECT             6       /* Forwarded credentials rejected */
+
+static krb5_data auth;
+static krb5_ticket *ticket;
+
+krb5_context telnet_context;
+static krb5_auth_context auth_context;
+
+static int
+Data(Authenticator *ap, int type, const void *d, int c)
+{
+       unsigned char *p = str_data + 4;
+       const unsigned char *cd = (const unsigned char *) d;
+
+       if (c == -1)
+               c = strlen(cd);
+
+       if (auth_debug_mode) {
+               printf("%s:%d: [%d] (%d)",
+                   str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
+                   str_data[3],
+                   type, c);
+               printd(d, c);
+               printf("\r\n");
+       }
+       *p++ = ap->type;
+       *p++ = ap->way;
+       *p++ = type;
+       while (c-- > 0) {
+               if ((*p++ = *cd++) == IAC)
+                       *p++ = IAC;
+       }
+       *p++ = IAC;
+       *p++ = SE;
+       if (str_data[3] == TELQUAL_IS)
+               printsub('>', &str_data[2], p - &str_data[2]);
+       return (telnet_net_write(str_data, p - str_data));
+}
+
+const char *
+get_krb5_err_text(krb5_context ctx, krb5_error_code ret)
+{
+       static const char       *str = NULL;
+
+       if (str)
+               krb5_free_error_message(ctx, str);
+
+       str = krb5_get_error_message(ctx, ret);
+
+       if (str != NULL)
+               return str;
+
+       return "unknown";
+}
+
+int
+kerberos5_init(Authenticator *ap, int server)
+{
+       krb5_error_code ret;
+
+       if (telnet_context == 0) {
+               ret = krb5_init_context(&telnet_context);
+               if (ret)
+                       return 0;
+       }
+
+       if (server) {
+               krb5_keytab kt;
+               krb5_kt_cursor cursor;
+
+               ret = krb5_kt_default(telnet_context, &kt);
+               if (ret)
+                       return 0;
+
+               ret = krb5_kt_start_seq_get(telnet_context, kt, &cursor);
+               if (ret) {
+                       krb5_kt_close(telnet_context, kt);
+                       return 0;
+               }
+               krb5_kt_end_seq_get(telnet_context, kt, &cursor);
+               krb5_kt_close(telnet_context, kt);
+
+               str_data[3] = TELQUAL_REPLY;
+       } else
+               str_data[3] = TELQUAL_IS;
+       return (1);
+}
+
+int
+kerberos5_send(Authenticator *ap)
+{
+       krb5_error_code ret;
+       krb5_ccache ccache;
+       int ap_opts;
+       krb5_data cksum_data;
+       char foo[2];
+
+       printf("[ Trying KERBEROS5 ... ]\r\n");
+
+       if (!UserNameRequested) {
+               if (auth_debug_mode) {
+                       printf("Kerberos V5: no user name supplied\r\n");
+               }
+               return (0);
+       }
+       ret = krb5_cc_default(telnet_context, &ccache);
+       if (ret) {
+               if (auth_debug_mode) {
+                       printf(
+                       "Kerberos V5: could not get default ccache: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               }
+               return (0);
+       }
+       if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
+               ap_opts = AP_OPTS_MUTUAL_REQUIRED;
+       else
+               ap_opts = 0;
+
+       ap_opts |= AP_OPTS_USE_SUBKEY;
+    
+       ret = krb5_auth_con_init(telnet_context, &auth_context);
+       if (ret) {
+               if (auth_debug_mode) {
+                       printf(
+                       "Kerberos V5: krb5_auth_con_init failed: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               }
+               return (0);
+       }
+       ret = krb5_auth_con_setaddrs_from_fd(telnet_context,
+           auth_context, &net);
+       if (ret) {
+               if (auth_debug_mode) {
+                       printf("Kerberos V5: "
+                           "krb5_auth_con_setaddrs_from_fd failed: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               }
+               return (0);
+       }
+       krb5_auth_con_setkeytype(telnet_context, auth_context,
+           ETYPE_DES_CBC_CRC);
+
+       foo[0] = ap->type;
+       foo[1] = ap->way;
+
+       cksum_data.length = sizeof(foo);
+       cksum_data.data = foo;
+       ret = krb5_mk_req(telnet_context, &auth_context, ap_opts, "host",
+           RemoteHostName, &cksum_data, ccache, &auth);
+       if (ret) {
+               if (1 || auth_debug_mode) {
+                       printf("Kerberos V5: mk_req failed (%s)\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               }
+               return (0);
+       }
+
+       if (!auth_sendname((unsigned char *) UserNameRequested,
+               strlen(UserNameRequested))) {
+               if (auth_debug_mode)
+                       printf("Not enough room for user name\r\n");
+               return (0);
+       }
+       if (!Data(ap, KRB_AUTH, auth.data, auth.length)) {
+               if (auth_debug_mode)
+                       printf("Not enough room for authentication data\r\n");
+               return (0);
+       }
+       if (auth_debug_mode) {
+               printf("Sent Kerberos V5 credentials to server\r\n");
+       }
+       return (1);
+}
+
+void
+kerberos5_is(Authenticator * ap, unsigned char *data, int cnt)
+{
+       krb5_error_code ret;
+       krb5_data outbuf;
+       krb5_keyblock *key_block;
+       char *name;
+       krb5_principal server;
+       int zero = 0;
+
+       if (cnt-- < 1)
+               return;
+       switch (*data++) {
+       case KRB_AUTH:
+               auth.data = (char *) data;
+               auth.length = cnt;
+
+               auth_context = NULL;
+
+               ret = krb5_auth_con_init(telnet_context, &auth_context);
+               if (ret) {
+                       Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       if (auth_debug_mode)
+                               printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n",
+                                   get_krb5_err_text(telnet_context, ret));
+                       return;
+               }
+               ret = krb5_auth_con_setaddrs_from_fd(telnet_context,
+                   auth_context, &zero);
+               if (ret) {
+                       Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       if (auth_debug_mode)
+                               printf("Kerberos V5: "
+                                   "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n",
+                                   get_krb5_err_text(telnet_context, ret));
+                       return;
+               }
+               ret = krb5_sock_to_principal(telnet_context, 0, "host",
+                   KRB5_NT_SRV_HST, &server);
+               if (ret) {
+                       Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       if (auth_debug_mode)
+                               printf("Kerberos V5: "
+                                   "krb5_sock_to_principal failed (%s)\r\n",
+                                   get_krb5_err_text(telnet_context, ret));
+                       return;
+               }
+               ret = krb5_rd_req(telnet_context, &auth_context, &auth,
+                   server, NULL, NULL, &ticket);
+               krb5_free_principal(telnet_context, server);
+
+               if (ret) {
+                       char *errbuf;
+
+                       asprintf(&errbuf,
+                           "Read req failed: %s",
+                           get_krb5_err_text(telnet_context, ret));
+                       Data(ap, KRB_REJECT, errbuf, -1);
+                       if (auth_debug_mode)
+                               printf("%s\r\n", errbuf);
+                       free(errbuf);
+                       return;
+               } {
+                       char foo[2];
+
+                       foo[0] = ap->type;
+                       foo[1] = ap->way;
+
+                       ret = krb5_verify_authenticator_checksum(telnet_context,
+                           auth_context, foo, sizeof(foo));
+
+                       if (ret) {
+                               char *errbuf;
+                               asprintf(&errbuf, "Bad checksum: %s",
+                                   get_krb5_err_text(telnet_context, ret));
+                               Data(ap, KRB_REJECT, errbuf, -1);
+                               if (auth_debug_mode)
+                                       printf("%s\r\n", errbuf);
+                               free(errbuf);
+                               return;
+                       }
+               }
+               ret = krb5_auth_con_getremotesubkey(telnet_context,
+                   auth_context, &key_block);
+
+               if (ret) {
+                       Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       if (auth_debug_mode)
+                               printf("Kerberos V5: "
+                                   "krb5_auth_con_getremotesubkey failed (%s)\r\n",
+                                   get_krb5_err_text(telnet_context, ret));
+                       return;
+               }
+               if (key_block == NULL) {
+                       ret = krb5_auth_con_getkey(telnet_context,
+                                                  auth_context,
+                                                  &key_block);
+               }
+               if (ret) {
+                       Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       if (auth_debug_mode)
+                               printf("Kerberos V5: "
+                                      "krb5_auth_con_getkey failed (%s)\r\n",
+                                      get_krb5_err_text(telnet_context, ret));
+                       return;
+               }
+               if (key_block == NULL) {
+                       Data(ap, KRB_REJECT, "no subkey received", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       if (auth_debug_mode)
+                               printf("Kerberos V5: "
+                                      "krb5_auth_con_getremotesubkey returned NULL key\r\n");
+                       return;
+               }
+               if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
+                       ret = krb5_mk_rep(telnet_context,
+                           auth_context, &outbuf);
+                       if (ret) {
+                               Data(ap, KRB_REJECT,
+                                   "krb5_mk_rep failed", -1);
+                               auth_finished(ap, AUTH_REJECT);
+                               if (auth_debug_mode)
+                                       printf("Kerberos V5: "
+                                           "krb5_mk_rep failed (%s)\r\n",
+                                           get_krb5_err_text(telnet_context,
+                                           ret));
+                               krb5_free_keyblock(telnet_context, key_block);
+                               return;
+                       }
+                       Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length);
+               }
+               if (krb5_unparse_name(telnet_context, ticket->client, &name))
+                       name = 0;
+
+               if (UserNameRequested && krb5_kuserok(telnet_context,
+                   ticket->client, UserNameRequested)) {
+                       Data(ap, KRB_ACCEPT, name ? name : "", name ? -1 : 0);
+                       if (auth_debug_mode) {
+                               printf("Kerberos5 identifies him as ``%s''\r\n",
+                                   name ? name : "");
+                       }
+                       if (key_block->keytype == ETYPE_DES_CBC_MD5 ||
+                           key_block->keytype == ETYPE_DES_CBC_MD4 ||
+                           key_block->keytype == ETYPE_DES_CBC_CRC) {
+                               Session_Key skey;
+
+                               skey.type = SK_DES;
+                               skey.length = 8;
+                               skey.data = key_block->keyvalue.data;
+                               encrypt_session_key(&skey, 0);
+                       }
+               } else {
+                       char *msg;
+
+                       asprintf(&msg, "user `%s' is not authorized to "
+                           "login as `%s'",
+                           name ? name : "<unknown>",
+                           UserNameRequested ? UserNameRequested : "<nobody>");
+                       if (msg == NULL)
+                               Data(ap, KRB_REJECT, NULL, 0);
+                       else {
+                               Data(ap, KRB_REJECT, (void *) msg, -1);
+                               free(msg);
+                       }
+                       auth_finished(ap, AUTH_REJECT);
+                       krb5_free_keyblock(telnet_context, key_block);
+                       break;
+               }
+               auth_finished(ap, AUTH_USER);
+               krb5_free_keyblock(telnet_context, key_block);
+               break;
+       case KRB_FORWARD:{
+                       struct passwd pws, *pwd;
+                       char pwbuf[1024];
+                       char ccname[1024];      /* XXX */
+                       krb5_data inbuf;
+                       krb5_ccache ccache;
+                       inbuf.data = (char *) data;
+                       inbuf.length = cnt;
+
+                       if (getpwnam_r(UserNameRequested, &pws, pwbuf,
+                           sizeof(pwbuf), &pwd) != 0 || pwd == NULL)
+                               break;
+
+                       snprintf(ccname, sizeof(ccname),
+                           "FILE:/tmp/krb5cc_%u", pwd->pw_uid);
+
+                       ret = krb5_cc_resolve(telnet_context, ccname, &ccache);
+                       if (ret) {
+                               if (auth_debug_mode)
+                                       printf("Kerberos V5: could not get ccache: %s\r\n",
+                                           get_krb5_err_text(telnet_context,
+                                           ret));
+                               break;
+                       }
+                       ret = krb5_cc_initialize(telnet_context, ccache,
+                           ticket->client);
+                       if (ret) {
+                               if (auth_debug_mode)
+                                       printf("Kerberos V5: could not init ccache: %s\r\n",
+                                           get_krb5_err_text(telnet_context,
+                                               ret));
+                               break;
+                       }
+                       ret = krb5_rd_cred2(telnet_context, auth_context,
+                           ccache, &inbuf);
+                       if (ret) {
+                               char *errbuf;
+
+                               asprintf(&errbuf,
+                                   "Read forwarded creds failed: %s",
+                                   get_krb5_err_text(telnet_context, ret));
+                               if (errbuf == NULL)
+                                       Data(ap, KRB_FORWARD_REJECT, NULL, 0);
+                               else
+                                       Data(ap, KRB_FORWARD_REJECT, errbuf, -1);
+                               if (auth_debug_mode)
+                                       printf("Could not read forwarded credentials: %s\r\n",
+                                           errbuf);
+                               free(errbuf);
+                       } else
+                               Data(ap, KRB_FORWARD_ACCEPT, 0, 0);
+                       chown(ccname + 5, pwd->pw_uid, -1);
+                       if (auth_debug_mode)
+                               printf("Forwarded credentials obtained\r\n");
+                       break;
+               }
+       default:
+               if (auth_debug_mode)
+                       printf("Unknown Kerberos option %d\r\n", data[-1]);
+               Data(ap, KRB_REJECT, 0, 0);
+               break;
+       }
+}
+
+void
+kerberos5_reply(Authenticator * ap, unsigned char *data, int cnt)
+{
+       static int mutual_complete = 0;
+
+       if (cnt-- < 1)
+               return;
+       switch (*data++) {
+       case KRB_REJECT:
+               if (cnt > 0) {
+                       printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
+                           cnt, data);
+               } else
+                       printf("[ Kerberos V5 refuses authentication ]\r\n");
+               auth_send_retry();
+               return;
+       case KRB_ACCEPT:{
+                       krb5_error_code ret;
+                       Session_Key skey;
+                       krb5_keyblock *keyblock;
+
+                       if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL &&
+                           !mutual_complete) {
+                               printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n");
+                               auth_send_retry();
+                               return;
+                       }
+                       if (cnt)
+                               printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data);
+                       else
+                               printf("[ Kerberos V5 accepts you ]\r\n");
+
+                       ret = krb5_auth_con_getlocalsubkey(telnet_context,
+                           auth_context, &keyblock);
+                       if (ret)
+                               ret = krb5_auth_con_getkey(telnet_context,
+                                   auth_context, &keyblock);
+                       if (ret) {
+                               printf("[ krb5_auth_con_getkey: %s ]\r\n",
+                                   get_krb5_err_text(telnet_context, ret));
+                               auth_send_retry();
+                               return;
+                       }
+                       skey.type = SK_DES;
+                       skey.length = 8;
+                       skey.data = keyblock->keyvalue.data;
+                       encrypt_session_key(&skey, 0);
+                       krb5_free_keyblock(telnet_context, keyblock);
+                       auth_finished(ap, AUTH_USER);
+                       if (forward_flags & OPTS_FORWARD_CREDS)
+                               kerberos5_forward(ap);
+                       break;
+               }
+       case KRB_RESPONSE:
+               if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
+                       /* the rest of the reply should contain a krb_ap_rep */
+                       krb5_ap_rep_enc_part *reply;
+                       krb5_data inbuf;
+                       krb5_error_code ret;
+
+                       inbuf.length = cnt;
+                       inbuf.data = (char *) data;
+
+                       ret = krb5_rd_rep(telnet_context,
+                           auth_context, &inbuf, &reply);
+                       if (ret) {
+                               printf("[ Mutual authentication failed: %s ]\r\n",
+                                   get_krb5_err_text(telnet_context, ret));
+                               auth_send_retry();
+                               return;
+                       }
+                       krb5_free_ap_rep_enc_part(telnet_context, reply);
+                       mutual_complete = 1;
+               }
+               return;
+       case KRB_FORWARD_ACCEPT:
+               printf("[ Kerberos V5 accepted forwarded credentials ]\r\n");
+               return;
+       case KRB_FORWARD_REJECT:
+               printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n",
+                   cnt, data);
+               return;
+       default:
+               if (auth_debug_mode)
+                       printf("Unknown Kerberos option %d\r\n", data[-1]);
+               return;
+       }
+}
+
+int
+kerberos5_status(Authenticator *ap, char *name, size_t l, int level)
+{
+       if (level < AUTH_USER)
+               return (level);
+
+       if (UserNameRequested &&
+           krb5_kuserok(telnet_context, ticket->client, UserNameRequested)) {
+               strlcpy(name, UserNameRequested, l);
+               return (AUTH_VALID);
+       } else
+               return (AUTH_USER);
+}
+#define        BUMP(buf, len)          while (*(buf)) {++(buf), --(len);}
+#define        ADDC(buf, len, c)       if ((len) > 0) {*(buf)++ = (c); --(len);}
+
+void
+kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+       int i;
+
+       buf[buflen - 1] = '\0'; /* make sure its NULL terminated */
+       buflen -= 1;
+
+       switch (data[3]) {
+       case KRB_REJECT:        /* Rejected (reason might follow) */
+               strlcpy((char *) buf, " REJECT ", buflen);
+               goto common;
+
+       case KRB_ACCEPT:        /* Accepted (name might follow) */
+               strlcpy((char *) buf, " ACCEPT ", buflen);
+common:
+               BUMP(buf, buflen);
+               if (cnt <= 4)
+                       break;
+               ADDC(buf, buflen, '"');
+               for (i = 4; i < cnt; i++)
+                       ADDC(buf, buflen, data[i]);
+               ADDC(buf, buflen, '"');
+               ADDC(buf, buflen, '\0');
+               break;
+
+
+       case KRB_AUTH:          /* Authentication data follows */
+               strlcpy((char *) buf, " AUTH", buflen);
+               goto common2;
+
+       case KRB_RESPONSE:
+               strlcpy((char *) buf, " RESPONSE", buflen);
+               goto common2;
+
+       case KRB_FORWARD:       /* Forwarded credentials follow */
+               strlcpy((char *) buf, " FORWARD", buflen);
+               goto common2;
+
+       case KRB_FORWARD_ACCEPT:        /* Forwarded credentials accepted */
+               strlcpy((char *) buf, " FORWARD_ACCEPT", buflen);
+               goto common2;
+
+       case KRB_FORWARD_REJECT:        /* Forwarded credentials rejected */
+               /* (reason might follow) */
+               strlcpy((char *) buf, " FORWARD_REJECT", buflen);
+               goto common2;
+
+       default:
+               snprintf(buf, buflen, " %d (unknown)", data[3]);
+common2:
+               BUMP(buf, buflen);
+               for (i = 4; i < cnt; i++) {
+                       snprintf(buf, buflen, " %d", data[i]);
+                       BUMP(buf, buflen);
+               }
+               break;
+       }
+}
+
+void
+kerberos5_forward(Authenticator * ap)
+{
+       krb5_error_code ret;
+       krb5_ccache ccache;
+       krb5_creds creds;
+       krb5_kdc_flags flags;
+       krb5_data out_data;
+       krb5_principal principal;
+
+       ret = krb5_cc_default(telnet_context, &ccache);
+       if (ret) {
+               if (auth_debug_mode)
+                       printf("KerberosV5: could not get default ccache: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               return;
+       }
+       ret = krb5_cc_get_principal(telnet_context, ccache, &principal);
+       if (ret) {
+               if (auth_debug_mode)
+                       printf("KerberosV5: could not get principal: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               return;
+       }
+       memset(&creds, 0, sizeof(creds));
+
+       creds.client = principal;
+
+       ret = krb5_build_principal(telnet_context, &creds.server,
+           strlen(principal->realm), principal->realm, "krbtgt",
+           principal->realm, NULL);
+
+       if (ret) {
+               if (auth_debug_mode)
+                       printf("KerberosV5: could not get principal: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               return;
+       }
+       creds.times.endtime = 0;
+
+       flags.i = 0;
+       flags.b.forwarded = 1;
+       if (forward_flags & OPTS_FORWARDABLE_CREDS)
+               flags.b.forwardable = 1;
+
+       ret = krb5_get_forwarded_creds(telnet_context, auth_context,
+           ccache, flags.i, RemoteHostName, &creds, &out_data);
+       if (ret) {
+               if (auth_debug_mode)
+                       printf("Kerberos V5: error getting forwarded creds: %s\r\n",
+                           get_krb5_err_text(telnet_context, ret));
+               return;
+       }
+       if (!Data(ap, KRB_FORWARD, out_data.data, out_data.length)) {
+               if (auth_debug_mode)
+                       printf("Not enough room for authentication data\r\n");
+       } else {
+               if (auth_debug_mode)
+                       printf("Forwarded local Kerberos V5 credentials to server\r\n");
+       }
+}
+#endif /* KRB5 */
diff --git a/lib/libtelnet/key-proto.h b/lib/libtelnet/key-proto.h
new file mode 100644 (file)
index 0000000..038e031
--- /dev/null
@@ -0,0 +1,63 @@
+/*     $NetBSD: key-proto.h,v 1.6 2005/02/06 05:53:07 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.
+ *
+ *     from: @(#)key-proto.h   8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifndef        __KEY_PROTO__
+#define        __KEY_PROTO__
+
+#include <sys/cdefs.h>
+
+int key_file_exists(void);
+void key_lookup(unsigned char *, Block);
+void key_stream_init(Block, Block, int);
+unsigned char key_stream(int, int);
+#endif
diff --git a/lib/libtelnet/misc-proto.h b/lib/libtelnet/misc-proto.h
new file mode 100644 (file)
index 0000000..4d3d52e
--- /dev/null
@@ -0,0 +1,72 @@
+/*     $NetBSD: misc-proto.h,v 1.10 2005/02/06 05:53:07 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.
+ *
+ *     from: @(#)misc-proto.h  8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America is assumed
+ * to require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#ifndef        __MISC_PROTO__
+#define        __MISC_PROTO__
+
+#include <sys/cdefs.h>
+
+void auth_encrypt_init(const char *, const char *, const char *, int);
+void auth_encrypt_user(const char *);
+void auth_encrypt_connect(int);
+void printd(const unsigned char *, int);
+
+/*
+ * These functions are imported from the application
+ */
+int telnet_net_write(unsigned char *, int);
+void net_encrypt(void);
+int telnet_spin(void);
+char *telnet_getenv(char *);
+char *telnet_gets(char *, char *, int, int);
+#endif
diff --git a/lib/libtelnet/misc.c b/lib/libtelnet/misc.c
new file mode 100644 (file)
index 0000000..32904a0
--- /dev/null
@@ -0,0 +1,93 @@
+/*     $NetBSD: misc.c,v 1.13 2012/03/21 05:33:27 matt 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[] = "@(#)misc.c     8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: misc.c,v 1.13 2012/03/21 05:33:27 matt Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "misc.h"
+#include "auth.h"
+#include "encrypt.h"
+
+const char *RemoteHostName;
+const char *LocalHostName;
+char *UserNameRequested = 0;
+int ConnectedCount = 0;
+
+void
+auth_encrypt_init(const char *local, const char *remote, const char *name,
+       int server)
+{
+       RemoteHostName = remote;
+       LocalHostName = local;
+#ifdef AUTHENTICATION
+       auth_init(name, server);
+#endif
+#ifdef ENCRYPTION
+       encrypt_init(name, server);
+#endif /* ENCRYPTION */
+       if (UserNameRequested) {
+               free(UserNameRequested);
+               UserNameRequested = 0;
+       }
+}
+
+void
+auth_encrypt_user(const char *name)
+{
+       if (UserNameRequested)
+               free(UserNameRequested);
+       UserNameRequested = name ? strdup(name) : 0;
+}
+
+void
+auth_encrypt_connect(int cnt)
+{
+}
+
+void
+printd(const unsigned char *data, int cnt)
+{
+       if (cnt > 16)
+               cnt = 16;
+       while (cnt-- > 0) {
+               printf(" %02x", *data);
+               ++data;
+       }
+}
diff --git a/lib/libtelnet/misc.h b/lib/libtelnet/misc.h
new file mode 100644 (file)
index 0000000..7211650
--- /dev/null
@@ -0,0 +1,46 @@
+/*     $NetBSD: misc.h,v 1.9 2012/01/09 15:25:34 christos 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.
+ *
+ *     from: @(#)misc.h        8.1 (Berkeley) 6/4/93
+ */
+
+__BEGIN_DECLS
+extern char *UserNameRequested;
+extern const char *LocalHostName;
+extern const char *RemoteHostName;
+extern int ConnectedCount;
+extern int ReservedPort;
+
+int isprefix(char *, const char *);
+char **genget(char *, char **, int);
+int Ambiguous(void *);
+__END_DECLS
+
+#include "misc-proto.h"
diff --git a/lib/libtelnet/pk.c b/lib/libtelnet/pk.c
new file mode 100644 (file)
index 0000000..75d009b
--- /dev/null
@@ -0,0 +1,292 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     Dave Safford.  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>
+
+#ifdef notdef
+__FBSDID("$FreeBSD: src/contrib/telnet/libtelnet/pk.c,v 1.10 2002/08/22 06:19:07 nsayer Exp $");
+#else
+__RCSID("$NetBSD: pk.c,v 1.4 2005/02/19 22:55:35 christos Exp $");
+#endif
+
+/* public key routines */
+/* functions:
+       genkeys(char *public, char *secret)
+       common_key(char *secret, char *public, desData *deskey)
+       pk_encode(char *in, *out, DesData *deskey);
+       pk_decode(char *in, *out, DesData *deskey);
+      where
+       char public[HEXKEYBYTES + 1];
+       char secret[HEXKEYBYTES + 1];
+ */
+
+#include <sys/time.h>
+#include <des.h>
+#include <openssl/bn.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pk.h"
+static void adjust(char *, const char *);
+
+/*
+ * Choose top 128 bits of the common key to use as our idea key.
+ */
+static void
+extractideakey(BIGNUM *ck, IdeaData *ideakey)
+{
+       BIGNUM *a = BN_new();
+       BIGNUM *z = BN_new();
+       BIGNUM *base = BN_new();
+       BN_CTX *ctx = BN_CTX_new();
+       size_t i;
+       char *k;
+
+       (void)BN_zero(a);
+       (void)BN_zero(z);
+       (void)BN_set_word(base, 1 << 8);
+       BN_add(a, ck, z);
+
+       for (i = 0; i < ((KEYSIZE - 128) / 8); i++)
+               BN_div(a, z, a, base, ctx);
+
+       k = (char *)(void *)ideakey;
+       for (i = 0; i < 16; i++) {
+               BN_div(a, z, a, base, ctx);
+               *k++ = (char)BN_get_word(z);
+       }
+
+       BN_CTX_free(ctx);
+       BN_free(base);
+       BN_free(z);
+       BN_free(a);
+}
+
+/*
+ * Choose middle 64 bits of the common key to use as our des key, possibly
+ * overwriting the lower order bits by setting parity. 
+ */
+static void
+extractdeskey(BIGNUM *ck, DesData *deskey)
+{
+       BIGNUM *a = BN_new();
+       BIGNUM *z = BN_new();
+       BIGNUM *base = BN_new();
+       BN_CTX *ctx = BN_CTX_new();
+       size_t i;
+       char *k;
+
+       (void)BN_zero(z);
+       (void)BN_zero(a);
+       (void)BN_set_word(base, 1 << 8);
+       BN_add(a, ck, z);
+
+       for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++)
+               BN_div(a, z, a, base, ctx);
+
+       k = (char *)deskey;
+       for (i = 0; i < 8; i++) {
+               BN_div(a, z, a, base, ctx);
+               *k++ = (char)BN_get_word(z);
+       }
+
+       BN_CTX_free(ctx);
+       BN_free(base);
+       BN_free(z);
+       BN_free(a);
+}
+
+/*
+ * get common key from my secret key and his public key
+ */
+void
+common_key(char *xsecret, char *xpublic, IdeaData *ideakey, DesData *deskey)
+{
+       BIGNUM *public = BN_new();
+       BIGNUM *secret = BN_new();
+       BIGNUM *common = BN_new();
+       BIGNUM *modulus  = BN_new();
+       BN_CTX *ctx = BN_CTX_new();
+
+       (void)BN_hex2bn(&modulus, HEXMODULUS);
+       (void)BN_hex2bn(&public, xpublic);
+       (void)BN_hex2bn(&secret, xsecret);
+       (void)BN_zero(common);
+
+       BN_mod_exp(common, public, secret, modulus, ctx);
+       extractdeskey(common, deskey);
+       extractideakey(common, ideakey);
+       des_set_odd_parity(deskey);
+
+       BN_CTX_free(ctx);
+       BN_free(common);
+       BN_free(secret);
+       BN_free(public);
+       BN_free(modulus);
+}
+
+/*
+ * Generate a seed
+ */
+static void
+getseed(char *seed, size_t seedsize)
+{
+       size_t i;
+
+       for (i = 0; i < seedsize; i++)
+               seed[i] = arc4random() & 0xff;
+}
+
+/*
+ * Generate a random public/secret key pair
+ */
+void
+genkeys(char *public, char *secret)
+{
+       size_t i;
+#      define BASEBITS (8 * sizeof(short) - 1)
+#      define BASE (1 << BASEBITS)
+       BIGNUM *pk = BN_new();
+       BIGNUM *sk = BN_new();
+       BIGNUM *tmp = BN_new();
+       BIGNUM *base = BN_new();
+       BIGNUM *root = BN_new();
+       BIGNUM *modulus = BN_new();
+       BN_CTX *ctx = BN_CTX_new();
+       short r;
+       unsigned short seed[KEYSIZE/BASEBITS + 1];
+       char *xkey;
+
+       (void)BN_zero(pk);
+       (void)BN_zero(sk);
+       (void)BN_set_word(base, BASE);
+       (void)BN_set_word(root, PROOT);
+       (void)BN_hex2bn(&modulus, HEXMODULUS);
+
+       getseed((char *)seed, sizeof(seed));    
+       for (i = 0; i < KEYSIZE/BASEBITS + 1; i++) {
+               r = seed[i] % BASE;
+               (void)BN_set_word(tmp, r);
+               BN_mul(sk, tmp, sk, ctx);
+               BN_add(sk, tmp, sk);
+       }
+
+       (void)BN_zero(tmp);
+       BN_div(tmp, sk, sk, modulus, ctx);
+       BN_mod_exp(pk, root, sk, modulus, ctx);
+
+       xkey = BN_bn2hex(sk);   
+       adjust(secret, xkey);
+       xkey = BN_bn2hex(pk);
+       adjust(public, xkey);
+
+       BN_CTX_free(ctx);
+       BN_free(sk);
+       BN_free(base);
+       BN_free(pk);
+       BN_free(tmp);
+       BN_free(root);
+       BN_free(modulus);
+} 
+
+/*
+ * Adjust the input key so that it is 0-filled on the left
+ */
+static void
+adjust(char *keyout, const char *keyin)
+{
+       const char *p;
+       char *s;
+
+       for (p = keyin; *p; p++) 
+               continue;
+       for (s = keyout + HEXKEYBYTES; p >= keyin; p--, s--)
+               *s = *p;
+       while (s >= keyout)
+               *s-- = '0';
+}
+
+static const char hextab[] = "0123456789ABCDEF";
+
+/* given a DES key, cbc encrypt and translate input to terminated hex */
+void
+pk_encode(const char *in, char *out, DesData *key)
+{
+       char buf[256];
+       DesData i;
+       des_key_schedule k;
+       size_t l, op, deslen;
+
+       (void)memset(&i, 0, sizeof(i));
+       (void)memset(buf, 0, sizeof(buf));
+       deslen = ((strlen(in) + 7) / 8) * 8;
+       des_key_sched(key, k);
+       des_cbc_encrypt(in, buf, deslen, k, &i, DES_ENCRYPT);
+       for (l = 0, op = 0; l < deslen; l++) {
+               out[op++] = hextab[(buf[l] & 0xf0) >> 4];
+               out[op++] = hextab[(buf[l] & 0x0f)];
+       }
+       out[op] = '\0';
+}
+
+/* given a DES key, translate input from hex and decrypt */
+void
+pk_decode(const char *in, char *out, DesData *key)
+{
+       char buf[256];
+       DesData i;
+       des_key_schedule k;
+       int n1, n2, op;
+       size_t l;
+       size_t len = strlen(in) / 2;
+
+       (void)memset(&i, 0, sizeof(i));
+       (void)memset(buf, 0, sizeof(buf));
+
+       for (l = 0, op = 0; l < len; l++, op += 2) {
+               if (in[op] > '9')
+                       n1 = in[op] - 'A' + 10;
+               else
+                       n1 = in[op] - '0';
+               if (in[op + 1] > '9')
+                       n2 = in[op + 1] - 'A' + 10;
+               else
+                       n2 = in[op + 1] - '0';
+               buf[l] = (char)(n1 * 16 + n2);
+       }
+       des_key_sched(key, k);
+       des_cbc_encrypt(buf, out, len, k, &i, DES_DECRYPT);
+       out[len] = '\0';
+}
diff --git a/lib/libtelnet/pk.h b/lib/libtelnet/pk.h
new file mode 100644 (file)
index 0000000..f47b447
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *      Dave Safford.  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.
+ * 
+ * $FreeBSD: src/contrib/telnet/libtelnet/pk.h,v 1.6 2001/11/30 21:06:34 markm Exp $
+ */
+
+/* header for the des routines that we will use */
+
+typedef unsigned char byte, DesData[ 8], IdeaData[16];
+#define DesKeys des_key_schedule
+
+#define DES_DECRYPT 0
+#define DES_ENCRYPT 1
+
+/* public key routines */
+/* functions:
+       genkeys(char *public, char *secret)
+       common_key(char *secret, char *public, desData *deskey)
+      where
+       char public[HEXKEYBYTES + 1];
+       char secret[HEXKEYBYTES + 1];
+ */
+
+#define HEXMODULUS "d4a0ba0250b6fd2ec626e7efd637df76c716e22d0944b88b"
+#define HEXKEYBYTES 48
+#define KEYSIZE 192
+#define KEYBYTES 24
+#define PROOT 3
+
+extern void genkeys(char *public, char *secret);
+extern void common_key(char *secret, char *public, IdeaData *common,
+  DesData *deskey);
+extern void pk_encode(const char *in, char *out, DesData *deskey);
+extern void pk_decode(const char *in, char *out, DesData *deskey);
diff --git a/lib/libtelnet/shlib_version b/lib/libtelnet/shlib_version
new file mode 100644 (file)
index 0000000..4075bd0
--- /dev/null
@@ -0,0 +1,5 @@
+#      $NetBSD: shlib_version,v 1.11 2009/01/11 03:07:50 christos Exp $
+#      Remember to update distrib/sets/lists/base/shl.* when changing
+#
+major=7
+minor=0
diff --git a/lib/libtelnet/spx.c b/lib/libtelnet/spx.c
new file mode 100644 (file)
index 0000000..054fc51
--- /dev/null
@@ -0,0 +1,594 @@
+/*     $NetBSD: spx.c,v 1.7 2005/04/19 03:19:46 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 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[] = "@(#)spx.c      8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: spx.c,v 1.7 2005/04/19 03:19:46 christos Exp $");
+#endif
+#endif /* not lint */
+
+#ifdef SPX
+/*
+ * COPYRIGHT (C) 1990 DIGITAL EQUIPMENT CORPORATION
+ * ALL RIGHTS RESERVED
+ *
+ * "Digital Equipment Corporation authorizes the reproduction,
+ * distribution and modification of this software subject to the following
+ * restrictions:
+ *
+ * 1.  Any partial or whole copy of this software, or any modification
+ * thereof, must include this copyright notice in its entirety.
+ *
+ * 2.  This software is supplied "as is" with no warranty of any kind,
+ * expressed or implied, for any purpose, including any warranty of fitness
+ * or merchantibility.  DIGITAL assumes no responsibility for the use or
+ * reliability of this software, nor promises to provide any form of
+ * support for it on any basis.
+ *
+ * 3.  Distribution of this software is authorized only if no profit or
+ * remuneration of any kind is received in exchange for such distribution.
+ *
+ * 4.  This software produces public key authentication certificates
+ * bearing an expiration date established by DIGITAL and RSA Data
+ * Security, Inc.  It may cease to generate certificates after the expiration
+ * date.  Any modification of this software that changes or defeats
+ * the expiration date or its effect is unauthorized.
+ *
+ * 5.  Software that will renew or extend the expiration date of
+ * authentication certificates produced by this software may be obtained
+ * from RSA Data Security, Inc., 10 Twin Dolphin Drive, Redwood City, CA
+ * 94065, (415)595-8782, or from DIGITAL"
+ *
+ */
+
+#include <sys/types.h>
+#include <arpa/telnet.h>
+#include <stdio.h>
+#include "gssapi_defs.h"
+#include <stdlib.h>
+#ifdef NO_STRING_H
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+
+#include <pwd.h>
+#include "encrypt.h"
+#include "auth.h"
+#include "misc.h"
+
+extern auth_debug_mode;
+
+static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0,
+                                       AUTHTYPE_SPX, };
+static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION,
+                                       TELQUAL_NAME, };
+
+#define        SPX_AUTH        0               /* Authentication data follows */
+#define        SPX_REJECT      1               /* Rejected (reason might follow) */
+#define SPX_ACCEPT     2               /* Accepted */
+
+#ifdef ENCRYPTION
+static Block   session_key     = { 0 };
+#endif /* ENCRYPTION */
+static Schedule sched;
+static Block   challenge       = { 0 };
+
+
+/*******************************************************************/
+
+gss_OID_set            actual_mechs;
+gss_OID                        actual_mech_type, output_name_type;
+int                    major_status, status, msg_ctx = 0, new_status;
+int                    req_flags = 0, ret_flags, lifetime_rec;
+gss_cred_id_t          gss_cred_handle;
+gss_ctx_id_t           actual_ctxhandle, context_handle;
+gss_buffer_desc                output_token, input_token, input_name_buffer;
+gss_buffer_desc                status_string;
+gss_name_t             desired_targname, src_name;
+gss_channel_bindings   input_chan_bindings;
+char                   lhostname[GSS_C_MAX_PRINTABLE_NAME];
+char                   targ_printable[GSS_C_MAX_PRINTABLE_NAME];
+int                    to_addr=0, from_addr=0;
+char                   *address;
+gss_buffer_desc                fullname_buffer;
+gss_OID                        fullname_type;
+gss_cred_id_t          gss_delegated_cred_handle;
+
+/*******************************************************************/
+
+
+
+       static int
+Data(ap, type, d, c)
+       Authenticator *ap;
+       int type;
+       void *d;
+       int c;
+{
+       unsigned char *p = str_data + 4;
+       unsigned char *cd = (unsigned char *)d;
+
+       if (c == -1)
+               c = strlen((char *)cd);
+
+       if (0) {
+               printf("%s:%d: [%d] (%d)",
+                       str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
+                       str_data[3],
+                       type, c);
+               printd(d, c);
+               printf("\r\n");
+       }
+       *p++ = ap->type;
+       *p++ = ap->way;
+       *p++ = type;
+       while (c-- > 0) {
+               if ((*p++ = *cd++) == IAC)
+                       *p++ = IAC;
+       }
+       *p++ = IAC;
+       *p++ = SE;
+       if (str_data[3] == TELQUAL_IS)
+               printsub('>', &str_data[2], p - (&str_data[2]));
+       return(telnet_net_write(str_data, p - str_data));
+}
+
+       int
+spx_init(ap, server)
+       Authenticator *ap;
+       int server;
+{
+       gss_cred_id_t   tmp_cred_handle;
+
+       if (server) {
+               str_data[3] = TELQUAL_REPLY;
+               gethostname(lhostname, sizeof(lhostname));
+               strlcpy(targ_printable, "SERVICE:rcmd@",
+                   sizeof(targ_printable));
+               strlcat(targ_printable, lhostname, sizeof(targ_printable));
+               input_name_buffer.length = strlen(targ_printable);
+               input_name_buffer.value = targ_printable;
+               major_status = gss_import_name(&status,
+                                       &input_name_buffer,
+                                       GSS_C_NULL_OID,
+                                       &desired_targname);
+               major_status = gss_acquire_cred(&status,
+                                       desired_targname,
+                                       0,
+                                       GSS_C_NULL_OID_SET,
+                                       GSS_C_ACCEPT,
+                                       &tmp_cred_handle,
+                                       &actual_mechs,
+                                       &lifetime_rec);
+               if (major_status != GSS_S_COMPLETE) return(0);
+       } else {
+               str_data[3] = TELQUAL_IS;
+       }
+       return(1);
+}
+
+       int
+spx_send(ap)
+       Authenticator *ap;
+{
+       Block enckey;
+       int r;
+
+       gss_OID actual_mech_type, output_name_type;
+       int     msg_ctx = 0, new_status, status;
+       int     req_flags = 0, ret_flags, lifetime_rec, major_status;
+       gss_buffer_desc  output_token, input_token, input_name_buffer;
+       gss_buffer_desc  output_name_buffer, status_string;
+       gss_name_t    desired_targname;
+       gss_channel_bindings  input_chan_bindings;
+       char targ_printable[GSS_C_MAX_PRINTABLE_NAME];
+       int  from_addr=0, to_addr=0, myhostlen, j;
+       int  deleg_flag=1, mutual_flag=0, replay_flag=0, seq_flag=0;
+       char *address;
+
+       printf("[ Trying SPX ... ]\n");
+       strlcpy(targ_printable, "SERVICE:rcmd@", sizeof(targ_printable));
+       strlcat(targ_printable, RemoteHostName, sizeof(targ_printable));
+
+       input_name_buffer.length = strlen(targ_printable);
+       input_name_buffer.value = targ_printable;
+
+       if (!UserNameRequested) {
+               return(0);
+       }
+
+       major_status = gss_import_name(&status,
+                                       &input_name_buffer,
+                                       GSS_C_NULL_OID,
+                                       &desired_targname);
+
+
+       major_status = gss_display_name(&status,
+                                       desired_targname,
+                                       &output_name_buffer,
+                                       &output_name_type);
+
+       printf("target is '%s'\n", output_name_buffer.value); fflush(stdout);
+
+       major_status = gss_release_buffer(&status, &output_name_buffer);
+
+       input_chan_bindings = (gss_channel_bindings)
+         malloc(sizeof(gss_channel_bindings_desc));
+
+       input_chan_bindings->initiator_addrtype = GSS_C_AF_INET;
+       input_chan_bindings->initiator_address.length = 4;
+       address = (char *) malloc(4);
+       input_chan_bindings->initiator_address.value = (char *) address;
+       address[0] = ((from_addr & 0xff000000) >> 24);
+       address[1] = ((from_addr & 0xff0000) >> 16);
+       address[2] = ((from_addr & 0xff00) >> 8);
+       address[3] = (from_addr & 0xff);
+       input_chan_bindings->acceptor_addrtype = GSS_C_AF_INET;
+       input_chan_bindings->acceptor_address.length = 4;
+       address = (char *) malloc(4);
+       input_chan_bindings->acceptor_address.value = (char *) address;
+       address[0] = ((to_addr & 0xff000000) >> 24);
+       address[1] = ((to_addr & 0xff0000) >> 16);
+       address[2] = ((to_addr & 0xff00) >> 8);
+       address[3] = (to_addr & 0xff);
+       input_chan_bindings->application_data.length = 0;
+
+       req_flags = 0;
+       if (deleg_flag)  req_flags = req_flags | 1;
+       if (mutual_flag) req_flags = req_flags | 2;
+       if (replay_flag) req_flags = req_flags | 4;
+       if (seq_flag)    req_flags = req_flags | 8;
+
+       major_status = gss_init_sec_context(&status,         /* minor status */
+                                       GSS_C_NO_CREDENTIAL, /* cred handle */
+                                       &actual_ctxhandle,   /* ctx handle */
+                                       desired_targname,    /* target name */
+                                       GSS_C_NULL_OID,      /* mech type */
+                                       req_flags,           /* req flags */
+                                       0,                   /* time req */
+                                       input_chan_bindings, /* chan binding */
+                                       GSS_C_NO_BUFFER,     /* input token */
+                                       &actual_mech_type,   /* actual mech */
+                                       &output_token,       /* output token */
+                                       &ret_flags,          /* ret flags */
+                                       &lifetime_rec);      /* time rec */
+
+       if ((major_status != GSS_S_COMPLETE) &&
+           (major_status != GSS_S_CONTINUE_NEEDED)) {
+         gss_display_status(&new_status,
+                               status,
+                               GSS_C_MECH_CODE,
+                               GSS_C_NULL_OID,
+                               &msg_ctx,
+                               &status_string);
+         printf("%s\n", status_string.value);
+         return(0);
+       }
+
+       if (!auth_sendname(UserNameRequested, strlen(UserNameRequested))) {
+               return(0);
+       }
+
+       if (!Data(ap, SPX_AUTH, (void *)output_token.value, output_token.length)) {
+               return(0);
+       }
+
+       return(1);
+}
+
+       void
+spx_is(ap, data, cnt)
+       Authenticator *ap;
+       unsigned char *data;
+       int cnt;
+{
+       Session_Key skey;
+       Block datablock;
+       int r;
+
+       if (cnt-- < 1)
+               return;
+       switch (*data++) {
+       case SPX_AUTH:
+               input_token.length = cnt;
+               input_token.value = (char *) data;
+
+               gethostname(lhostname, sizeof(lhostname));
+
+               strlcpy(targ_printable, "SERVICE:rcmd@",
+                   sizeof(targ_printable));
+               strlcat(targ_printable, lhostname, sizeof(targ_printable));
+
+               input_name_buffer.length = strlen(targ_printable);
+               input_name_buffer.value = targ_printable;
+
+               major_status = gss_import_name(&status,
+                                       &input_name_buffer,
+                                       GSS_C_NULL_OID,
+                                       &desired_targname);
+
+               major_status = gss_acquire_cred(&status,
+                                       desired_targname,
+                                       0,
+                                       GSS_C_NULL_OID_SET,
+                                       GSS_C_ACCEPT,
+                                       &gss_cred_handle,
+                                       &actual_mechs,
+                                       &lifetime_rec);
+
+               major_status = gss_release_name(&status, desired_targname);
+
+               input_chan_bindings = (gss_channel_bindings)
+                 malloc(sizeof(gss_channel_bindings_desc));
+
+               input_chan_bindings->initiator_addrtype = GSS_C_AF_INET;
+               input_chan_bindings->initiator_address.length = 4;
+               address = (char *) malloc(4);
+               input_chan_bindings->initiator_address.value = (char *) address;
+               address[0] = ((from_addr & 0xff000000) >> 24);
+               address[1] = ((from_addr & 0xff0000) >> 16);
+               address[2] = ((from_addr & 0xff00) >> 8);
+               address[3] = (from_addr & 0xff);
+               input_chan_bindings->acceptor_addrtype = GSS_C_AF_INET;
+               input_chan_bindings->acceptor_address.length = 4;
+               address = (char *) malloc(4);
+               input_chan_bindings->acceptor_address.value = (char *) address;
+               address[0] = ((to_addr & 0xff000000) >> 24);
+               address[1] = ((to_addr & 0xff0000) >> 16);
+               address[2] = ((to_addr & 0xff00) >> 8);
+               address[3] = (to_addr & 0xff);
+               input_chan_bindings->application_data.length = 0;
+
+               major_status = gss_accept_sec_context(&status,
+                                               &context_handle,
+                                               gss_cred_handle,
+                                               &input_token,
+                                               input_chan_bindings,
+                                               &src_name,
+                                               &actual_mech_type,
+                                               &output_token,
+                                               &ret_flags,
+                                               &lifetime_rec,
+                                               &gss_delegated_cred_handle);
+
+
+               if (major_status != GSS_S_COMPLETE) {
+
+                 major_status = gss_display_name(&status,
+                                       src_name,
+                                       &fullname_buffer,
+                                       &fullname_type);
+                       Data(ap, SPX_REJECT, (void *)"auth failed", -1);
+                       auth_finished(ap, AUTH_REJECT);
+                       return;
+               }
+
+               major_status = gss_display_name(&status,
+                                       src_name,
+                                       &fullname_buffer,
+                                       &fullname_type);
+
+
+               Data(ap, SPX_ACCEPT, (void *)output_token.value, output_token.length);
+               auth_finished(ap, AUTH_USER);
+               break;
+
+       default:
+               Data(ap, SPX_REJECT, 0, 0);
+               break;
+       }
+}
+
+
+       void
+spx_reply(ap, data, cnt)
+       Authenticator *ap;
+       unsigned char *data;
+       int cnt;
+{
+       Session_Key skey;
+
+       if (cnt-- < 1)
+               return;
+       switch (*data++) {
+       case SPX_REJECT:
+               if (cnt > 0) {
+                       printf("[ SPX refuses authentication because %.*s ]\r\n",
+                               cnt, data);
+               } else
+                       printf("[ SPX refuses authentication ]\r\n");
+               auth_send_retry();
+               return;
+       case SPX_ACCEPT:
+               printf("[ SPX accepts you ]\n");
+               if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
+                       /*
+                        * Send over the encrypted challenge.
+                        */
+                 input_token.value = (char *) data;
+                 input_token.length = cnt;
+
+                 major_status = gss_init_sec_context(&status, /* minor stat */
+                                       GSS_C_NO_CREDENTIAL, /* cred handle */
+                                       &actual_ctxhandle,   /* ctx handle */
+                                       desired_targname,    /* target name */
+                                       GSS_C_NULL_OID,      /* mech type */
+                                       req_flags,           /* req flags */
+                                       0,                   /* time req */
+                                       input_chan_bindings, /* chan binding */
+                                       &input_token,        /* input token */
+                                       &actual_mech_type,   /* actual mech */
+                                       &output_token,       /* output token */
+                                       &ret_flags,          /* ret flags */
+                                       &lifetime_rec);      /* time rec */
+
+                 if (major_status != GSS_S_COMPLETE) {
+                   gss_display_status(&new_status,
+                                       status,
+                                       GSS_C_MECH_CODE,
+                                       GSS_C_NULL_OID,
+                                       &msg_ctx,
+                                       &status_string);
+                   printf("[ SPX mutual response fails ... '%s' ]\r\n",
+                        status_string.value);
+                   auth_send_retry();
+                   return;
+                 }
+               }
+               auth_finished(ap, AUTH_USER);
+               return;
+
+       default:
+               return;
+       }
+}
+
+       int
+spx_status(ap, name, l, level)
+       Authenticator *ap;
+       char *name;
+       size_t l;
+       int level;
+{
+
+       gss_buffer_desc  fullname_buffer, acl_file_buffer;
+       gss_OID          fullname_type;
+       char acl_file[160], fullname[160];
+       int major_status, status = 0;
+       struct passwd pws, *pwd;
+       char pwbuf[1024];
+
+       /*
+        * hard code fullname to
+        *   "SPX:/C=US/O=Digital/OU=LKG/OU=Sphinx/OU=Users/CN=Kannan Alagappan"
+        * and acl_file to "~kannan/.sphinx"
+        */
+
+       if (getpwnam_r(UserNameRequested, &pws, pwbuf, sizeof(pwbuf), &pwd)
+           != 0 || pwd == NULL) {
+           return(AUTH_USER);   /*  not authenticated  */
+       }
+
+       strlcpy(acl_file, pwd->pw_dir, sizeof(acl_file));
+       strlcat(acl_file, "/.sphinx", sizeof(acl_file));
+       acl_file_buffer.value = acl_file;
+       acl_file_buffer.length = strlen(acl_file);
+
+       major_status = gss_display_name(&status,
+                                       src_name,
+                                       &fullname_buffer,
+                                       &fullname_type);
+
+       if (level < AUTH_USER)
+               return(level);
+
+       major_status = gss__check_acl(&status, &fullname_buffer,
+                                       &acl_file_buffer);
+
+       if (major_status == GSS_S_COMPLETE) {
+         strlcpy(name, UserNameRequested, l);
+         return(AUTH_VALID);
+       } else {
+          return(AUTH_USER);
+       }
+
+}
+
+#define        BUMP(buf, len)          while (*(buf)) {++(buf), --(len);}
+#define        ADDC(buf, len, c)       if ((len) > 0) {*(buf)++ = (c); --(len);}
+
+       void
+spx_printsub(data, cnt, buf, buflen)
+       unsigned char *data, *buf;
+       int cnt, buflen;
+{
+       char lbuf[32];
+       register int i;
+
+       buf[buflen-1] = '\0';           /* make sure its NULL terminated */
+       buflen -= 1;
+
+       switch(data[3]) {
+       case SPX_REJECT:                /* Rejected (reason might follow) */
+               strncpy((char *)buf, " REJECT ", buflen);
+               goto common;
+
+       case SPX_ACCEPT:                /* Accepted (name might follow) */
+               strncpy((char *)buf, " ACCEPT ", buflen);
+       common:
+               BUMP(buf, buflen);
+               if (cnt <= 4)
+                       break;
+               ADDC(buf, buflen, '"');
+               for (i = 4; i < cnt; i++)
+                       ADDC(buf, buflen, data[i]);
+               ADDC(buf, buflen, '"');
+               ADDC(buf, buflen, '\0');
+               break;
+
+       case SPX_AUTH:                  /* Authentication data follows */
+               strncpy((char *)buf, " AUTH", buflen);
+               goto common2;
+
+       default:
+               snprintf(lbuf, sizeof(lbuf), " %d (unknown)", data[3]);
+               strncpy((char *)buf, lbuf, buflen);
+       common2:
+               BUMP(buf, buflen);
+               for (i = 4; i < cnt; i++) {
+                       snprintf(lbuf, sizeof(lbuf), " %d", data[i]);
+                       strncpy((char *)buf, lbuf, buflen);
+                       BUMP(buf, buflen);
+               }
+               break;
+       }
+}
+
+#endif
+
+#ifdef notdef
+
+prkey(msg, key)
+       char *msg;
+       unsigned char *key;
+{
+       register int i;
+       printf("%s:", msg);
+       for (i = 0; i < 8; i++)
+               printf(" %3d", key[i]);
+       printf("\r\n");
+}
+#endif
diff --git a/lib/libtelnet/sra.c b/lib/libtelnet/sra.c
new file mode 100644 (file)
index 0000000..fcef257
--- /dev/null
@@ -0,0 +1,637 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *      Dave Safford.  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>
+#ifdef notdef
+__FBSDID("$FreeBSD: src/contrib/telnet/libtelnet/sra.c,v 1.16 2002/05/06 09:48:02 markm Exp $");
+#else
+__RCSID("$NetBSD: sra.c,v 1.11 2012/01/09 15:25:34 christos Exp $");
+#endif
+
+#ifdef SRA
+#ifdef ENCRYPTION
+#include <sys/types.h>
+#include <arpa/telnet.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <ttyent.h>
+
+#ifndef NOPAM
+#include <security/pam_appl.h>
+#else
+#include <unistd.h>
+#endif
+
+#include "auth.h"
+#include "misc.h"
+#include "encrypt.h"
+#include "pk.h"
+
+char pka[HEXKEYBYTES+1], ska[HEXKEYBYTES+1], pkb[HEXKEYBYTES+1];
+char *user, *pass, *xuser, *xpass;
+char *passprompt, *xpassprompt;
+DesData ck;
+IdeaData ik;
+
+extern int auth_debug_mode;
+extern char *line;             /* see sys_term.c */
+
+static int sra_valid = 0;
+static int passwd_sent = 0;
+
+static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0,
+                                       AUTHTYPE_SRA, };
+
+#define SMALL_LEN      256
+#define XSMALL_LEN     513
+#define SRA_KEY        0
+#define SRA_USER 1
+#define SRA_CONTINUE 2
+#define SRA_PASS 3
+#define SRA_ACCEPT 4
+#define SRA_REJECT 5
+
+static int check_user(char *, const char *);
+
+/* support routine to send out authentication message */
+static int
+Data(Authenticator *ap, int type, void *d, int c)
+{
+        unsigned char *p = str_data + 4;
+       unsigned char *cd = d;
+
+       if (c == -1)
+               c = strlen(d);
+
+        if (auth_debug_mode) {
+                printf("%s:%d: [%d] (%d)",
+                   str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
+                   str_data[3], type, c);
+                printd(d, c);
+                printf("\r\n");
+        }
+       *p++ = ap->type;
+       *p++ = ap->way;
+       *p++ = type;
+        while (c-- > 0) {
+                if ((*p++ = *cd++) == IAC)
+                        *p++ = IAC;
+        }
+        *p++ = IAC;
+        *p++ = SE;
+       if (str_data[3] == TELQUAL_IS)
+               printsub('>', &str_data[2], p - (&str_data[2]));
+        return telnet_net_write(str_data, p - str_data);
+}
+
+int
+sra_init(Authenticator *ap __unused, int server)
+{
+       if (server)
+               str_data[3] = TELQUAL_REPLY;
+       else
+               str_data[3] = TELQUAL_IS;
+
+       user = malloc(SMALL_LEN);
+       xuser = malloc(XSMALL_LEN);
+       pass = malloc(SMALL_LEN);
+       xpass = malloc(XSMALL_LEN);
+       passprompt = malloc(SMALL_LEN);
+       xpassprompt = malloc(XSMALL_LEN);
+
+       if (user == NULL || xuser == NULL || pass == NULL || xpass ==
+           NULL || passprompt == NULL || xpassprompt == NULL)
+               return 0; /* malloc failed */
+
+       passwd_sent = 0;
+       
+       genkeys(pka, ska);
+       return 1;
+}
+
+/* client received a go-ahead for sra */
+int
+sra_send(Authenticator *ap)
+{
+       /* send PKA */
+
+       if (auth_debug_mode)
+               printf("Sent PKA to server.\r\n" );
+       printf("Trying SRA secure login:\r\n");
+       if (!Data(ap, SRA_KEY, (void *)pka, HEXKEYBYTES)) {
+               if (auth_debug_mode)
+                       printf("Not enough room for authentication data\r\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+/* server received an IS -- could be SRA KEY, USER, or PASS */
+void
+sra_is(Authenticator *ap, unsigned char *data, int cnt)
+{
+       int valid;
+       Session_Key skey;
+
+       if (cnt-- < 1)
+               goto bad;
+       switch (*data++) {
+
+       case SRA_KEY:
+               if (cnt < HEXKEYBYTES) {
+                       Data(ap, SRA_REJECT, (void *)0, 0);
+                       auth_finished(ap, AUTH_USER);
+                       if (auth_debug_mode) {
+                               printf("SRA user rejected for bad PKB\r\n");
+                       }
+                       return;
+               }
+               if (auth_debug_mode)
+                       printf("Sent pka\r\n");
+               if (!Data(ap, SRA_KEY, (void *)pka, HEXKEYBYTES)) {
+                       if (auth_debug_mode)
+                               printf("Not enough room\r\n");
+                       return;
+               }
+               memcpy(pkb, data, HEXKEYBYTES);
+               pkb[HEXKEYBYTES] = '\0';
+               common_key(ska, pkb, &ik, &ck);
+               return;
+
+       case SRA_USER:
+               /* decode KAB(u) */
+               if (cnt > XSMALL_LEN - 1) /* Attempted buffer overflow */
+                       break;
+               memcpy(xuser, data, cnt);
+               xuser[cnt] = '\0';
+               pk_decode(xuser, user, &ck);
+               auth_encrypt_user(user);
+#ifndef NOPAM
+               (void)check_user(user, "*");
+#endif
+               pk_encode(passprompt, xpassprompt, &ck);
+               Data(ap, SRA_CONTINUE, xpassprompt, XSMALL_LEN - 1);
+
+               return;
+
+       case SRA_PASS:
+               if (cnt > XSMALL_LEN - 1) /* Attempted buffer overflow */
+                       break;
+               /* decode KAB(P) */
+               memcpy(xpass, data, cnt);
+               xpass[cnt] = '\0';
+               pk_decode(xpass, pass, &ck);
+
+               /* check user's password */
+               valid = check_user(user, pass);
+
+               if(valid) {
+                       /* PAM (via check_user()) may have changed 'user' */
+                       auth_encrypt_user(user);
+                       Data(ap, SRA_ACCEPT, (void *)0, 0);
+                       skey.data = ck;
+                       skey.type = SK_DES;
+                       skey.length = 8;
+                       encrypt_session_key(&skey, 1);
+
+                       sra_valid = 1;
+                       auth_finished(ap, AUTH_VALID);
+                       if (auth_debug_mode) {
+                               printf("SRA user accepted\r\n");
+                       }
+               }
+               else {
+                       pk_encode(passprompt, xpassprompt, &ck);
+                       Data(ap, SRA_CONTINUE, (void *)xpassprompt,
+                           XSMALL_LEN - 1);
+                       if (auth_debug_mode) {
+                               printf("SRA user failed\r\n");
+                       }
+               }
+               return;
+
+       default:
+               if (auth_debug_mode)
+                       printf("Unknown SRA option %d\r\n", data[-1]);
+       }
+bad:
+       Data(ap, SRA_REJECT, 0, 0);
+       sra_valid = 0;
+       auth_finished(ap, AUTH_REJECT);
+}
+
+/* client received REPLY -- could be SRA KEY, CONTINUE, ACCEPT, or REJECT */
+void
+sra_reply(Authenticator *ap, unsigned char *data, int cnt)
+{
+       char uprompt[SMALL_LEN], tuser[SMALL_LEN];
+       Session_Key skey;
+       size_t i;
+
+       if (cnt-- < 1)
+               return;
+       switch (*data++) {
+
+       case SRA_KEY:
+               /* calculate common key */
+               if (cnt < HEXKEYBYTES) {
+                       if (auth_debug_mode) {
+                               printf("SRA user rejected for bad PKB\r\n");
+                       }
+                       return;
+               }
+               memcpy(pkb, data, HEXKEYBYTES);
+               pkb[HEXKEYBYTES] = '\0';                
+
+               common_key(ska, pkb, &ik, &ck);
+
+       enc_user:
+
+               /* encode user */
+               memset(tuser, 0, sizeof(tuser));
+               snprintf(uprompt, sizeof(uprompt), "User (%s): ",
+                   UserNameRequested);
+               if (telnet_gets(uprompt, tuser, SMALL_LEN - 1, 1) == NULL) {
+                       printf("\n");
+                       exit(1);
+               }
+               if (tuser[0] == '\n' || tuser[0] == '\r' )
+                       strlcpy(user, UserNameRequested, SMALL_LEN);
+               else {
+                       /* telnet_gets leaves the newline on */
+                       for(i = 0; i < sizeof(tuser); i++) {
+                               if (tuser[i] == '\n') {
+                                       tuser[i] = '\0';
+                                       break;
+                               }
+                       }
+                       strlcpy(user, tuser, SMALL_LEN);
+               }
+               pk_encode(user, xuser, &ck);
+
+               /* send it off */
+               if (auth_debug_mode)
+                       printf("Sent KAB(U)\r\n");
+               if (!Data(ap, SRA_USER, (void *)xuser, strlen(xuser))) {
+                       if (auth_debug_mode)
+                               printf("Not enough room\r\n");
+                       return;
+               }
+               break;
+
+       case SRA_CONTINUE:
+               if (passwd_sent) {
+                       passwd_sent = 0;
+                       printf("[ SRA login failed ]\r\n");
+                       goto enc_user;
+               }
+               if (cnt > XSMALL_LEN - 1) { 
+                       break;
+               } else if (cnt > 0) {
+                       (void)memcpy(xpassprompt, data, cnt);
+                       pk_decode(xpassprompt, passprompt, &ck);
+               } else {
+                       (void)strlcpy(passprompt, "Password: ", SMALL_LEN);
+               }
+               /* encode password */
+               memset(pass, 0, SMALL_LEN);
+               if (telnet_gets(passprompt, pass, SMALL_LEN - 1, 0) == NULL) {
+                       printf("\n");
+                       exit(1);
+               }
+               pk_encode(pass, xpass, &ck);
+               /* send it off */
+               if (auth_debug_mode)
+                       printf("Sent KAB(P)\r\n");
+               if (!Data(ap, SRA_PASS, (void *)xpass, strlen(xpass))) {
+                       if (auth_debug_mode)
+                               printf("Not enough room\r\n");
+                       return;
+               }
+               passwd_sent = 1;
+               break;
+
+       case SRA_REJECT:
+               printf("[ SRA refuses authentication ]\r\n");
+               printf("Trying plaintext login:\r\n");
+               auth_finished(0, AUTH_REJECT);
+               return;
+
+       case SRA_ACCEPT:
+               printf("[ SRA accepts you ]\r\n");
+               skey.data = ck;
+               skey.type = SK_DES;
+               skey.length = 8;
+               encrypt_session_key(&skey, 0);
+
+               auth_finished(ap, AUTH_VALID);
+               return;
+       default:
+               if (auth_debug_mode)
+                       printf("Unknown SRA option %d\r\n", data[-1]);
+               return;
+       }
+}
+
+int
+sra_status(Authenticator *ap __unused, char *name, size_t len, int level)
+{
+       if (level < AUTH_USER)
+               return level;
+       if (UserNameRequested && sra_valid) {
+               strlcpy(name, UserNameRequested, len);
+               return AUTH_VALID;
+       } else
+               return AUTH_USER;
+}
+
+#define        BUMP(buf, len)          while (*(buf)) { ++(buf), --(len); }
+#define        ADDC(buf, len, c)       if ((len) > 0) { *(buf)++ = (c); --(len); }
+
+void
+sra_printsub(unsigned char *data, int cnt, unsigned char *ubuf, int buflen)
+{
+       char lbuf[32], *buf = (char *)ubuf;
+       int i;
+
+       buf[buflen - 1] = '\0';                 /* make sure its NULL terminated */
+       buflen -= 1;
+
+       switch(data[3]) {
+
+       case SRA_CONTINUE:
+               strncpy(buf, " CONTINUE ", buflen);
+               goto common;
+
+       case SRA_REJECT:                /* Rejected (reason might follow) */
+               strncpy(buf, " REJECT ", buflen);
+               goto common;
+
+       case SRA_ACCEPT:                /* Accepted (name might follow) */
+               strncpy(buf, " ACCEPT ", buflen);
+
+       common:
+               BUMP(buf, buflen);
+               if (cnt <= 4)
+                       break;
+               ADDC(buf, buflen, '"');
+               for (i = 4; i < cnt; i++)
+                       ADDC(buf, buflen, data[i]);
+               ADDC(buf, buflen, '"');
+               ADDC(buf, buflen, '\0');
+               break;
+
+       case SRA_KEY:                   /* Authentication data follows */
+               strncpy(buf, " KEY ", buflen);
+               goto common2;
+
+       case SRA_USER:
+               strncpy(buf, " USER ", buflen);
+               goto common2;
+
+       case SRA_PASS:
+               strncpy(buf, " PASS ", buflen);
+               goto common2;
+
+       default:
+               snprintf(lbuf, sizeof(lbuf), " %d (unknown)", data[3]);
+               strncpy(buf, lbuf, buflen);
+       common2:
+               BUMP(buf, buflen);
+               for (i = 4; i < cnt; i++) {
+                       snprintf(lbuf, sizeof(lbuf), " %d", data[i]);
+                       strncpy(buf, lbuf, buflen);
+                       BUMP(buf, buflen);
+               }
+               break;
+       }
+}
+
+#ifdef NOPAM
+static int
+isroot(const char *usr)
+{
+       struct passwd pws, *pwd;
+       char pwbuf[1024];
+
+       if (getpwnam_r(usr, &pws, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
+           pwd == NULL)
+               return 0;
+       return (!pwd->pw_uid);
+}
+
+static int
+rootterm(const char *ttyname)
+{
+       struct ttyent *t;
+       const char *ttyn;
+
+       ttyn = ttyname;
+       if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
+               ttyn += sizeof(_PATH_DEV) - 1;
+
+       return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
+}
+
+static int
+check_user(char *name, const char *cred)
+{
+       struct passwd pws, *pw;
+       char pwbuf[1024];
+       char *xpasswd, *salt;
+
+       if (isroot(name) && !rootterm(line))
+       {
+               crypt("AA", "*"); /* Waste some time to simulate success */
+               return 0;
+       }
+
+       if (getpwnam_r(name, &pws, pwbuf, sizeof(pwbuf), &pw) == 0 &&
+           pw != NULL) {
+               if (pw->pw_shell == NULL) {
+                       return 0;
+               }
+
+               salt = pw->pw_passwd;
+               xpasswd = crypt(cred, salt);
+               /* The strcmp does not catch null passwords! */
+               if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
+                       return 0;
+               }
+               return 1;
+       }
+       return 0;
+}
+#else  /* !NOPAM */
+
+/*
+ * The following is stolen from ftpd, which stole it from the imap-uw
+ * PAM module and login.c. It is needed because we can't really
+ * "converse" with the user, having already gone to the trouble of
+ * getting their username and password through an encrypted channel.
+ */
+
+#define COPY_STRING(s) (s ? strdup(s) : NULL)
+
+struct cred_t {
+       const char *uname;
+       const char *pass;
+};
+typedef struct cred_t cred_t;
+
+static int
+auth_conv(int num_msg, const struct pam_message **msg,
+    struct pam_response **resp, void *appdata)
+{
+       int i;
+       cred_t *cred = appdata;
+       struct pam_response *reply = malloc(sizeof(*reply) * num_msg);
+
+       if (reply == NULL)
+               return PAM_BUF_ERR;
+
+       for (i = 0; i < num_msg; i++) {
+               switch (msg[i]->msg_style) {
+               case PAM_PROMPT_ECHO_ON:        /* assume want user name */
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       reply[i].resp = COPY_STRING(cred->uname);
+                       /* PAM frees resp. */
+                       break;
+               case PAM_PROMPT_ECHO_OFF:       /* assume want password */
+                   (void)strlcpy(passprompt, msg[i]->msg, SMALL_LEN);
+                   reply[i].resp_retcode = PAM_SUCCESS;
+                   reply[i].resp = COPY_STRING(cred->pass);
+                   /* PAM frees resp. */
+                   break;
+               case PAM_TEXT_INFO:
+               case PAM_ERROR_MSG:
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       reply[i].resp = NULL;
+                       break;
+               default:                        /* unknown message style */
+                       free(reply);
+                       return PAM_CONV_ERR;
+               }
+       }
+
+       *resp = reply;
+       return PAM_SUCCESS;
+}
+
+/*
+ * The PAM version as a side effect may put a new username in *name.
+ */
+static int
+check_user(char *name, const char *cred)
+{
+       pam_handle_t *pamh = NULL;
+       const void *item;
+       int rval;
+       int e;
+       cred_t auth_cred = { name, cred };
+       struct pam_conv conv = { &auth_conv, &auth_cred };
+
+       e = pam_start("telnetd", name, &conv, &pamh);
+       if (e != PAM_SUCCESS) {
+               syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
+               return 0;
+       }
+
+#if 0 /* Where can we find this value? */
+       e = pam_set_item(pamh, PAM_RHOST, remotehost);
+       if (e != PAM_SUCCESS) {
+               syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
+                       pam_strerror(pamh, e));
+               return 0;
+       }
+#endif
+
+       e = pam_authenticate(pamh, 0);
+       switch (e) {
+       case PAM_SUCCESS:
+               /*
+                * With PAM we support the concept of a "template"
+                * user.  The user enters a login name which is
+                * authenticated by PAM, usually via a remote service
+                * such as RADIUS or TACACS+.  If authentication
+                * succeeds, a different but related "template" name
+                * is used for setting the credentials, shell, and
+                * home directory.  The name the user enters need only
+                * exist on the remote authentication server, but the
+                * template name must be present in the local password
+                * database.
+                *
+                * This is supported by two various mechanisms in the
+                * individual modules.  However, from the application's
+                * point of view, the template user is always passed
+                * back as a changed value of the PAM_USER item.
+                */
+               if ((e = pam_get_item(pamh, PAM_USER, &item)) == 
+                   PAM_SUCCESS) {
+                       strlcpy(name, item, SMALL_LEN);
+               } else
+                       syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
+                       pam_strerror(pamh, e));
+#if 0  /* pam_securetty(8) should be used to enforce this */
+               if (isroot(name) && !rootterm(line))
+                       rval = 0;
+               else
+#endif
+                       rval = 1;
+               break;
+
+       case PAM_AUTH_ERR:
+       case PAM_USER_UNKNOWN:
+       case PAM_MAXTRIES:
+               rval = 0;
+       break;
+
+       default:
+               syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e));
+               rval = 0;
+               break;
+       }
+
+       if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
+               syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+               rval = 0;
+       }
+       return rval;
+}
+
+#endif /* !NOPAM */
+
+#endif /* ENCRYPTION */
+#endif /* SRA */
index a58fbaed3bdc19e4a3dbff1d9cebc4d230ce5ba9..7fa904bd2183dd42eda66b0cfe9985ec74c66427 100644 (file)
@@ -26,7 +26,7 @@ SUBDIR= asa \
        \
        sdiff sed seq shar shlock \
        shuffle  sort split stat su \
-       tail tee tic time touch \
+       tail tee telnet tic time touch \
        tput \
        tr true tsort tty ul uname unexpand unifdef \
        uniq units unvis unzip users \
diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile
new file mode 100644 (file)
index 0000000..cc93f95
--- /dev/null
@@ -0,0 +1,89 @@
+#      $NetBSD: Makefile,v 1.50 2012/08/10 12:10:27 joerg Exp $
+#
+# Copyright (c) 1990 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. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#      This product includes software developed by the University of
+#      California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#      from: @(#)Makefile      8.1 (Berkeley) 6/6/93
+#
+
+WARNS?=        4       # XXX -Wshadow etc.  fix asap
+CWARNFLAGS.clang+=     -Wno-tautological-compare -Wno-format-security
+
+.include <bsd.own.mk>
+
+USE_FORT?= yes # network client
+
+PROG=  telnet
+
+CPPFLAGS+=-DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK
+CPPFLAGS+=-I${.CURDIR}
+
+LDADD+= -lterminfo ${LIBTELNETDIR}/libtelnet.a
+DPADD+=        ${LIBTERMINFO} ${LIBTELNETDIR}/libtelnet.a
+
+SRCS=  authenc.c commands.c main.c network.c ring.c sys_bsd.c telnet.c \
+       terminal.c tn3270.c utilities.c
+
+CPPFLAGS+=-I${NETBSDSRCDIR}/lib
+.if !defined(__MINIX)
+CPPFLAGS+=-DIPSEC
+LDADD+=        -lipsec
+DPADD+=        ${LIBIPSEC}
+.endif # !defined(__MINIX)
+
+.if (${USE_INET6} != "no")
+CPPFLAGS+=-DINET6
+.endif
+
+LIBTELNETDIR!= cd ${.CURDIR}/../../lib/libtelnet; ${PRINTOBJDIR}
+
+.if (${USE_KERBEROS} != "no")
+CPPFLAGS+=-DKRB5 -DFORWARD
+LDADD+= -lkrb5 -lasn1 -lcom_err -lroken
+DPADD+=        ${LIBKRB5} ${LIBASN1} ${LIBCOM_ERR} ${LIBROKEN}
+.endif
+
+.if (${MKCRYPTO} != "no")
+CPPFLAGS+=-DAUTHENTICATION -DENCRYPTION
+LDADD+= -ldes -lcrypto -lcrypt
+DPADD+=        ${LIBDES} ${LIBCRYPTO} ${LIBCRYPT}
+.endif
+
+.if (${USE_PAM} != "no")
+LDADD+= -lpam ${PAM_STATIC_LDADD}
+DPADD+=        ${LIBPAM} ${PAM_STATIC_DPADD}
+.endif
+
+.for f in commands telnet terminal utilities
+COPTS.${f}.c+=  -Wno-pointer-sign
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/telnet/README b/usr.bin/telnet/README
new file mode 100644 (file)
index 0000000..9f66295
--- /dev/null
@@ -0,0 +1,763 @@
+
+This is a distribution of both client and server telnet.  These programs
+have been compiled on:
+                       telnet  telnetd
+       4.4 BSD-Lite      x       x
+       4.3 BSD Reno      X       X
+       UNICOS 9.1        X       X
+       UNICOS 9.0        X       X
+       UNICOS 8.0        X       X
+       BSDI 2.0          X       X
+       Solaris 2.4       x       x (no linemode in server)
+       SunOs 4.1.4       X       X (no linemode in server)
+       Ultrix 4.3        X       X (no linemode in server)
+       Ultrix 4.1        X       X (no linemode in server)
+
+In addition, previous versions have been compiled on the following
+machines, but were not available for testing this version.
+                       telnet  telnetd
+       Next1.0           X       X
+       UNICOS 8.3        X       X
+       UNICOS 7.C        X       X
+       UNICOS 7.0        X       X
+       SunOs 4.0.3c      X       X (no linemode in server)
+       4.3 BSD           X       X (no linemode in server)
+       DYNIX V3.0.12     X       X (no linemode in server)
+       Ultrix 3.1        X       X (no linemode in server)
+       Ultrix 4.0        X       X (no linemode in server)
+       SunOs 3.5         X       X (no linemode in server)
+       SunOs 4.1.3       X       X (no linemode in server)
+       Solaris 2.2       x       x (no linemode in server)
+       Solaris 2.3       x       x (no linemode in server)
+       BSDI 1.0          X       X
+       BSDI 1.1          X       X
+       DYNIX V3.0.17.9   X       X (no linemode in server)
+       HP-UX 8.0         x       x (no linemode in server)
+
+This code should work, but there are no guarantees.
+
+Oct 23, 1995
+
+This is a bugfix release.
+
+       The change in the previous release from using makeutx() to
+       pututxline() caused problems on SunOS/Solaris.  It has been
+       changed back to using makeutx().  Symptoms include users
+       getting error messages when logging in about not being able
+       to open the tty.
+
+       Using memmove() instead of memcpy() caused problems under
+       SunOS 4.x, since it doesn't have memmove().  Config.generic
+       has been modified to include mem.o for SunOS 4.x.
+
+       Some new code was added to telnetd to do some environment
+       variable cleanup before execing login.  Thanks to Sam Hartman
+       at MIT for pointing this out.
+
+       A couple of other minor bugfixes.
+
+May 30, 1995
+
+This release represents what is on the 4.4BSD-Lite2 release, which
+should be the final BSD release.  I will continue to support of
+telnet, The code (without encryption) is available via anonymous ftp
+from ftp.cray.com, in src/telnet/telnet.YY.MM.DD.NE.tar.Z, where
+YY.MM.DD is replaced with the year, month and day of the release.
+If you can't find it at one of these places, at some point in the
+near future information about the latest releases should be available
+from ftp.borman.com.
+
+In addition, the version with the encryption code is available via
+ftp from net-dist.mit.edu, in the directory /pub/telnet.  There
+is a README file there that gives further information on how
+to get the distribution.
+
+Questions, comments, bug reports and bug fixes can be sent to
+one of these addresses:
+               dab@borman.com
+               dab@cray.com
+               dab@bsdi.com
+
+This release is mainly bug fixes and code cleanup.
+
+       Replace all calls to bcopy()/bzero() with calls to
+       memmove()/memset() and all calls to index()/rindex()
+       with calls to strchr()/strrchr().
+
+       Add some missing diagnostics for option tracing
+       to telnetd.
+
+       Add support for BSDI 2.0 and Solaris 2.4.
+
+       Add support for UNICOS 8.0
+
+       Get rid of expanded tabs and trailing white spaces.
+
+       From Paul Vixie:
+               Fix for telnet going into an endless spin
+               when the session dies abnormally.
+
+       From Jef Poskanzer:
+               Changes to allow telnet to compile
+               under SunOS 3.5.
+
+       From Philip Guenther:
+               makeutx() doesn't expand utmpx,
+               use pututxline() instead.
+
+       From Chris Torek:
+               Add a sleep(1) before execing login
+               to avoid race condition that can eat
+               up the login prompt.
+               Use terminal speed directly if it is
+               not an encoded value.
+
+       From Steve Parker:
+               Fix to realloc() call.  Fix for execing
+               login on solaris with no user name.
+
+January 19, 1994
+
+This is a list of some of the changes since the last tar release
+of telnet/telnetd.  There are probably other changes that aren't
+listed here, but this should hit a lot of the main ones.
+
+   General:
+       Changed #define for AUTHENTICATE to AUTHENTICATION
+       Changed #define for ENCRYPT to ENCRYPTION
+       Changed #define for DES_ENCRYPT to DES_ENCRYPTION
+
+       Added support for SPX authentication: -DSPX
+
+       Added support for Kerberos Version 5 authentication: -DKRB5
+
+       Added support for ANSI C function prototypes
+
+       Added support for the NEW-ENVIRON option (RFC-1572)
+       including support for USERVAR.
+
+       Made support for the old Environment Option (RFC-1408)
+       conditional on -DOLD_ENVIRON
+
+       Added #define ENV_HACK - support for RFC 1571
+
+       The encryption code is removed from the public distributions.
+       Domestic 4.4 BSD distributions contain the encryption code.
+
+       ENV_HACK: Code to deal with systems that only implement
+               the old ENVIRON option, and have reversed definitions
+               of ENV_VAR and ENV_VAL.  Also fixes ENV processing in
+               client to handle things besides just the default set...
+
+       NO_BSD_SETJMP: UNICOS configuration for
+               UNICOS 6.1/6.0/5.1/5.0 systems.
+
+       STREAMSPTY: Use /dev/ptmx to get a clean pty.  This
+               is for SVr4 derivatives (Like Solaris)
+
+       UTMPX: For systems that have /etc/utmpx. This is for
+               SVr4 derivatives (Like Solaris)
+
+       Definitions for BSDI 1.0
+
+       Definitions for 4.3 Reno and 4.4 BSD.
+
+       Definitions for UNICOS 8.0 and UNICOS 7.C
+
+       Definitions for Solaris 2.0
+
+       Definitions for HP-UX 8.0
+
+       Latest Copyright notices from Berkeley.
+
+       FLOW-CONTROL: support for RFC-XXXx
+
+
+   Client Specific:
+
+       Fix the "send" command to not send garbage...
+
+       Fix status message for "skiprc"
+
+       Make sure to send NAWS after telnet has been suspended
+       or an external command has been run, if the window size
+       has changed.
+
+       sysV88 support.
+
+   Server Specific:
+
+       Support flowcontrol option in non-linemode servers.
+
+       -k Server supports Kludge Linemode, but will default to
+          either single character mode or real Linemode support.
+          The user will have to explicitly ask to switch into
+          kludge linemode. ("stty extproc", or escape back to
+          to telnet and say "mode line".)
+
+       -u Specify the length of the hostname field in the utmp
+          file.  Hostname longer than this length will be put
+          into the utmp file in dotted decimal notation, rather
+          than putting in a truncated hostname.
+       
+       -U Registered hosts only.  If a reverse hostname lookup
+          fails, the connection will be refused.
+
+       -f/-F
+          Allows forwarding of credentials for KRB5.
+
+February 22, 1991:
+
+    Features:
+
+       This version of telnet/telnetd has support for both
+       the AUTHENTICATION and ENCRYPTION options.  The
+       AUTHENTICATION option is fairly well defined, and
+       an option number has been assigned to it.  The
+       ENCRYPTION option is still in a state of flux; an
+       option number has been assigned to, but it is still
+       subject to change.  The code is provided in this release
+       for experimental and testing purposes.
+
+       The telnet "send" command can now be used to send
+       do/dont/will/wont commands, with any telnet option
+       name.  The rules for when do/dont/will/wont are sent
+       are still followed, so just because the user requests
+       that one of these be sent doesn't mean that it will
+       be sent...
+
+       The telnet "getstatus" command no longer requires
+       that option printing be enabled to see the response
+       to the "DO STATUS" command.
+
+       A -n flag has been added to telnetd to disable
+       keepalives.
+
+       A new telnet command, "auth" has been added (if
+       AUTHENTICATE is defined).  It has four sub-commands,
+       "status", "disable", "enable" and "help".
+
+       A new telnet command, "encrypt" has been added (if
+       ENCRYPT is defined).  It has many sub-commands:
+       "enable", "type", "start", "stop", "input",
+       "-input", "output", "-output", "status", and "help".
+
+       The LOGOUT option is now supported by both telnet
+       and telnetd, a new command, "logout", was added
+       to support this.
+
+       Several new toggle options were added:
+           "autoencrypt", "autodecrypt", "autologin", "authdebug",
+           "encdebug", "skiprc", "verbose_encrypt"
+
+       An "rlogin" interface has been added.  If the program
+       is named "rlogin", or the "-r" flag is given, then
+       an rlogin type of interface will be used.
+               ~.      Terminates the session
+               ~<susp> Suspend the session
+               ~^]     Escape to telnet command mode
+               ~~      Pass through the ~.
+           BUG: If you type the rlogin escape character
+                in the middle of a line while in rlogin
+                mode, you cannot erase it or any characters
+                before it.  Hopefully this can be fixed
+                in a future release...
+
+    General changes:
+
+       A "libtelnet.a" has now been created.  This library
+       contains code that is common to both telnet and
+       telnetd.  This is also where library routines that
+       are needed, but are not in the standard C library,
+       are placed.
+
+       The makefiles have been re-done.  All of the site
+       specific configuration information has now been put
+       into a single "Config.generic" file, in the top level
+       directory.  Changing this one file will take care of
+       all three subdirectories.  Also, to add a new/local
+       definition, a "Config.local" file may be created
+       at the top level; if that file exists, the subdirectories
+       will use that file instead of "Config.generic".
+
+       Many 1-2 line functions in commands.c have been
+       removed, and just inserted in-line, or replaced
+       with a macro.
+
+    Bug Fixes:
+
+       The non-termio code in both telnet and telnetd was
+       setting/clearing CTLECH in the sg_flags word.  This
+       was incorrect, and has been changed to set/clear the
+       LCTLECH bit in the local mode word.
+
+       The SRCRT #define has been removed.  If IP_OPTIONS
+       and IPPROTO_IP are defined on the system, then the
+       source route code is automatically enabled.
+
+       The NO_GETTYTAB #define has been removed; there
+       is a compatibility routine that can be built into
+       libtelnet to achieve the same results.
+
+       The server, telnetd, has been switched to use getopt()
+       for parsing the argument list.
+
+       The code for getting the input/output speeds via
+       cfgetispeed()/cfgetospeed() was still not quite
+       right in telnet.  Posix says if the ispeed is 0,
+       then it is really equal to the ospeed.
+
+       The suboption processing code in telnet now has
+       explicit checks to make sure that we received
+       the entire suboption (telnetd was already doing this).
+
+       The telnet code for processing the terminal type
+       could cause a core dump if an existing connection
+       was closed, and a new connection opened without
+       exiting telnet.
+
+       Telnetd was doing a TCSADRAIN when setting the new
+       terminal settings;  This is not good, because it means
+       that the tcsetattr() will hang waiting for output to
+       drain, and telnetd is the only one that will drain
+       the output...  The fix is to use TCSANOW which does
+       not wait.
+
+       Telnetd was improperly setting/clearing the ISTRIP
+       flag in the c_lflag field, it should be using the
+       c_iflag field. 
+
+       When the child process of telnetd was opening the
+       slave side of the pty, it was re-setting the EXTPROC
+       bit too early, and some of the other initialization
+       code was wiping it out.  This would cause telnetd
+       to go out of linemode and into single character mode.
+
+       One instance of leaving linemode in telnetd forgot
+       to send a WILL ECHO to the client, the net result
+       would be that the user would see double character
+       echo.
+
+       If the MODE was being changed several times very
+       quickly, telnetd could get out of sync with the
+       state changes and the returning acks; and wind up
+       being left in the wrong state.
+
+September 14, 1990:
+
+       Switch the client to use getopt() for parsing the
+       argument list.  The 4.3Reno getopt.c is included for
+       systems that don't have getopt().
+
+       Use the posix _POSIX_VDISABLE value for what value
+       to use when disabling special characters.  If this
+       is undefined, it defaults to 0x3ff.
+
+       For non-termio systems, TIOCSETP was being used to
+       change the state of the terminal.  This causes the
+       input queue to be flushed, which we don't want.  This
+       is now changed to TIOCSETN.
+
+       Take out the "#ifdef notdef" around the code in the
+       server that generates a "sync" when the pty output
+       is flushed.  The potential problem is that some older
+       telnet clients may go into an infinite loop when they
+       receive a "sync", if so, the server can be compiled
+       with "NO_URGENT" defined.
+
+       Fix the client where it was setting/clearing the OPOST
+       bit in the c_lflag field, not the c_oflag field.
+
+       Fix the client where it was setting/clearing the ISTRIP
+       bit in the c_lflag field, not the c_iflag field.  (On
+       4.3Reno, this is the ECHOPRT bit in the c_lflag field.)
+       The client also had its interpretation of WILL BINARY
+       and DO BINARY reversed.
+
+       Fix a bug in client that would cause a core dump when
+       attempting to remove the last environment variable.
+
+       In the client, there were a few places were switch()
+       was being passed a character, and if it was a negative
+       value, it could get sign extended, and not match
+       the 8 bit case statements.  The fix is to and the
+       switch value with 0xff.
+
+       Add a couple more printoption() calls in the client, I
+       don't think there are any more places were a telnet
+       command can be received and not printed out when
+       "options" is on.
+
+       A new flag has been added to the client, "-a".  Currently,
+       this just causes the USER name to be sent across, in
+       the future this may be used to signify that automatic
+       authentication is requested.
+
+       The USER variable is now only sent by the client if
+       the "-a" or "-l user" options are explicitly used, or
+       if the user explicitly asks for the "USER" environment
+       variable to be exported.  In the server, if it receives
+       the "USER" environment variable, it won't print out the
+       banner message, so that only "Password:" will be printed.
+       This makes the semantics more like rlogin, and should be
+       more familiar to the user.  (People are not used to
+       getting a banner message, and then getting just a
+       "Password:" prompt.)
+
+       Re-vamp the code for starting up the child login
+       process.  The code was getting ugly, and it was
+       hard to tell what was really going on.  What we
+       do now is after the fork(), in the child:
+               1) make sure we have no controlling tty
+               2) open and initialize the tty
+               3) do a setsid()/setpgrp()
+               4) makes the tty our controlling tty.
+       On some systems, #2 makes the tty our controlling
+       tty, and #4 is a no-op.  The parent process does
+       a gets rid of any controlling tty after the child
+       is fork()ed.
+
+       Use the strdup() library routine in telnet, instead
+       of the local savestr() routine.  If you don't have
+       strdup(), you need to define NO_STRDUP.
+
+       Add support for ^T (SIGINFO/VSTATUS), found in the
+       4.3Reno distribution.  This maps to the AYT character.
+       You need a 4-line bugfix in the kernel to get this
+       to work properly:
+
+       > *** tty_pty.c.ORG     Tue Sep 11 09:41:53 1990
+       > --- tty_pty.c Tue Sep 11 17:48:03 1990
+       > ***************
+       > *** 609,613 ****
+       >                       if ((tp->t_lflag&NOFLSH) == 0)
+       >                               ttyflush(tp, FREAD|FWRITE);
+       > !                     pgsignal(tp->t_pgrp, *(unsigned int *)data);
+       >                       return(0);
+       >               }
+       > --- 609,616 ----
+       >                       if ((tp->t_lflag&NOFLSH) == 0)
+       >                               ttyflush(tp, FREAD|FWRITE);
+       > !                     pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
+       > !                     if ((*(unsigned int *)data == SIGINFO) &&
+       > !                         ((tp->t_lflag&NOKERNINFO) == 0))
+       > !                             ttyinfo(tp);
+       >                       return(0);
+       >               }
+
+       The client is now smarter when setting the telnet escape
+       character; it only sets it to one of VEOL and VEOL2 if
+       one of them is undefined, and the other one is not already
+       defined to the telnet escape character.
+
+       Handle TERMIOS systems that have separate input and output
+       line speed settings embedded in the flags.
+
+       Many other minor bug fixes.
+
+June 20, 1990:
+       Re-organize makefiles and source tree.  The telnet/Source
+       directory is now gone, and all the source that was in
+       telnet/Source is now just in the telnet directory.
+
+       Separate makefile for each system are now gone.  There
+       are two makefiles, Makefile and Makefile.generic.
+       The "Makefile" has the definitions for the various
+       system, and "Makefile.generic" does all the work.
+       There is a variable called "WHAT" that is used to
+       specify what to make.  For example, in the telnet
+       directory, you might say:
+               make 4.4bsd WHAT=clean
+       to clean out the directory.
+
+       Add support for the ENVIRON and XDISPLOC options.
+       In order for the server to work, login has to have
+       the "-p" option to preserve environment variables.
+
+       Add the SOFT_TAB and LIT_ECHO modes in the LINEMODE support.
+
+       Add the "-l user" option to command line and open command
+       (This is passed through the ENVIRON option).
+
+       Add the "-e" command line option, for setting the escape
+       character.
+
+       Add the "-D", diagnostic, option to the server.  This allows
+       the server to print out debug information, which is very
+       useful when trying to debug a telnet that doesn't have any
+       debugging ability.
+
+       Turn off the literal next character when not in LINEMODE.
+
+       Don't recognize ^Y locally, just pass it through.
+
+       Make minor modifications for Sun4.0 and Sun4.1
+
+       Add support for both FORW1 and FORW2 characters.  The
+       telnet escape character is set to whichever of the
+       two is not being used.  If both are in use, the escape
+       character is not set, so when in linemode the user will
+       have to follow the escape character with a <CR> or <EOF)
+       to get it passed through.
+
+       Commands can now be put in single and double quotes, and
+       a backslash is now an escape character.  This is needed
+       for allowing arbitrary strings to be assigned to environment
+       variables.
+
+       Switch telnetd to use macros like telnet for keeping
+       track of the state of all the options.
+
+       Fix telnetd's processing of options so that we always do
+       the right processing of the LINEMODE option, regardless
+       of who initiates the request to turn it on.  Also, make
+       sure that if the other side went "WILL ECHO" in response
+       to our "DO ECHO", that we send a "DONT ECHO" to get the
+       option turned back off!
+
+       Fix the TERMIOS setting of the terminal speed to handle both
+       BSD's separate fields, and the SYSV method of CBAUD bits.
+
+       Change how we deal with the other side refusing to enable
+       an option.  The sequence used to be: send DO option; receive
+       WONT option; send DONT option.  Now, the sequence is: send
+       DO option; receive WONT option.  Both should be valid
+       according to the spec, but there has been at least one
+       client implementation of telnet identified that can get
+       really confused by this.  (The exact sequence, from a trace
+       on the server side, is (numbers are number of responses that
+       we expect to get after that line...):
+
+               send WILL ECHO  1 (initial request)
+               send WONT ECHO  2 (server is changing state)
+               recv DO ECHO    1 (first reply, ok.  expect DONT ECHO next)
+               send WILL ECHO  2 (server changes state again)
+               recv DONT ECHO  1 (second reply, ok.  expect DO ECHO next)
+               recv DONT ECHO  0 (third reply, wrong answer. got DONT!!!)
+       ***     send WONT ECHO    (send WONT to acknowledge the DONT)
+               send WILL ECHO  1 (ask again to enable option)
+               recv DO ECHO    0
+
+               recv DONT ECHO  0
+               send WONT ECHO  1
+               recv DONT ECHO  0
+               recv DO ECHO    1
+               send WILL ECHO  0
+               (and the last 5 lines loop forever)
+
+       The line with the "***" is last of the WILL/DONT/WONT sequence.
+       The change to the server to not generate that makes this same
+       example become:
+
+               send will ECHO  1
+               send wont ECHO  2
+               recv do ECHO    1
+               send will ECHO  2
+               recv dont ECHO  1
+               recv dont ECHO  0
+               recv do ECHO    1
+               send will ECHO  0
+
+       There is other option negotiation going on, and not sending
+       the third part changes some of the timings, but this specific
+       example no longer gets stuck in a loop.  The "telnet.state"
+       file has been modified to reflect this change to the algorithm.
+
+       A bunch of miscellaneous bug fixes and changes to make
+       lint happier.
+
+       This version of telnet also has some KERBEROS stuff in
+       it. This has not been tested, it uses an un-authorized
+       telnet option number, and uses an out-of-date version
+       of the (still being defined) AUTHENTICATION option.
+       There is no support for this code, do not enable it.
+
+
+March 1, 1990:
+CHANGES/BUGFIXES SINCE LAST RELEASE:
+       Some support for IP TOS has been added.  Requires that the
+       kernel support the IP_TOS socket option (currently this
+       is only in UNICOS 6.0).
+
+       Both telnet and telnetd now use the cc_t typedef.  typedefs are
+       included for systems that don't have it (in termios.h).
+
+       SLC_SUSP was not supported properly before.  It is now.
+
+       IAC EOF was not translated  properly in telnetd for SYSV_TERMIO
+       when not in linemode.  It now saves a copy of the VEOF character,
+       so that when ICANON is turned off and we can't trust it anymore
+       (because it is now the VMIN character) we use the saved value.
+
+       There were two missing "break" commands in the linemode
+       processing code in telnetd.
+
+       Telnetd wasn't setting the kernel window size information
+       properly.  It was using the rows for both rows and columns...
+
+Questions/comments go to
+               David Borman
+               Cray Research, Inc.
+               655F Lone Oak Drive
+               Eagan, MN 55123
+               dab@cray.com.
+
+README:        You are reading it.
+
+Config.generic:
+       This file contains all the OS specific definitions.  It
+       has pre-definitions for many common system types, and is
+       in standard makefile format.  See the comments at the top
+       of the file for more information.
+
+Config.local:
+       This is not part of the distribution, but if this file exists,
+       it is used instead of "Config.generic".  This allows site
+       specific configuration without having to modify the distributed
+       "Config.generic" file.
+
+kern.diff:
+       This file contains the diffs for the changes needed for the
+       kernel to support LINEMODE is the server.  These changes are
+       for a 4.3BSD system.  You may need to make some changes for
+       your particular system.
+
+       There is a new bit in the terminal state word, TS_EXTPROC.
+       When this bit is set, several aspects of the terminal driver
+       are disabled.  Input line editing, character echo, and
+       mapping of signals are all disabled.  This allows the telnetd
+       to turn of these functions when in linemode, but still keep
+       track of what state the user wants the terminal to be in.
+
+       New ioctl()s:
+
+               TIOCEXT         Turn on/off the TS_EXTPROC bit
+               TIOCGSTATE      Get t_state of tty to look at TS_EXTPROC bit
+               TIOCSIG         Generate a signal to processes in the
+                               current process group of the pty.
+
+       There is a new mode for packet driver, the TIOCPKT_IOCTL bit.
+       When packet mode is turned on in the pty, and the TS_EXTPROC
+       bit is set, then whenever the state of the pty is changed, the
+       next read on the master side of the pty will have the TIOCPKT_IOCTL
+       bit set, and the data will contain the following:
+               struct xx {
+                       struct sgttyb a;
+                       struct tchars b;
+                       struct ltchars c;
+                       int t_state;
+                       int t_flags;
+               }
+       This allows the process on the server side of the pty to know
+       when the state of the terminal has changed, and what the new
+       state is.
+
+       However, if you define USE_TERMIO or SYSV_TERMIO, the code will
+       expect that the structure returned in the TIOCPKT_IOCTL is
+       the termio/termios structure.
+
+stty.diff:
+       This file contains the changes needed for the stty(1) program
+       to report on the current status of the TS_EXTPROC bit.  It also
+       allows the user to turn on/off the TS_EXTPROC bit.  This is useful
+       because it allows the user to say "stty -extproc", and the
+       LINEMODE option will be automatically disabled, and saying "stty
+       extproc" will re-enable the LINEMODE option.
+
+telnet.state:
+       Both the client and server have code in them to deal
+       with option negotiation loops.  The algorithm that is
+       used is described in this file.
+
+telnet:
+       This directory contains the client code.  No kernel changes are
+       needed to use this code.
+
+telnetd:
+       This directory contains the server code.  If LINEMODE or KLUDGELINEMODE
+       are defined, then the kernel modifications listed above are needed.
+
+libtelnet:
+       This directory contains code that is common to both the client
+       and the server.
+
+arpa:
+       This directory has a new <arpa/telnet.h>
+
+libtelnet/Makefile.4.4:
+telnet/Makefile.4.4:
+telnetd/Makefile.4.4:
+       These are the makefiles that can be used on a 4.3Reno
+       system when this software is installed in /usr/src/lib/libtelnet,
+       /usr/src/libexec/telnetd, and /usr/src/usr.bin/telnet.
+
+
+The following TELNET options are supported:
+       
+       LINEMODE:
+               The LINEMODE option is supported as per RFC1116.  The
+               FORWARDMASK option is not currently supported.
+
+       BINARY: The client has the ability to turn on/off the BINARY
+               option in each direction.  Turning on BINARY from
+               server to client causes the LITOUT bit to get set in
+               the terminal driver on both ends,  turning on BINARY
+               from the client to the server causes the PASS8 bit
+               to get set in the terminal driver on both ends.
+
+       TERMINAL-TYPE:
+               This is supported as per RFC1091.  On the server side,
+               when a terminal type is received, termcap/terminfo
+               is consulted to determine if it is a known terminal
+               type.  It keeps requesting terminal types until it
+               gets one that it recognizes, or hits the end of the
+               list.  The server side looks up the entry in the
+               termcap/terminfo data base, and generates a list of
+               names which it then passes one at a time to each
+               request for a terminal type, duplicating the last
+               entry in the list before cycling back to the beginning.
+
+       NAWS:   The Negotiate about Window Size, as per RFC 1073.
+
+       TERMINAL-SPEED:
+               Implemented as per RFC 1079
+
+       TOGGLE-FLOW-CONTROL:
+               Implemented as per RFC 1080
+
+       TIMING-MARK:
+               As per RFC 860
+
+       SGA:    As per RFC 858
+
+       ECHO:   As per RFC 857
+
+       LOGOUT: As per RFC 727
+
+       STATUS:
+               The server will send its current status upon
+               request.  It does not ask for the clients status.
+               The client will request the servers current status
+               from the "send getstatus" command.
+
+       ENVIRON:
+               This option is currently being defined by the IETF
+               Telnet Working Group, and an RFC has not yet been
+               issued, but should be in the near future...
+
+       X-DISPLAY-LOCATION:
+               This functionality can be done through the ENVIRON
+               option, it is added here for completeness.
+
+       AUTHENTICATION:
+               This option is currently being defined by the IETF
+               Telnet Working Group, and an RFC has not yet been
+               issued.  The basic framework is pretty much decided,
+               but the definitions for the specific authentication
+               schemes is still in a state of flux.
+
+       ENCRYPTION:
+               This option is currently being defined by the IETF
+               Telnet Working Group, and an RFC has not yet been
+               issued.  The draft RFC is still in a state of flux,
+               so this code may change in the future.
diff --git a/usr.bin/telnet/authenc.c b/usr.bin/telnet/authenc.c
new file mode 100644 (file)
index 0000000..8e7574d
--- /dev/null
@@ -0,0 +1,107 @@
+/*     $NetBSD: authenc.c,v 1.13 2012/01/09 16:08:55 christos 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.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: authenc.c,v 1.13 2012/01/09 16:08:55 christos Exp $");
+#endif
+#endif /* not lint */
+
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+#include <unistd.h>
+#include <sys/types.h>
+#include <arpa/telnet.h>
+#include <libtelnet/encrypt.h>
+#include <libtelnet/misc.h>
+
+#include "general.h"
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+#include "types.h"
+
+int
+telnet_net_write(unsigned char *str, int len)
+{
+       if (NETROOM() > len) {
+               ring_supply_data(&netoring, str, len);
+               if (str[0] == IAC && str[1] == SE)
+                       printsub('>', &str[2], len-2);
+               return(len);
+       }
+       return(0);
+}
+
+void
+net_encrypt(void)
+{
+#ifdef ENCRYPTION
+       if (encrypt_output)
+               ring_encrypt(&netoring, encrypt_output);
+       else
+               ring_clearto(&netoring);
+#endif /* ENCRYPTION */
+}
+
+int
+telnet_spin(void)
+{
+       return(-1);
+}
+
+char *
+telnet_getenv(char *val)
+{
+       return((char *)env_getvalue((unsigned char *)val));
+}
+
+char *
+telnet_gets(char *prmpt, char *result, int length, int echo)
+{
+       extern int globalmode;
+       int om = globalmode;
+       char *res;
+
+       TerminalNewMode(-1);
+       if (echo) {
+               printf("%s", prmpt);
+               res = fgets(result, length, stdin);
+       } else if ((res = getpass(prmpt)) != NULL) {
+               strlcpy(result, res, length);
+               res = result;
+       }
+       TerminalNewMode(om);
+       return(res);
+}
+#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c
new file mode 100644 (file)
index 0000000..b244b75
--- /dev/null
@@ -0,0 +1,2907 @@
+/*     $NetBSD: commands.c,v 1.68 2012/01/09 16:08:55 christos 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) 1988, 1990, 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[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: commands.c,v 1.68 2012/01/09 16:08:55 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <arpa/telnet.h>
+
+#include "general.h"
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+#include "types.h"
+#include <libtelnet/misc.h>
+#ifdef AUTHENTICATION
+#include <libtelnet/auth.h>
+#endif
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+
+#if    defined(IPPROTO_IP) && defined(IP_TOS)
+int tos = -1;
+#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
+
+char   *hostname;
+static char _hostname[MAXHOSTNAMELEN];
+
+typedef struct {
+       const char      *name;  /* command name */
+       const char      *help;  /* help string (NULL for no help) */
+       int     (*handler)      /* routine which executes command */
+                       (int, char *[]);
+       int     needconnect;    /* Do we need to be connected to execute? */
+} Command;
+
+static char line[256];
+static char saveline[256];
+static int margc;
+static char *margv[20];
+
+static void makeargv(void);
+static int special(char *);
+static const char *control(cc_t);
+static int sendcmd(int, char **);
+static int send_esc(char *);
+static int send_docmd(char *);
+static int send_dontcmd(char *);
+static int send_willcmd(char *);
+static int send_wontcmd(char *);
+static int send_help(char *);
+static int lclchars(int);
+static int togdebug(int);
+static int togcrlf(int);
+static int togbinary(int);
+static int togrbinary(int);
+static int togxbinary(int);
+static int togglehelp(int);
+static void settogglehelp(int);
+static int toggle(int, char *[]);
+static struct setlist *getset(char *);
+static int setcmd(int, char *[]);
+static int unsetcmd(int, char *[]);
+static int dokludgemode(int);
+static int dolinemode(int);
+static int docharmode(int);
+static int dolmmode(int, int );
+static int modecmd(int, char *[]);
+static int display(int, char *[]);
+static int setescape(int, char *[]);
+static int togcrmod(int, char *[]);
+static int bye(int, char *[]);
+static void slc_help(int);
+static struct slclist *getslc(char *);
+static int slccmd(int, char *[]);
+static struct env_lst *env_help(const unsigned char *, unsigned char *);
+static struct envlist *getenvcmd(char *);
+#ifdef AUTHENTICATION
+static int auth_help(char *);
+#endif
+#ifdef TN3270
+static void filestuff(int);
+#endif
+static int status(int, char *[]);
+static const char *sockaddr_ntop (struct sockaddr *);
+typedef int (*intrtn_t)(int, char **);
+static int call(intrtn_t, ...);
+static Command *getcmd(char *);
+static int help(int, char *[]);
+
+static void
+makeargv(void)
+{
+    char *cp, *cp2, c;
+    char **argp = margv;
+    static char bang[] = "!";
+
+    margc = 0;
+    cp = line;
+    if (*cp == '!') {          /* Special case shell escape */
+       strlcpy(saveline, line, sizeof(saveline)); /* save for shell command */
+       *argp++ = bang;         /* No room in string to get this */
+       margc++;
+       cp++;
+    }
+    while ((c = *cp) != '\0') {
+       int inquote = 0;
+       while (isspace((unsigned char)c))
+           c = *++cp;
+       if (c == '\0')
+           break;
+       *argp++ = cp;
+       margc += 1;
+       for (cp2 = cp; c != '\0'; c = *++cp) {
+           if (inquote) {
+               if (c == inquote) {
+                   inquote = 0;
+                   continue;
+               }
+           } else {
+               if (c == '\\') {
+                   if ((c = *++cp) == '\0')
+                       break;
+               } else if (c == '"') {
+                   inquote = '"';
+                   continue;
+               } else if (c == '\'') {
+                   inquote = '\'';
+                   continue;
+               } else if (isspace((unsigned char)c))
+                   break;
+           }
+           *cp2++ = c;
+       }
+       *cp2 = '\0';
+       if (c == '\0')
+           break;
+       cp++;
+    }
+    *argp++ = 0;
+}
+
+/*
+ * Make a character string into a number.
+ *
+ * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
+ */
+
+static int
+special(char *s)
+{
+       char c;
+       char b;
+
+       switch (*s) {
+       case '^':
+               b = *++s;
+               if (b == '?') {
+                   c = b | 0x40;               /* DEL */
+               } else {
+                   c = b & 0x1f;
+               }
+               break;
+       default:
+               c = *s;
+               break;
+       }
+       return c;
+}
+
+/*
+ * Construct a control character sequence
+ * for a special character.
+ */
+static const char *
+control(cc_t c)
+{
+       static char buf[5];
+       /*
+        * The only way I could get the Sun 3.5 compiler
+        * to shut up about
+        *      if ((unsigned int)c >= 0x80)
+        * was to assign "c" to an unsigned int variable...
+        * Arggg....
+        */
+       unsigned int uic = (unsigned int)c;
+
+       if (uic == 0x7f)
+               return ("^?");
+       if (c == (cc_t)_POSIX_VDISABLE) {
+               return "off";
+       }
+       if (uic >= 0x80) {
+               buf[0] = '\\';
+               buf[1] = ((c>>6)&07) + '0';
+               buf[2] = ((c>>3)&07) + '0';
+               buf[3] = (c&07) + '0';
+               buf[4] = 0;
+       } else if (uic >= 0x20) {
+               buf[0] = c;
+               buf[1] = 0;
+       } else {
+               buf[0] = '^';
+               buf[1] = '@'+c;
+               buf[2] = 0;
+       }
+       return (buf);
+}
+
+
+
+/*
+ *     The following are data structures and routines for
+ *     the "send" command.
+ *
+ */
+
+struct sendlist {
+    const char *name;          /* How user refers to it (case independent) */
+    const char *help;          /* Help information (0 ==> no help) */
+    int                needconnect;    /* Need to be connected */
+    int                narg;           /* Number of arguments */
+    int                (*handler)      /* Routine to perform (for special ops) */
+                       (char *);
+    int                nbyte;          /* Number of bytes to send this command */
+    int                what;           /* Character to be sent (<0 ==> special) */
+};
+\f
+
+static struct sendlist Sendlist[] = {
+    { "ao",    "Send Telnet Abort output",             1, 0, 0, 2, AO },
+    { "ayt",   "Send Telnet 'Are You There'",          1, 0, 0, 2, AYT },
+    { "brk",   "Send Telnet Break",                    1, 0, 0, 2, BREAK },
+    { "break", 0,                                      1, 0, 0, 2, BREAK },
+    { "ec",    "Send Telnet Erase Character",          1, 0, 0, 2, EC },
+    { "el",    "Send Telnet Erase Line",               1, 0, 0, 2, EL },
+    { "escape",        "Send current escape character",        1, 0, send_esc, 1, 0 },
+    { "ga",    "Send Telnet 'Go Ahead' sequence",      1, 0, 0, 2, GA },
+    { "ip",    "Send Telnet Interrupt Process",        1, 0, 0, 2, IP },
+    { "intp",  0,                                      1, 0, 0, 2, IP },
+    { "interrupt", 0,                                  1, 0, 0, 2, IP },
+    { "intr",  0,                                      1, 0, 0, 2, IP },
+    { "nop",   "Send Telnet 'No operation'",           1, 0, 0, 2, NOP },
+    { "eor",   "Send Telnet 'End of Record'",          1, 0, 0, 2, EOR },
+    { "abort", "Send Telnet 'Abort Process'",          1, 0, 0, 2, ABORT },
+    { "susp",  "Send Telnet 'Suspend Process'",        1, 0, 0, 2, SUSP },
+    { "eof",   "Send Telnet End of File Character",    1, 0, 0, 2, xEOF },
+    { "synch", "Perform Telnet 'Synch operation'",     1, 0, dosynch, 2, 0 },
+    { "getstatus", "Send request for STATUS",          1, 0, get_status, 6, 0 },
+    { "?",     "Display send options",                 0, 0, send_help, 0, 0 },
+    { "help",  0,                                      0, 0, send_help, 0, 0 },
+    { "do",    0,                                      0, 1, send_docmd, 3, 0 },
+    { "dont",  0,                                      0, 1, send_dontcmd, 3, 0 },
+    { "will",  0,                                      0, 1, send_willcmd, 3, 0 },
+    { "wont",  0,                                      0, 1, send_wontcmd, 3, 0 },
+    { .name = 0 }
+};
+
+#define        GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
+                               sizeof(struct sendlist)))
+
+static int
+sendcmd(int  argc, char **argv)
+{
+    int count;         /* how many bytes we are going to need to send */
+    int i;
+    struct sendlist *s;        /* pointer to current command */
+    int success = 0;
+    int needconnect = 0;
+
+    if (argc < 2) {
+       printf("need at least one argument for 'send' command\n");
+       printf("'send ?' for help\n");
+       return 0;
+    }
+    /*
+     * First, validate all the send arguments.
+     * In addition, we see how much space we are going to need, and
+     * whether or not we will be doing a "SYNCH" operation (which
+     * flushes the network queue).
+     */
+    count = 0;
+    for (i = 1; i < argc; i++) {
+       s = GETSEND(argv[i]);
+       if (s == 0) {
+           printf("Unknown send argument '%s'\n'send ?' for help.\n",
+                       argv[i]);
+           return 0;
+       } else if (Ambiguous(s)) {
+           printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
+                       argv[i]);
+           return 0;
+       }
+       if (i + s->narg >= argc) {
+           fprintf(stderr,
+           "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\n",
+               s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
+           return 0;
+       }
+       count += s->nbyte;
+       if (s->handler == send_help) {
+           send_help(NULL);
+           return 0;
+       }
+
+       i += s->narg;
+       needconnect += s->needconnect;
+    }
+    if (!connected && needconnect) {
+       printf("?Need to be connected first.\n");
+       printf("'send ?' for help\n");
+       return 0;
+    }
+    /* Now, do we have enough room? */
+    if (NETROOM() < count) {
+       printf("There is not enough room in the buffer TO the network\n");
+       printf("to process your request.  Nothing will be done.\n");
+       printf("('send synch' will throw away most data in the network\n");
+       printf("buffer, if this might help.)\n");
+       return 0;
+    }
+    /* OK, they are all OK, now go through again and actually send */
+    count = 0;
+    for (i = 1; i < argc; i++) {
+       if ((s = GETSEND(argv[i])) == 0) {
+           fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
+           (void) quit(0, NULL);
+           /*NOTREACHED*/
+       }
+       if (s->handler) {
+           count++;
+           success += (*s->handler)(argv[i+1]);
+           i += s->narg;
+       } else {
+           NET2ADD(IAC, s->what);
+           printoption("SENT", IAC, s->what);
+       }
+    }
+    return (count == success);
+}
+
+static int
+send_esc(char *s)
+{
+    NETADD(escape);
+    return 1;
+}
+
+static int
+send_docmd(char *name)
+{
+    return(send_tncmd(send_do, "do", name));
+}
+
+static int
+send_dontcmd(char *name)
+{
+    return(send_tncmd(send_dont, "dont", name));
+}
+static int
+send_willcmd(char *name)
+{
+    return(send_tncmd(send_will, "will", name));
+}
+static int
+send_wontcmd(char *name)
+{
+    return(send_tncmd(send_wont, "wont", name));
+}
+
+int
+send_tncmd(void        (*func)(int, int), const char   *cmd, char *name)
+{
+    const char **cpp;
+    int val = 0;
+
+    if (isprefix(name, "?")) {
+       int col, len;
+
+       printf("usage: send %s <value|option>\n", cmd);
+       printf("\"value\" must be from 0 to 255\n");
+       printf("Valid options are:\n\t");
+
+       col = 8;
+       for (cpp = telopts; *cpp; cpp++) {
+           len = strlen(*cpp) + 3;
+           if (col + len > 65) {
+               printf("\n\t");
+               col = 8;
+           }
+           printf(" \"%s\"", *cpp);
+           col += len;
+       }
+       printf("\n");
+       return 0;
+    }
+    cpp = (void *)genget(name, __UNCONST(telopts), sizeof(char *));
+    if (Ambiguous(cpp)) {
+       fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n",
+                                       name, cmd);
+       return 0;
+    }
+    if (cpp) {
+       val = cpp - telopts;
+    } else {
+       char *cp = name;
+
+       while (*cp >= '0' && *cp <= '9') {
+           val *= 10;
+           val += *cp - '0';
+           cp++;
+       }
+       if (*cp != 0) {
+           fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n",
+                                       name, cmd);
+           return 0;
+       } else if (val < 0 || val > 255) {
+           fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n",
+                                       name, cmd);
+           return 0;
+       }
+    }
+    if (!connected) {
+       printf("?Need to be connected first.\n");
+       return 0;
+    }
+    (*func)(val, 1);
+    return 1;
+}
+
+static int
+send_help(char *n)
+{
+    struct sendlist *s;        /* pointer to current command */
+    for (s = Sendlist; s->name; s++) {
+       if (s->help)
+           printf("%-15s %s\n", s->name, s->help);
+    }
+    return(0);
+}
+\f
+/*
+ * The following are the routines and data structures referred
+ * to by the arguments to the "toggle" command.
+ */
+
+static int
+lclchars(int n)
+{
+    donelclchars = 1;
+    return 1;
+}
+
+static int
+togdebug(int n)
+{
+    if (net > 0 &&
+       (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, telnet_debug)) < 0) {
+           perror("setsockopt (SO_DEBUG)");
+    }
+    return 1;
+}
+
+static int
+togcrlf(int n)
+{
+    if (crlf) {
+       printf("Will send carriage returns as telnet <CR><LF>.\n");
+    } else {
+       printf("Will send carriage returns as telnet <CR><NUL>.\n");
+    }
+    return 1;
+}
+
+int binmode;
+
+static int
+togbinary(int val)
+{
+    donebinarytoggle = 1;
+
+    if (val >= 0) {
+       binmode = val;
+    } else {
+       if (my_want_state_is_will(TELOPT_BINARY) &&
+                               my_want_state_is_do(TELOPT_BINARY)) {
+           binmode = 1;
+       } else if (my_want_state_is_wont(TELOPT_BINARY) &&
+                               my_want_state_is_dont(TELOPT_BINARY)) {
+           binmode = 0;
+       }
+       val = binmode ? 0 : 1;
+    }
+
+    if (val == 1) {
+       if (my_want_state_is_will(TELOPT_BINARY) &&
+                                       my_want_state_is_do(TELOPT_BINARY)) {
+           printf("Already operating in binary mode with remote host.\n");
+       } else {
+           printf("Negotiating binary mode with remote host.\n");
+           tel_enter_binary(3);
+       }
+    } else {
+       if (my_want_state_is_wont(TELOPT_BINARY) &&
+                                       my_want_state_is_dont(TELOPT_BINARY)) {
+           printf("Already in network ascii mode with remote host.\n");
+       } else {
+           printf("Negotiating network ascii mode with remote host.\n");
+           tel_leave_binary(3);
+       }
+    }
+    return 1;
+}
+
+static int
+togrbinary(int val)
+{
+    donebinarytoggle = 1;
+
+    if (val == -1)
+       val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
+
+    if (val == 1) {
+       if (my_want_state_is_do(TELOPT_BINARY)) {
+           printf("Already receiving in binary mode.\n");
+       } else {
+           printf("Negotiating binary mode on input.\n");
+           tel_enter_binary(1);
+       }
+    } else {
+       if (my_want_state_is_dont(TELOPT_BINARY)) {
+           printf("Already receiving in network ascii mode.\n");
+       } else {
+           printf("Negotiating network ascii mode on input.\n");
+           tel_leave_binary(1);
+       }
+    }
+    return 1;
+}
+
+static int
+togxbinary(int val)
+{
+    donebinarytoggle = 1;
+
+    if (val == -1)
+       val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
+
+    if (val == 1) {
+       if (my_want_state_is_will(TELOPT_BINARY)) {
+           printf("Already transmitting in binary mode.\n");
+       } else {
+           printf("Negotiating binary mode on output.\n");
+           tel_enter_binary(2);
+       }
+    } else {
+       if (my_want_state_is_wont(TELOPT_BINARY)) {
+           printf("Already transmitting in network ascii mode.\n");
+       } else {
+           printf("Negotiating network ascii mode on output.\n");
+           tel_leave_binary(2);
+       }
+    }
+    return 1;
+}
+
+#ifdef ENCRYPTION
+extern int EncryptAutoEnc(int);
+extern int EncryptAutoDec(int);
+extern int EncryptDebug(int);
+extern int EncryptVerbose(int);
+#endif /* ENCRYPTION */
+
+struct togglelist {
+    const char *name;          /* name of toggle */
+    const char *help;          /* help message */
+    int                (*handler)      /* routine to do actual setting */
+                       (int);
+    int                *variable;
+    const char *actionexplanation;
+};
+
+static struct togglelist Togglelist[] = {
+    { "autoflush",
+       "flushing of output when sending interrupt characters",
+           0,
+               &autoflush,
+                   "flush output when sending interrupt characters" },
+    { "autosynch",
+       "automatic sending of interrupt characters in urgent mode",
+           0,
+               &autosynch,
+                   "send interrupt characters in urgent mode" },
+#ifdef AUTHENTICATION
+    { "autologin",
+       "automatic sending of login and/or authentication info",
+           0,
+               &autologin,
+                   "send login name and/or authentication information" },
+    { "authdebug",
+       "Toggle authentication debugging",
+           auth_togdebug,
+               0,
+                    "print authentication debugging information" },
+#endif
+#ifdef ENCRYPTION
+    { "autoencrypt",
+      "automatic encryption of data stream",
+           EncryptAutoEnc,
+               0,
+                    "automatically encrypt output" },
+    { "autodecrypt",
+      "automatic decryption of data stream",
+           EncryptAutoDec,
+               0,
+                    "automatically decrypt input" },
+    { "verbose_encrypt",
+      "Toggle verbose encryption output",
+           EncryptVerbose,
+               0,
+                    "print verbose encryption output" },
+    { "encdebug",
+      "Toggle encryption debugging",
+           EncryptDebug,
+               0,
+                    "print encryption debugging information" },
+#endif /* ENCRYPTION */
+    { "skiprc",
+       "don't read ~/.telnetrc file",
+           0,
+               &skiprc,
+                   "skip reading of ~/.telnetrc file" },
+    { "binary",
+       "sending and receiving of binary data",
+           togbinary,
+               0,
+                   0 },
+    { "inbinary",
+       "receiving of binary data",
+           togrbinary,
+               0,
+                   0 },
+    { "outbinary",
+       "sending of binary data",
+           togxbinary,
+               0,
+                   0 },
+    { "crlf",
+       "sending carriage returns as telnet <CR><LF>",
+          togcrlf,
+               &crlf,
+                   0 },
+    { "crmod",
+       "mapping of received carriage returns",
+           0,
+               &crmod,
+                   "map carriage return on output" },
+    { "localchars",
+       "local recognition of certain control characters",
+           lclchars,
+               &localchars,
+                   "recognize certain control characters" },
+    { " ", "", 0, NULL, NULL },                /* empty line */
+#ifdef TN3270
+    { "apitrace",
+       "(debugging) toggle tracing of API transactions",
+           0,
+               &apitrace,
+                   "trace API transactions" },
+    { "cursesdata",
+       "(debugging) toggle printing of hexadecimal curses data",
+           0,
+               &cursesdata,
+                   "print hexadecimal representation of curses data" },
+#endif /* defined(TN3270) */
+    { "debug",
+       "debugging",
+           togdebug,
+               &telnet_debug,
+                   "turn on socket level debugging" },
+    { "netdata",
+       "printing of hexadecimal network data (debugging)",
+           0,
+               &netdata,
+                   "print hexadecimal representation of network traffic" },
+    { "prettydump",
+       "output of \"netdata\" to user readable format (debugging)",
+           0,
+               &prettydump,
+                   "print user readable output for \"netdata\"" },
+    { "options",
+       "viewing of options processing (debugging)",
+           0,
+               &showoptions,
+                   "show option processing" },
+    { "termdata",
+       "(debugging) toggle printing of hexadecimal terminal data",
+           0,
+               &termdata,
+                   "print hexadecimal representation of terminal traffic" },
+    { "?",
+       0,
+           togglehelp, NULL, NULL },
+    { "help",
+       0,
+           togglehelp, NULL, NULL },
+    { .name = 0 }
+};
+
+static int
+togglehelp(int n)
+{
+    struct togglelist *c;
+
+    for (c = Togglelist; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s toggle %s\n", c->name, c->help);
+           else
+               printf("\n");
+       }
+    }
+    printf("\n");
+    printf("%-15s %s\n", "?", "display help information");
+    return 0;
+}
+
+static void
+settogglehelp(int set)
+{
+    struct togglelist *c;
+
+    for (c = Togglelist; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s %s %s\n", c->name, set ? "enable" : "disable",
+                                               c->help);
+           else
+               printf("\n");
+       }
+    }
+}
+
+#define        GETTOGGLE(name) (struct togglelist *) \
+               genget(name, (char **) Togglelist, sizeof(struct togglelist))
+
+static int
+toggle(int  argc, char *argv[])
+{
+    int retval = 1;
+    char *name;
+    struct togglelist *c;
+
+    if (argc < 2) {
+       fprintf(stderr,
+           "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
+       return 0;
+    }
+    argc--;
+    argv++;
+    while (argc--) {
+       name = *argv++;
+       c = GETTOGGLE(name);
+       if (Ambiguous(c)) {
+           fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
+                                       name);
+           return 0;
+       } else if (c == 0) {
+           fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
+                                       name);
+           return 0;
+       } else {
+           if (c->variable) {
+               *c->variable = !*c->variable;           /* invert it */
+               if (c->actionexplanation) {
+                   printf("%s %s.\n", *c->variable? "Will" : "Won't",
+                                                       c->actionexplanation);
+               }
+           }
+           if (c->handler) {
+               retval &= (*c->handler)(-1);
+           }
+       }
+    }
+    return retval;
+}
+\f
+/*
+ * The following perform the "set" command.
+ */
+
+struct termios new_tc = { .c_iflag = 0 };
+
+struct setlist {
+    const char *name;                  /* name */
+    const char *help;                  /* help information */
+    void (*handler)(char *);
+    cc_t *charp;                       /* where it is located at */
+};
+
+static struct setlist Setlist[] = {
+#ifdef KLUDGELINEMODE
+    { "echo",  "character to toggle local echoing on/off", 0, &echoc },
+#endif
+    { "escape",        "character to escape back to telnet command mode", 0, &escape },
+    { "rlogin", "rlogin escape character", 0, &rlogin },
+    { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
+    { " ", "", NULL, NULL },
+    { " ", "The following need 'localchars' to be toggled true", 0, 0 },
+    { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp },
+    { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp },
+    { "quit",  "character to cause an Abort process", 0, termQuitCharp },
+    { "eof",   "character to cause an EOF ", 0, termEofCharp },
+    { " ", "", NULL, NULL },
+    { " ", "The following are for local editing in linemode", 0, 0 },
+    { "erase", "character to use to erase a character", 0, termEraseCharp },
+    { "kill",  "character to use to erase a line", 0, termKillCharp },
+    { "lnext", "character to use for literal next", 0, termLiteralNextCharp },
+    { "susp",  "character to cause a Suspend Process", 0, termSuspCharp },
+    { "reprint", "character to use for line reprint", 0, termRprntCharp },
+    { "worderase", "character to use to erase a word", 0, termWerasCharp },
+    { "start", "character to use for XON", 0, termStartCharp },
+    { "stop",  "character to use for XOFF", 0, termStopCharp },
+    { "forw1", "alternate end of line character", 0, termForw1Charp },
+    { "forw2", "alternate end of line character", 0, termForw2Charp },
+    { "ayt",   "alternate AYT character", 0, termAytCharp },
+    { .name = 0 }
+};
+
+static struct setlist *
+getset(char *name)
+{
+    return (struct setlist *)
+               genget(name, (char **) Setlist, sizeof(struct setlist));
+}
+
+void
+set_escape_char(char *s)
+{
+       if (rlogin != _POSIX_VDISABLE) {
+               rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
+               printf("Telnet rlogin escape character is '%s'.\n",
+                                       control(rlogin));
+       } else {
+               escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
+               printf("Telnet escape character is '%s'.\n", control(escape));
+       }
+}
+
+static int
+setcmd(int  argc, char *argv[])
+{
+    int value;
+    struct setlist *ct;
+    struct togglelist *c;
+
+    if (argc < 2 || argc > 3) {
+       printf("Format is 'set Name Value'\n'set ?' for help.\n");
+       return 0;
+    }
+    if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
+       for (ct = Setlist; ct->name; ct++)
+           printf("%-15s %s\n", ct->name, ct->help);
+       printf("\n");
+       settogglehelp(1);
+       printf("%-15s %s\n", "?", "display help information");
+       return 0;
+    }
+
+    ct = getset(argv[1]);
+    if (ct == 0) {
+       c = GETTOGGLE(argv[1]);
+       if (c == 0) {
+           fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
+                       argv[1]);
+           return 0;
+       } else if (Ambiguous(c)) {
+           fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
+                       argv[1]);
+           return 0;
+       }
+       if (c->variable) {
+           if ((argc == 2) || (strcmp("on", argv[2]) == 0))
+               *c->variable = 1;
+           else if (strcmp("off", argv[2]) == 0)
+               *c->variable = 0;
+           else {
+               printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n");
+               return 0;
+           }
+           if (c->actionexplanation) {
+               printf("%s %s.\n", *c->variable? "Will" : "Won't",
+                                                       c->actionexplanation);
+           }
+       }
+       if (c->handler)
+           (*c->handler)(1);
+    } else if (argc != 3) {
+       printf("Format is 'set Name Value'\n'set ?' for help.\n");
+       return 0;
+    } else if (Ambiguous(ct)) {
+       fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
+                       argv[1]);
+       return 0;
+    } else if (ct->handler) {
+       (*ct->handler)(argv[2]);
+       printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp);
+    } else {
+       if (strcmp("off", argv[2])) {
+           value = special(argv[2]);
+       } else {
+           value = _POSIX_VDISABLE;
+       }
+       *(ct->charp) = (cc_t)value;
+       printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
+    }
+    slc_check();
+    return 1;
+}
+
+static int
+unsetcmd(int  argc, char *argv[])
+{
+    struct setlist *ct;
+    struct togglelist *c;
+    char *name;
+
+    if (argc < 2) {
+       fprintf(stderr,
+           "Need an argument to 'unset' command.  'unset ?' for help.\n");
+       return 0;
+    }
+    if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
+       for (ct = Setlist; ct->name; ct++)
+           printf("%-15s %s\n", ct->name, ct->help);
+       printf("\n");
+       settogglehelp(0);
+       printf("%-15s %s\n", "?", "display help information");
+       return 0;
+    }
+
+    argc--;
+    argv++;
+    while (argc--) {
+       name = *argv++;
+       ct = getset(name);
+       if (ct == 0) {
+           c = GETTOGGLE(name);
+           if (c == 0) {
+               fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n",
+                       name);
+               return 0;
+           } else if (Ambiguous(c)) {
+               fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
+                       name);
+               return 0;
+           }
+           if (c->variable) {
+               *c->variable = 0;
+               if (c->actionexplanation) {
+                   printf("%s %s.\n", *c->variable? "Will" : "Won't",
+                                                       c->actionexplanation);
+               }
+           }
+           if (c->handler)
+               (*c->handler)(0);
+       } else if (Ambiguous(ct)) {
+           fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
+                       name);
+           return 0;
+       } else if (ct->handler) {
+           (*ct->handler)(0);
+           printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp);
+       } else {
+           *(ct->charp) = _POSIX_VDISABLE;
+           printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
+       }
+    }
+    return 1;
+}
+\f
+/*
+ * The following are the data structures and routines for the
+ * 'mode' command.
+ */
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+
+static int
+dokludgemode(int n)
+{
+    kludgelinemode = 1;
+    send_wont(TELOPT_LINEMODE, 1);
+    send_dont(TELOPT_SGA, 1);
+    send_dont(TELOPT_ECHO, 1);
+    return 1;
+}
+#endif
+
+static int
+dolinemode(int n)
+{
+#ifdef KLUDGELINEMODE
+    if (kludgelinemode)
+       send_dont(TELOPT_SGA, 1);
+#endif
+    send_will(TELOPT_LINEMODE, 1);
+    send_dont(TELOPT_ECHO, 1);
+    return 1;
+}
+
+static int
+docharmode(int n)
+{
+#ifdef KLUDGELINEMODE
+    if (kludgelinemode)
+       send_do(TELOPT_SGA, 1);
+    else
+#endif
+    send_wont(TELOPT_LINEMODE, 1);
+    send_do(TELOPT_ECHO, 1);
+    return 1;
+}
+
+static int
+dolmmode(int bit, int on)
+{
+    unsigned char c;
+    extern int linemode;
+
+    if (my_want_state_is_wont(TELOPT_LINEMODE)) {
+       printf("?Need to have LINEMODE option enabled first.\n");
+       printf("'mode ?' for help.\n");
+       return 0;
+    }
+
+    if (on)
+       c = (linemode | bit);
+    else
+       c = (linemode & ~bit);
+    lm_mode(&c, 1, 1);
+    return 1;
+}
+
+int
+set_mode(int bit)
+{
+    return dolmmode(bit, 1);
+}
+
+int
+clear_mode(int bit)
+{
+    return dolmmode(bit, 0);
+}
+
+struct modelist {
+       const char      *name;  /* command name */
+       const char      *help;  /* help string */
+       int     (*handler)      /* routine which executes command */
+                       (int);
+       int     needconnect;    /* Do we need to be connected to execute? */
+       int     arg1;
+};
+
+static struct modelist ModeList[] = {
+    { "character", "Disable LINEMODE option",  docharmode, 1, 0 },
+#ifdef KLUDGELINEMODE
+    { "",      "(or disable obsolete line-by-line mode)", 0, 0, 0 },
+#endif
+    { "line",  "Enable LINEMODE option",       dolinemode, 1, 0 },
+#ifdef KLUDGELINEMODE
+    { "",      "(or enable obsolete line-by-line mode)", 0, 0, 0 },
+#endif
+    { "", "", 0, 0, 0 },
+    { "",      "These require the LINEMODE option to be enabled", 0, 0, 0 },
+    { "isig",  "Enable signal trapping",       set_mode, 1, MODE_TRAPSIG },
+    { "+isig", 0,                              set_mode, 1, MODE_TRAPSIG },
+    { "-isig", "Disable signal trapping",      clear_mode, 1, MODE_TRAPSIG },
+    { "edit",  "Enable character editing",     set_mode, 1, MODE_EDIT },
+    { "+edit", 0,                              set_mode, 1, MODE_EDIT },
+    { "-edit", "Disable character editing",    clear_mode, 1, MODE_EDIT },
+    { "softtabs", "Enable tab expansion",      set_mode, 1, MODE_SOFT_TAB },
+    { "+softtabs", 0,                          set_mode, 1, MODE_SOFT_TAB },
+    { "-softtabs", "Disable character editing",        clear_mode, 1, MODE_SOFT_TAB },
+    { "litecho", "Enable literal character echo", set_mode, 1, MODE_LIT_ECHO },
+    { "+litecho", 0,                           set_mode, 1, MODE_LIT_ECHO },
+    { "-litecho", "Disable literal character echo", clear_mode, 1, MODE_LIT_ECHO },
+    { "help",  0,                              modehelp, 0, 0 },
+#ifdef KLUDGELINEMODE
+    { "kludgeline", 0,                         dokludgemode, 1, 0 },
+#endif
+    { "", "", 0, 0, 0 },
+    { "?",     "Print help information",       modehelp, 0, 0 },
+    { .name = 0 },
+};
+
+
+int
+modehelp(int n)
+{
+    struct modelist *mt;
+
+    printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
+    for (mt = ModeList; mt->name; mt++) {
+       if (mt->help) {
+           if (*mt->help)
+               printf("%-15s %s\n", mt->name, mt->help);
+           else
+               printf("\n");
+       }
+    }
+    return 0;
+}
+
+#define        GETMODECMD(name) (struct modelist *) \
+               genget(name, (char **) ModeList, sizeof(struct modelist))
+
+static int
+modecmd(int  argc, char *argv[])
+{
+    struct modelist *mt;
+
+    if (argc != 2) {
+       printf("'mode' command requires an argument\n");
+       printf("'mode ?' for help.\n");
+    } else if ((mt = GETMODECMD(argv[1])) == 0) {
+       fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
+    } else if (Ambiguous(mt)) {
+       fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
+    } else if (mt->needconnect && !connected) {
+       printf("?Need to be connected first.\n");
+       printf("'mode ?' for help.\n");
+    } else if (mt->handler) {
+       return (*mt->handler)(mt->arg1);
+    }
+    return 0;
+}
+\f
+/*
+ * The following data structures and routines implement the
+ * "display" command.
+ */
+
+static int
+display(int  argc, char *argv[])
+{
+    struct togglelist *tl;
+    struct setlist *sl;
+
+#define        dotog(tl)       if (tl->variable && tl->actionexplanation) { \
+                           if (*tl->variable) { \
+                               printf("will"); \
+                           } else { \
+                               printf("won't"); \
+                           } \
+                           printf(" %s.\n", tl->actionexplanation); \
+                       }
+
+#define        doset(sl)   if (sl->name && *sl->name != ' ') { \
+                       if (sl->handler == 0) \
+                           printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \
+                       else \
+                           printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \
+                   }
+
+    if (argc == 1) {
+       for (tl = Togglelist; tl->name; tl++) {
+           dotog(tl);
+       }
+       printf("\n");
+       for (sl = Setlist; sl->name; sl++) {
+           doset(sl);
+       }
+    } else {
+       int i;
+
+       for (i = 1; i < argc; i++) {
+           sl = getset(argv[i]);
+           tl = GETTOGGLE(argv[i]);
+           if (Ambiguous(sl) || Ambiguous(tl)) {
+               printf("?Ambiguous argument '%s'.\n", argv[i]);
+               return 0;
+           } else if (!sl && !tl) {
+               printf("?Unknown argument '%s'.\n", argv[i]);
+               return 0;
+           } else {
+               if (tl) {
+                   dotog(tl);
+               }
+               if (sl) {
+                   doset(sl);
+               }
+           }
+       }
+    }
+/*@*/optionstatus();
+#ifdef ENCRYPTION
+    EncryptStatus();
+#endif /* ENCRYPTION */
+    return 1;
+#undef doset
+#undef dotog
+}
+\f
+/*
+ * The following are the data structures, and many of the routines,
+ * relating to command processing.
+ */
+
+/*
+ * Set the escape character.
+ */
+static int
+setescape(int argc, char *argv[])
+{
+       char *arg;
+       char buf[50];
+
+       printf(
+           "Deprecated usage - please use 'set escape%s%s' in the future.\n",
+                               (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
+       if (argc > 2)
+               arg = argv[1];
+       else {
+               printf("new escape character: ");
+               (void) fgets(buf, sizeof(buf), stdin);
+               arg = buf;
+       }
+       if (arg[0] != '\0')
+               escape = arg[0];
+       if (!In3270) {
+               printf("Escape character is '%s'.\n", control(escape));
+       }
+       (void) fflush(stdout);
+       return 1;
+}
+
+/*VARARGS*/
+static int
+togcrmod(int argc, char *argv[])
+{
+    crmod = !crmod;
+    printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
+    printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
+    (void) fflush(stdout);
+    return 1;
+}
+
+/*VARARGS*/
+int
+suspend(int argc, char *argv[])
+{
+    setcommandmode();
+    {
+       long oldrows, oldcols, newrows, newcols, err;
+
+       err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
+       (void) kill(0, SIGTSTP);
+       /*
+        * If we didn't get the window size before the SUSPEND, but we
+        * can get them now (?), then send the NAWS to make sure that
+        * we are set up for the right window size.
+        */
+       if (TerminalWindowSize(&newrows, &newcols) && connected &&
+           (err || ((oldrows != newrows) || (oldcols != newcols)))) {
+               sendnaws();
+       }
+    }
+    /* reget parameters in case they were changed */
+    TerminalSaveState();
+    setconnmode(0);
+    return 1;
+}
+
+#ifndef TN3270
+/*ARGSUSED*/
+int
+shell(int argc, char *argv[])
+{
+    long oldrows, oldcols, newrows, newcols;
+    long volatile err; /* Avoid vfork clobbering */
+
+    setcommandmode();
+
+    err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
+    switch(vfork()) {
+    case -1:
+       perror("Fork failed");
+       break;
+
+    case 0:
+       {
+           /*
+            * Fire up the shell in the child.
+            */
+           const char *shellp, *shellname;
+
+           shellp = getenv("SHELL");
+           if (shellp == NULL)
+               shellp = "/bin/sh";
+           if ((shellname = strrchr(shellp, '/')) == 0)
+               shellname = shellp;
+           else
+               shellname++;
+           if (argc > 1)
+               execl(shellp, shellname, "-c", &saveline[1], NULL);
+           else
+               execl(shellp, shellname, NULL);
+           perror("execl");
+           _exit(1);
+       }
+    default:
+           (void)wait((int *)0);       /* Wait for the shell to complete */
+
+           if (TerminalWindowSize(&newrows, &newcols) && connected &&
+               (err || ((oldrows != newrows) || (oldcols != newcols)))) {
+                   sendnaws();
+           }
+           break;
+    }
+    return 1;
+}
+#endif /* !defined(TN3270) */
+
+/*VARARGS*/
+static int
+bye(int  argc, char *argv[])
+{
+    extern int resettermname;
+
+    if (connected) {
+       (void) shutdown(net, 2);
+       printf("Connection closed.\n");
+       (void) NetClose(net);
+       connected = 0;
+       resettermname = 1;
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+       auth_encrypt_connect(connected);
+#endif /* defined(AUTHENTICATION) */
+       /* reset options */
+       tninit();
+#ifdef TN3270
+       SetIn3270();            /* Get out of 3270 mode */
+#endif /* defined(TN3270) */
+    }
+    if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
+       longjmp(toplevel, 1);
+       /* NOTREACHED */
+    }
+    return 1;                  /* Keep lint, etc., happy */
+}
+
+/*VARARGS*/
+int
+quit(int argc, char *argv[])
+{
+       (void) call(bye, "bye", "fromquit", 0);
+       Exit(0);
+       /*NOTREACHED*/
+}
+
+/*VARARGS*/
+int
+logout(int argc, char *argv[])
+{
+       send_do(TELOPT_LOGOUT, 1);
+       (void) netflush();
+       return 1;
+}
+
+\f
+/*
+ * The SLC command.
+ */
+
+struct slclist {
+       const char      *name;
+       const char      *help;
+       void    (*handler)(int);
+       int     arg;
+};
+
+struct slclist SlcList[] = {
+    { "export",        "Use local special character definitions",
+                                               slc_mode_export,        0 },
+    { "import",        "Use remote special character definitions",
+                                               slc_mode_import,        1 },
+    { "check", "Verify remote special character definitions",
+                                               slc_mode_import,        0 },
+    { "help",  0,                              slc_help,               0 },
+    { "?",     "Print help information",       slc_help,               0 },
+    { .name = 0 },
+};
+
+static void
+slc_help(int n)
+{
+    struct slclist *c;
+
+    for (c = SlcList; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s %s\n", c->name, c->help);
+           else
+               printf("\n");
+       }
+    }
+}
+
+static struct slclist *
+getslc(char *name)
+{
+    return (struct slclist *)
+               genget(name, (char **) SlcList, sizeof(struct slclist));
+}
+
+static int
+slccmd(int  argc, char *argv[])
+{
+    struct slclist *c;
+
+    if (argc != 2) {
+       fprintf(stderr,
+           "Need an argument to 'slc' command.  'slc ?' for help.\n");
+       return 0;
+    }
+    c = getslc(argv[1]);
+    if (c == 0) {
+       fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n",
+                               argv[1]);
+       return 0;
+    }
+    if (Ambiguous(c)) {
+       fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n",
+                               argv[1]);
+       return 0;
+    }
+    (*c->handler)(c->arg);
+    slcstate();
+    return 1;
+}
+\f
+/*
+ * The ENVIRON command.
+ */
+
+struct envlist {
+       const char      *name;
+       const char      *help;
+       struct env_lst *(*handler)(const unsigned char *, unsigned char *);
+       int     narg;
+};
+
+struct envlist EnvList[] = {
+    { "define",        "Define an environment variable",
+                                               env_define,     2 },
+    { "undefine", "Undefine an environment variable",
+                                               env_undefine,   1 },
+    { "export",        "Mark an environment variable for automatic export",
+                                               env_export,     1 },
+    { "unexport", "Don't mark an environment variable for automatic export",
+                                               env_unexport,   1 },
+    { "send",  "Send an environment variable", env_send,       1 },
+    { "list",  "List the current environment variables",
+                                               env_list,       0 },
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+    { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)",
+                                               env_varval,    1 },
+#endif
+    { "help",  0,                              env_help,               0 },
+    { "?",     "Print help information",       env_help,               0 },
+    { .name = 0 },
+};
+
+static struct env_lst *
+env_help(const unsigned char *us1, unsigned char *us2)
+{
+    struct envlist *c;
+
+    for (c = EnvList; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s %s\n", c->name, c->help);
+           else
+               printf("\n");
+       }
+    }
+    return NULL;
+}
+
+static struct envlist *
+getenvcmd(char *name)
+{
+    return (struct envlist *)
+               genget(name, (char **) EnvList, sizeof(struct envlist));
+}
+
+int
+env_cmd(int  argc, char *argv[])
+{
+    struct envlist *c;
+
+    if (argc < 2) {
+       fprintf(stderr,
+           "Need an argument to 'environ' command.  'environ ?' for help.\n");
+       return 0;
+    }
+    c = getenvcmd(argv[1]);
+    if (c == 0) {
+       fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n",
+                               argv[1]);
+       return 0;
+    }
+    if (Ambiguous(c)) {
+       fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n",
+                               argv[1]);
+       return 0;
+    }
+    if (c->narg + 2 != argc) {
+       fprintf(stderr,
+           "Need %s%d argument%s to 'environ %s' command.  'environ ?' for help.\n",
+               c->narg < argc + 2 ? "only " : "",
+               c->narg, c->narg == 1 ? "" : "s", c->name);
+       return 0;
+    }
+    (*c->handler)(argv[2], argv[3]);
+    return 1;
+}
+
+struct env_lst {
+       struct env_lst *next;   /* pointer to next structure */
+       struct env_lst *prev;   /* pointer to previous structure */
+       unsigned char *var;     /* pointer to variable name */
+       unsigned char *value;   /* pointer to variable value */
+       int export;             /* 1 -> export with default list of variables */
+       int welldefined;        /* A well defined variable */
+};
+
+struct env_lst envlisthead;
+
+struct env_lst *
+env_find(const unsigned char *var)
+{
+       struct env_lst *ep;
+
+       for (ep = envlisthead.next; ep; ep = ep->next) {
+               if (strcmp((const char *)ep->var, (const char *)var) == 0)
+                       return(ep);
+       }
+       return(NULL);
+}
+
+void
+env_init(void)
+{
+       extern char **environ;
+       char **epp, *cp;
+       struct env_lst *ep;
+
+       for (epp = environ; *epp; epp++) {
+               if ((cp = strchr(*epp, '=')) != NULL) {
+                       *cp = '\0';
+                       ep = env_define((unsigned char *)*epp,
+                                       (unsigned char *)cp+1);
+                       ep->export = 0;
+                       *cp = '=';
+               }
+       }
+       /*
+        * Special case for DISPLAY variable.  If it is ":0.0" or
+        * "unix:0.0", we have to get rid of "unix" and insert our
+        * hostname.
+        */
+       if ((ep = env_find("DISPLAY"))
+           && ((*ep->value == ':')
+               || (strncmp((char *)ep->value, "unix:", 5) == 0))) {
+               char hbuf[MAXHOSTNAMELEN + 1];
+               char *cp2 = strchr((char *)ep->value, ':');
+
+               gethostname(hbuf, sizeof hbuf);
+               hbuf[sizeof(hbuf) - 1] = '\0';
+               cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1);
+               sprintf((char *)cp, "%s%s", hbuf, cp2);
+               free(ep->value);
+               ep->value = (unsigned char *)cp;
+       }
+       /*
+        * If USER is not defined, but LOGNAME is, then add
+        * USER with the value from LOGNAME.  By default, we
+        * don't export the USER variable.
+        */
+       if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) {
+               env_define((const unsigned char *)"USER", ep->value);
+               env_unexport((const unsigned char *)"USER", NULL);
+       }
+       env_export((const unsigned char *)"DISPLAY", NULL);
+       env_export((const unsigned char *)"PRINTER", NULL);
+}
+
+struct env_lst *
+env_define(const unsigned char *var, unsigned char *value)
+{
+       struct env_lst *ep;
+
+       if ((ep = env_find(var)) != NULL) {
+               if (ep->var)
+                       free(ep->var);
+               if (ep->value)
+                       free(ep->value);
+       } else {
+               ep = (struct env_lst *)malloc(sizeof(struct env_lst));
+               ep->next = envlisthead.next;
+               envlisthead.next = ep;
+               ep->prev = &envlisthead;
+               if (ep->next)
+                       ep->next->prev = ep;
+       }
+       ep->welldefined = opt_welldefined(var);
+       ep->export = 1;
+       ep->var = (unsigned char *)strdup((const char *)var);
+       ep->value = (unsigned char *)strdup((const char *)value);
+       return(ep);
+}
+
+struct env_lst *
+env_undefine(const unsigned char *var, unsigned char *d)
+{
+       struct env_lst *ep;
+
+       if ((ep = env_find(var)) != NULL) {
+               ep->prev->next = ep->next;
+               if (ep->next)
+                       ep->next->prev = ep->prev;
+               if (ep->var)
+                       free(ep->var);
+               if (ep->value)
+                       free(ep->value);
+               free(ep);
+       }
+       return NULL;
+}
+
+struct env_lst *
+env_export(const unsigned char *var, unsigned char *d)
+{
+       struct env_lst *ep;
+
+       if ((ep = env_find(var)) != NULL)
+               ep->export = 1;
+       return NULL;
+}
+
+struct env_lst *
+env_unexport(const unsigned char *var, unsigned char *d)
+{
+       struct env_lst *ep;
+
+       if ((ep = env_find(var)) != NULL)
+               ep->export = 0;
+       return NULL;
+}
+
+struct env_lst *
+env_send(const unsigned char *var, unsigned char *d)
+{
+       struct env_lst *ep;
+
+       if (my_state_is_wont(TELOPT_NEW_ENVIRON)
+#ifdef OLD_ENVIRON
+           && my_state_is_wont(TELOPT_OLD_ENVIRON)
+#endif
+               ) {
+               fprintf(stderr,
+                   "Cannot send '%s': Telnet ENVIRON option not enabled\n",
+                                                                       var);
+               return NULL;
+       }
+       ep = env_find(var);
+       if (ep == 0) {
+               fprintf(stderr, "Cannot send '%s': variable not defined\n",
+                                                                       var);
+               return NULL;
+       }
+       env_opt_start_info();
+       env_opt_add(ep->var);
+       env_opt_end(0);
+       return NULL;
+}
+
+struct env_lst *
+env_list(const unsigned char *d1, unsigned char *d2)
+{
+       struct env_lst *ep;
+
+       for (ep = envlisthead.next; ep; ep = ep->next) {
+               printf("%c %-20s %s\n", ep->export ? '*' : ' ',
+                                       ep->var, ep->value);
+       }
+       return NULL;
+}
+
+unsigned char *
+env_default(int init, int welldefined)
+{
+       static struct env_lst *nep = NULL;
+
+       if (init) {
+               nep = &envlisthead;
+               return NULL;
+       }
+       if (nep) {
+               while ((nep = nep->next) != NULL) {
+                       if (nep->export && (nep->welldefined == welldefined))
+                               return(nep->var);
+               }
+       }
+       return(NULL);
+}
+
+unsigned char *
+env_getvalue(const unsigned char *var)
+{
+       struct env_lst *ep;
+
+       if ((ep = env_find(var)) != NULL)
+               return(ep->value);
+       return(NULL);
+}
+
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+void
+env_varval(const unsigned char *what)
+{
+       extern int old_env_var, old_env_value, env_auto;
+       int len = strlen((char *)what);
+
+       if (len == 0)
+               goto unknown;
+
+       if (strncasecmp((char *)what, "status", len) == 0) {
+               if (env_auto)
+                       printf("%s%s", "VAR and VALUE are/will be ",
+                                       "determined automatically\n");
+               if (old_env_var == OLD_ENV_VAR)
+                       printf("VAR and VALUE set to correct definitions\n");
+               else
+                       printf("VAR and VALUE definitions are reversed\n");
+       } else if (strncasecmp((char *)what, "auto", len) == 0) {
+               env_auto = 1;
+               old_env_var = OLD_ENV_VALUE;
+               old_env_value = OLD_ENV_VAR;
+       } else if (strncasecmp((char *)what, "right", len) == 0) {
+               env_auto = 0;
+               old_env_var = OLD_ENV_VAR;
+               old_env_value = OLD_ENV_VALUE;
+       } else if (strncasecmp((char *)what, "wrong", len) == 0) {
+               env_auto = 0;
+               old_env_var = OLD_ENV_VALUE;
+               old_env_value = OLD_ENV_VAR;
+       } else {
+unknown:
+               printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n");
+       }
+}
+#endif
+
+#ifdef AUTHENTICATION
+/*
+ * The AUTHENTICATE command.
+ */
+
+struct authlist {
+       const char      *name;
+       const char      *help;
+       int     (*handler)(char *);
+       int     narg;
+};
+
+struct authlist AuthList[] = {
+    { "status",        "Display current status of authentication information",
+                                               auth_status,    0 },
+    { "disable", "Disable an authentication type ('auth disable ?' for more)",
+                                               auth_disable,   1 },
+    { "enable", "Enable an authentication type ('auth enable ?' for more)",
+                                               auth_enable,    1 },
+    { "help",  0,                              auth_help,              0 },
+    { "?",     "Print help information",       auth_help,              0 },
+    { .name = 0 },
+};
+
+static int
+auth_help(char *s)
+{
+    struct authlist *c;
+
+    for (c = AuthList; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s %s\n", c->name, c->help);
+           else
+               printf("\n");
+       }
+    }
+    return 0;
+}
+
+int
+auth_cmd(int  argc, char *argv[])
+{
+    struct authlist *c;
+
+    if (argc < 2) {
+       fprintf(stderr,
+           "Need an argument to 'auth' command.  'auth ?' for help.\n");
+       return 0;
+    }
+
+    c = (struct authlist *)
+               genget(argv[1], (char **) AuthList, sizeof(struct authlist));
+    if (c == 0) {
+       fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n",
+                               argv[1]);
+       return 0;
+    }
+    if (Ambiguous(c)) {
+       fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n",
+                               argv[1]);
+       return 0;
+    }
+    if (c->narg + 2 != argc) {
+       fprintf(stderr,
+           "Need %s%d argument%s to 'auth %s' command.  'auth ?' for help.\n",
+               c->narg < argc + 2 ? "only " : "",
+               c->narg, c->narg == 1 ? "" : "s", c->name);
+       return 0;
+    }
+    return((*c->handler)(argv[2]));
+}
+#endif
+
+#ifdef ENCRYPTION
+/*
+ * The ENCRYPT command.
+ */
+
+struct encryptlist {
+       const char      *name;
+       const char      *help;
+       int     (*handler)(char *, char *);
+       int     needconnect;
+       int     minarg;
+       int     maxarg;
+};
+
+static int
+       EncryptHelp(char *, char *);
+typedef int (*encrypthandler)(char *, char *);
+
+struct encryptlist EncryptList[] = {
+    { "enable", "Enable encryption. ('encrypt enable ?' for more)",
+                                               EncryptEnable, 1, 1, 2 },
+    { "disable", "Disable encryption. ('encrypt enable ?' for more)",
+                                               EncryptDisable, 0, 1, 2 },
+    { "type", "Set encryption type. ('encrypt type ?' for more)",
+                                               EncryptType, 0, 1, 1 },
+    { "start", "Start encryption. ('encrypt start ?' for more)",
+                               (encrypthandler) EncryptStart, 1, 0, 1 },
+    { "stop", "Stop encryption. ('encrypt stop ?' for more)",
+                               (encrypthandler) EncryptStop, 1, 0, 1 },
+    { "input", "Start encrypting the input stream",
+                               (encrypthandler) EncryptStartInput, 1, 0, 0 },
+    { "-input", "Stop encrypting the input stream",
+                               (encrypthandler) EncryptStopInput, 1, 0, 0 },
+    { "output", "Start encrypting the output stream",
+                               (encrypthandler) EncryptStartOutput, 1, 0, 0 },
+    { "-output", "Stop encrypting the output stream",
+                               (encrypthandler) EncryptStopOutput, 1, 0, 0 },
+
+    { "status",       "Display current status of authentication information",
+                               (encrypthandler) EncryptStatus, 0, 0, 0 },
+    { "help", 0,                                EncryptHelp,   0, 0, 0 },
+    { "?",    "Print help information",                 EncryptHelp,   0, 0, 0 },
+    { .name = 0 },
+};
+
+static int
+EncryptHelp(char *s1, char *s2)
+{
+       struct encryptlist *c;
+
+       for (c = EncryptList; c->name; c++) {
+               if (c->help) {
+                       if (*c->help)
+                               printf("%-15s %s\n", c->name, c->help);
+                       else
+                               printf("\n");
+               }
+       }
+       return (0);
+}
+
+int
+encrypt_cmd(int argc, char *argv[])
+{
+       struct encryptlist *c;
+
+       if (argc < 2) {
+               fprintf(stderr,
+                   "Need an argument to 'encrypt' command.  "
+                   "'encrypt ?' for help.\n");
+               return (0);
+       }
+
+       c = (struct encryptlist *)
+           genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist));
+       if (c == NULL) {
+               fprintf(stderr,
+                   "'%s': unknown argument ('encrypt ?' for help).\n",
+                   argv[1]);
+               return (0);
+       }
+       if (Ambiguous(c)) {
+               fprintf(stderr,
+                   "'%s': ambiguous argument ('encrypt ?' for help).\n",
+                   argv[1]);
+               return (0);
+       }
+       argc -= 2;
+       if (argc < c->minarg || argc > c->maxarg) {
+               if (c->minarg == c->maxarg) {
+                       fprintf(stderr, "Need %s%d argument%s ",
+                           c->minarg < argc ? "only " : "", c->minarg,
+                           c->minarg == 1 ? "" : "s");
+               } else {
+                       fprintf(stderr, "Need %s%d-%d arguments ",
+                           c->maxarg < argc ? "only " : "", c->minarg,
+                           c->maxarg);
+               }
+               fprintf(stderr,
+                   "to 'encrypt %s' command.  'encrypt ?' for help.\n",
+                   c->name);
+               return (0);
+       }
+       if (c->needconnect && !connected) {
+               if (!(argc && (isprefix(argv[2], "help") ||
+                   isprefix(argv[2], "?")))) {
+                       printf("?Need to be connected first.\n");
+                       return (0);
+               }
+       }
+       return ((*c->handler)(argv[2], argv[3]));
+}
+#endif /* ENCRYPTION */
+
+#ifdef TN3270
+static void
+filestuff(int fd)
+{
+    int res;
+
+    setconnmode(0);
+    res = fcntl(fd, F_GETOWN, 0);
+    setcommandmode();
+
+    if (res == -1) {
+       perror("fcntl");
+       return;
+    }
+    printf("\tOwner is %d.\n", res);
+
+    setconnmode(0);
+    res = fcntl(fd, F_GETFL, 0);
+    setcommandmode();
+
+    if (res == -1) {
+       perror("fcntl");
+       return;
+    }
+#ifdef notdef
+    printf("\tFlags are 0x%x: %s\n", res, decodeflags(res));
+#endif
+}
+#endif /* defined(TN3270) */
+
+/*
+ * Print status about the connection.
+ */
+/*ARGSUSED*/
+static int
+status(int argc, char *argv[])
+{
+    if (connected) {
+       printf("Connected to %s.\n", hostname);
+       if ((argc < 2) || strcmp(argv[1], "notmuch")) {
+           int mode = getconnmode();
+
+           if (my_want_state_is_will(TELOPT_LINEMODE)) {
+               printf("Operating with LINEMODE option\n");
+               printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No");
+               printf("%s catching of signals\n",
+                                       (mode&MODE_TRAPSIG) ? "Local" : "No");
+               slcstate();
+#ifdef KLUDGELINEMODE
+           } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) {
+               printf("Operating in obsolete linemode\n");
+#endif
+           } else {
+               printf("Operating in single character mode\n");
+               if (localchars)
+                   printf("Catching signals locally\n");
+           }
+           printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote");
+           if (my_want_state_is_will(TELOPT_LFLOW))
+               printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No");
+#ifdef ENCRYPTION
+           encrypt_display();
+#endif /* ENCRYPTION */
+       }
+    } else {
+       printf("No connection.\n");
+    }
+#   ifndef TN3270
+    printf("Escape character is '%s'.\n", control(escape));
+    (void) fflush(stdout);
+#   else /* !defined(TN3270) */
+    if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
+       printf("Escape character is '%s'.\n", control(escape));
+    }
+    if ((argc >= 2) && !strcmp(argv[1], "everything")) {
+       printf("SIGIO received %d time%s.\n",
+                               sigiocount, (sigiocount == 1)? "":"s");
+       if (In3270) {
+           printf("Process ID %d, process group %d.\n",
+                                           getpid(), getpgrp());
+           printf("Terminal input:\n");
+           filestuff(tin);
+           printf("Terminal output:\n");
+           filestuff(tout);
+           printf("Network socket:\n");
+           filestuff(net);
+       }
+    }
+    if (In3270 && transcom) {
+       printf("Transparent mode command is '%s'.\n", transcom);
+    }
+    (void) fflush(stdout);
+    if (In3270) {
+       return 0;
+    }
+#   endif /* defined(TN3270) */
+    return 1;
+}
+
+/*
+ * Function that gets called when SIGINFO is received.
+ */
+int
+ayt_status(void)
+{
+    return call(status, "status", "notmuch", 0);
+}
+
+static const char *
+sockaddr_ntop(struct sockaddr *sa)
+{
+    static char addrbuf[NI_MAXHOST];
+    const int niflags = NI_NUMERICHOST;
+
+    if (getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf),
+           NULL, 0, niflags) == 0)
+       return addrbuf;
+    else
+       return NULL;
+}
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+static int setpolicy (int, struct addrinfo *, char *);
+
+static int
+setpolicy(int netw, struct addrinfo *res, char *policy)
+{
+       char *buf;
+       int level;
+       int optname;
+
+       if (policy == NULL)
+               return 0;
+
+       buf = ipsec_set_policy(policy, strlen(policy));
+       if (buf == NULL) {
+               printf("%s\n", ipsec_strerror());
+               return -1;
+       }
+       level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
+       optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY;
+       if (setsockopt(netw, level, optname, buf, ipsec_get_policylen(buf)) < 0){
+               perror("setsockopt");
+               return -1;
+       }
+
+       free(buf);
+       return 0;
+}
+#endif
+
+int
+tn(int argc, char *argv[])
+{
+    struct addrinfo hints, *res, *res0;
+    const char *cause = "telnet: unknown";
+    int error;
+#if    defined(IP_OPTIONS) && defined(IPPROTO_IP)
+    char *srp = 0;
+    long srlen;
+    int proto, opt;
+#endif
+    char *cmd, *hostp = 0;
+    const char *portp = 0;
+    const char *user = 0;
+
+    if (connected) {
+       printf("?Already connected to %s\n", hostname);
+       return 0;
+    }
+    if (argc < 2) {
+       (void) strlcpy(line, "open ", sizeof(line));
+       printf("(to) ");
+       (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
+       makeargv();
+       argc = margc;
+       argv = margv;
+    }
+    cmd = *argv;
+    --argc; ++argv;
+    while (argc) {
+       if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?"))
+           goto usage;
+       if (strcmp(*argv, "-l") == 0) {
+           --argc; ++argv;
+           if (argc == 0)
+               goto usage;
+           user = *argv++;
+           --argc;
+           continue;
+       }
+       if (strcmp(*argv, "-a") == 0) {
+           --argc; ++argv;
+           autologin = 1;
+           continue;
+       }
+       if (hostp == 0) {
+           hostp = *argv++;
+           --argc;
+           continue;
+       }
+       if (portp == 0) {
+           portp = *argv++;
+           --argc;
+           continue;
+       }
+    usage:
+       printf("usage: %s [-l user] [-a] host-name [port]\n", cmd);
+       return 0;
+    }
+    if (hostp == 0)
+       goto usage;
+
+    (void) strlcpy(_hostname, hostp, sizeof(_hostname));
+    if (hostp[0] == '@' || hostp[0] == '!') {
+       char *p;
+       hostname = NULL;
+       for (p = hostp + 1; *p; p++) {
+           if (*p == ',' || *p == '@')
+               hostname = p;
+       }
+       if (hostname == NULL) {
+           fprintf(stderr, "%s: bad source route specification\n", hostp);
+           return 0;
+       }
+       *hostname++ = '\0';
+    } else
+       hostname = hostp;
+
+    if (!portp) {
+       telnetport = 1;
+       portp = "telnet";
+    } else if (portp[0] == '-') {
+       /* use telnet negotiation if port number/name preceded by minus sign */
+       telnetport = 1;
+       portp++;
+    } else
+       telnetport = 0;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = family;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = 0;
+    hints.ai_flags = AI_NUMERICHOST;   /* avoid forward lookup */
+    error = getaddrinfo(hostname, portp, &hints, &res0);
+    if (!error) {
+       /* numeric */
+       if (doaddrlookup &&
+           getnameinfo(res0->ai_addr, res0->ai_addrlen,
+               _hostname, sizeof(_hostname), NULL, 0, NI_NAMEREQD) == 0)
+           ; /* okay */
+       else
+           strlcpy(_hostname, hostname, sizeof(_hostname));
+    } else {
+       /* FQDN - try again with forward DNS lookup */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = family;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_protocol = 0;
+       hints.ai_flags = AI_CANONNAME;
+       error = getaddrinfo(hostname, portp, &hints, &res0);
+       if (error == EAI_SERVICE) {
+           fprintf(stderr, "tcp/%s: unknown service\n", portp);
+           return 0;
+       } else if (error) {
+           fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error));
+           return 0;
+       }
+       if (res0->ai_canonname)
+           (void)strlcpy(_hostname, res0->ai_canonname, sizeof(_hostname));
+       else
+           (void)strlcpy(_hostname, hostname, sizeof(_hostname));
+    }
+    hostname = _hostname;
+
+    net = -1;
+    for (res = res0; res; res = res->ai_next) {
+       printf("Trying %s...\n", sockaddr_ntop(res->ai_addr));
+       net = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+       if (net < 0) {
+           cause = "telnet: socket";
+           continue;
+       }
+
+       if (telnet_debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
+           perror("setsockopt (SO_DEBUG)");
+       }
+       if (hostp[0] == '@' || hostp[0] == '!') {
+           if ((srlen = sourceroute(res, hostp, &srp, &proto, &opt)) < 0) {
+               (void) NetClose(net);
+               net = -1;
+               continue;
+           }
+           if (srp && setsockopt(net, proto, opt, srp, srlen) < 0)
+               perror("setsockopt (source route)");
+       }
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+       if (setpolicy(net, res, ipsec_policy_in) < 0) {
+           (void) NetClose(net);
+           net = -1;
+           continue;
+       }
+       if (setpolicy(net, res, ipsec_policy_out) < 0) {
+           (void) NetClose(net);
+           net = -1;
+           continue;
+       }
+#endif
+
+       if (connect(net, res->ai_addr, res->ai_addrlen) < 0) {
+           if (res->ai_next) {
+               int oerrno = errno;
+
+               fprintf(stderr, "telnet: connect to address %s: ",
+                                               sockaddr_ntop(res->ai_addr));
+               errno = oerrno;
+               perror((char *)0);
+           }
+           cause = "telnet: Unable to connect to remote host";
+           (void) NetClose(net);
+           net = -1;
+           continue;
+       }
+
+       connected++;
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+       auth_encrypt_connect(connected);
+#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
+       break;
+    }
+    freeaddrinfo(res0);
+    if (net < 0 || connected == 0) {
+       perror(cause);
+       return 0;
+    }
+
+    cmdrc(hostp, hostname);
+    if (autologin && user == NULL) {
+       struct passwd *pw;
+
+       user = getenv("USER");
+       if (user == NULL ||
+           ((pw = getpwnam(user)) && pw->pw_uid != getuid())) {
+               if ((pw = getpwuid(getuid())) != NULL)
+                       user = pw->pw_name;
+               else
+                       user = NULL;
+       }
+    }
+    if (user) {
+       env_define((const unsigned char *)"USER", __UNCONST(user));
+       env_export((const unsigned char *)"USER", NULL);
+    }
+    (void) call(status, "status", "notmuch", 0);
+    telnet(user); 
+    (void) NetClose(net);
+    ExitString("Connection closed by foreign host.\n",1);
+    /*NOTREACHED*/
+}
+
+#define HELPINDENT ((int)sizeof ("connect"))
+
+static char
+       openhelp[] =    "connect to a site",
+       closehelp[] =   "close current connection",
+       logouthelp[] =  "forcibly logout remote user and close the connection",
+       quithelp[] =    "exit telnet",
+       statushelp[] =  "print status information",
+       helphelp[] =    "print help information",
+       sendhelp[] =    "transmit special characters ('send ?' for more)",
+       sethelp[] =     "set operating parameters ('set ?' for more)",
+       unsethelp[] =   "unset operating parameters ('unset ?' for more)",
+       togglestring[] ="toggle operating parameters ('toggle ?' for more)",
+       slchelp[] =     "change state of special characters ('slc ?' for more)",
+       displayhelp[] = "display operating parameters",
+#ifdef TN3270
+       transcomhelp[] = "specify Unix command for transparent mode pipe",
+#endif /* defined(TN3270) */
+#ifdef AUTHENTICATION
+       authhelp[] =    "turn on (off) authentication ('auth ?' for more)",
+#endif
+#ifdef ENCRYPTION
+       encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)",
+#endif /* ENCRYPTION */
+       zhelp[] =       "suspend telnet",
+       shellhelp[] =   "invoke a subshell",
+       envhelp[] =     "change environment variables ('environ ?' for more)",
+       modestring[] = "try to enter line or character mode ('mode ?' for more)";
+
+static Command cmdtab[] = {
+       { "close",      closehelp,      bye,            1 },
+       { "logout",     logouthelp,     logout,         1 },
+       { "display",    displayhelp,    display,        0 },
+       { "mode",       modestring,     modecmd,        0 },
+       { "open",       openhelp,       tn,             0 },
+       { "quit",       quithelp,       quit,           0 },
+       { "send",       sendhelp,       sendcmd,        0 },
+       { "set",        sethelp,        setcmd,         0 },
+       { "unset",      unsethelp,      unsetcmd,       0 },
+       { "status",     statushelp,     status,         0 },
+       { "toggle",     togglestring,   toggle,         0 },
+       { "slc",        slchelp,        slccmd,         0 },
+#ifdef TN3270
+       { "transcom",   transcomhelp,   settranscom,    0 },
+#endif /* defined(TN3270) */
+#ifdef AUTHENTICATION
+       { "auth",       authhelp,       auth_cmd,       0 },
+#endif
+#ifdef ENCRYPTION
+       { "encrypt",    encrypthelp,    encrypt_cmd,    0 },
+#endif
+       { "z",          zhelp,          suspend,        0 },
+#ifdef TN3270
+       { "!",          shellhelp,      shell,          1 },
+#else
+       { "!",          shellhelp,      shell,          0 },
+#endif
+       { "environ",    envhelp,        env_cmd,        0 },
+       { "?",          helphelp,       help,           0 },
+       { NULL,         NULL,           NULL,           0 }
+};
+
+static char    crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
+static char    escapehelp[] =  "deprecated command -- use 'set escape' instead";
+
+static Command cmdtab2[] = {
+       { "help",       0,              help,           0 },
+       { "escape",     escapehelp,     setescape,      0 },
+       { "crmod",      crmodhelp,      togcrmod,       0 },
+       { NULL,         NULL,           NULL,           0 }
+};
+
+
+/*
+ * Call routine with argc, argv set from args (terminated by 0).
+ */
+
+/*VARARGS1*/
+static int
+call(intrtn_t routine, ...)
+{
+    va_list ap;
+    char *args[100];
+    int argno = 0;
+
+    va_start(ap, routine);
+    while ((args[argno++] = va_arg(ap, char *)) != 0) {
+       ;
+    }
+    va_end(ap);
+    return (*routine)(argno-1, args);
+}
+
+
+static Command *
+getcmd(char *name)
+{
+    Command *cm;
+
+    if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))) != NULL)
+       return cm;
+    return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
+}
+
+void
+command(int top, const char *tbuf, int cnt)
+{
+    Command *c;
+
+    setcommandmode();
+    if (!top) {
+       putchar('\n');
+    } else {
+       (void) signal(SIGINT, SIG_DFL);
+       (void) signal(SIGQUIT, SIG_DFL);
+    }
+    for (;;) {
+       if (rlogin == _POSIX_VDISABLE)
+               printf("%s> ", prompt);
+       if (tbuf) {
+           char *cp;
+           cp = line;
+           while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
+               cnt--;
+           tbuf = 0;
+           if (cp == line || *--cp != '\n' || cp == line)
+               goto getline;
+           *cp = '\0';
+           if (rlogin == _POSIX_VDISABLE)
+               printf("%s\n", line);
+       } else {
+       getline:
+           if (rlogin != _POSIX_VDISABLE)
+               printf("%s> ", prompt);
+#ifdef TN3270
+           fflush(stdout);
+#endif
+           if (fgets(line, sizeof(line), stdin) == NULL) {
+               if (feof(stdin) || ferror(stdin)) {
+                   (void) quit(0, NULL);
+                   /*NOTREACHED*/
+               }
+               break;
+           }
+       }
+       if (line[0] == 0)
+           break;
+       makeargv();
+       if (margv[0] == 0) {
+           break;
+       }
+       c = getcmd(margv[0]);
+       if (Ambiguous(c)) {
+           printf("?Ambiguous command\n");
+           continue;
+       }
+       if (c == 0) {
+           printf("?Invalid command\n");
+           continue;
+       }
+       if (c->needconnect && !connected) {
+           printf("?Need to be connected first.\n");
+           continue;
+       }
+       if ((*c->handler)(margc, margv)) {
+           break;
+       }
+    }
+    if (!top) {
+       if (!connected) {
+           longjmp(toplevel, 1);
+           /*NOTREACHED*/
+       }
+#ifdef TN3270
+       if (shell_active == 0) {
+           setconnmode(0);
+       }
+#else  /* defined(TN3270) */
+       setconnmode(0);
+#endif /* defined(TN3270) */
+    }
+}
+\f
+/*
+ * Help command.
+ */
+static int
+help(int argc, char *argv[])
+{
+       Command *c;
+
+       if (argc == 1) {
+               printf("Commands may be abbreviated.  Commands are:\n\n");
+               for (c = cmdtab; c->name; c++)
+                       if (c->help) {
+                               printf("%-*s\t%s\n", HELPINDENT, c->name,
+                                                                   c->help);
+                       }
+               return 0;
+       }
+       while (--argc > 0) {
+               char *arg;
+               arg = *++argv;
+               c = getcmd(arg);
+               if (Ambiguous(c))
+                       printf("?Ambiguous help command %s\n", arg);
+               else if (c == (Command *)0)
+                       printf("?Invalid help command %s\n", arg);
+               else
+                       printf("%s\n", c->help);
+       }
+       return 0;
+}
+
+static char *rcname = 0;
+static char rcbuf[128];
+
+void
+cmdrc(const char *m1, const char *m2)
+{
+    Command *c;
+    FILE *rcfile;
+    int gotmachine = 0;
+    int l1 = strlen(m1);
+    int l2 = strlen(m2);
+    char m1save[MAXHOSTNAMELEN + 1];
+
+    if (skiprc)
+       return;
+
+    strlcpy(m1save, m1, sizeof(m1save));
+    m1 = m1save;
+
+    if (rcname == 0) {
+       rcname = getenv("HOME");
+       if (rcname)
+           strlcpy(rcbuf, rcname, sizeof(rcbuf));
+       else
+           rcbuf[0] = '\0';
+       strlcat(rcbuf, "/.telnetrc", sizeof(rcbuf));
+       rcname = rcbuf;
+    }
+
+    if ((rcfile = fopen(rcname, "r")) == 0) {
+       return;
+    }
+
+    for (;;) {
+       if (fgets(line, sizeof(line), rcfile) == NULL)
+           break;
+       if (line[0] == 0)
+           break;
+       if (line[0] == '#')
+           continue;
+       if (gotmachine) {
+           if (!isspace((unsigned char)line[0]))
+               gotmachine = 0;
+       }
+       if (gotmachine == 0) {
+           if (isspace((unsigned char)line[0]))
+               continue;
+           if (strncasecmp(line, m1, l1) == 0)
+               strncpy(line, &line[l1], sizeof(line) - l1);
+           else if (strncasecmp(line, m2, l2) == 0)
+               strncpy(line, &line[l2], sizeof(line) - l2);
+           else if (strncasecmp(line, "DEFAULT", 7) == 0)
+               strncpy(line, &line[7], sizeof(line) - 7);
+           else
+               continue;
+           if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
+               continue;
+           gotmachine = 1;
+       }
+       makeargv();
+       if (margv[0] == 0)
+           continue;
+       c = getcmd(margv[0]);
+       if (Ambiguous(c)) {
+           printf("?Ambiguous command: %s\n", margv[0]);
+           continue;
+       }
+       if (c == 0) {
+           printf("?Invalid command: %s\n", margv[0]);
+           continue;
+       }
+       /*
+        * This should never happen...
+        */
+       if (c->needconnect && !connected) {
+           printf("?Need to be connected first for %s.\n", margv[0]);
+           continue;
+       }
+       (*c->handler)(margc, margv);
+    }
+    fclose(rcfile);
+}
+
+/*
+ * Source route is handed in as
+ *     [!]@hop1@hop2...@dst
+ *
+ * If the leading ! is present, it is a strict source route, otherwise it is
+ * assmed to be a loose source route.  Note that leading ! is effective
+ * only for IPv4 case.
+ *
+ * We fill in the source route option as
+ *     hop1,hop2,hop3...dest
+ * and return a pointer to hop1, which will
+ * be the address to connect() to.
+ *
+ * Arguments:
+ *     ai:     The address (by struct addrinfo) for the final destination.
+ *
+ *     arg:    Pointer to route list to decipher
+ *
+ *     cpp:    Pointer to a pointer, so that sourceroute() can return
+ *             the address of result buffer (statically alloc'ed).
+ *
+ *     protop/optp:
+ *             Pointer to an integer.  The pointed variable
+ *     lenp:   pointer to an integer that contains the
+ *             length of *cpp if *cpp != NULL.
+ *
+ * Return values:
+ *
+ *     Returns the length of the option pointed to by *cpp.  If the
+ *     return value is -1, there was a syntax error in the
+ *     option, either arg contained unknown characters or too many hosts,
+ *     or hostname cannot be resolved.
+ *
+ *     The caller needs to pass return value (len), *cpp, *protop and *optp
+ *     to setsockopt(2).
+ *
+ *     *cpp:   Points to the result buffer.  The region is statically
+ *             allocated by the function.
+ *
+ *     *protop:
+ *             protocol # to be passed to setsockopt(2).
+ *
+ *     *optp:  option # to be passed to setsockopt(2).
+ *
+ */
+int
+sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *protop, int *optp)
+{
+       char *cp, *cp2, *lsrp, *lsrep;
+       struct addrinfo hints, *res;
+       int len, error;
+       struct sockaddr_in *sin;
+       char c;
+       static char lsr[44];
+#ifdef INET6
+       struct cmsghdr *cmsg;
+       struct sockaddr_in6 *sin6;
+       static char rhbuf[1024];
+#endif
+
+       /*
+        * Verify the arguments.
+        */
+       if (cpp == NULL)
+               return -1;
+
+       cp = arg;
+
+       *cpp = NULL;
+
+         /* init these just in case.... */
+       lsrp = NULL;
+       lsrep = NULL;
+#ifdef INET6
+       cmsg = NULL;
+#endif
+       
+       switch (ai->ai_family) {
+       case AF_INET:
+               lsrp = lsr;
+               lsrep = lsrp + sizeof(lsr);
+
+               /*
+                * Next, decide whether we have a loose source
+                * route or a strict source route, and fill in
+                * the begining of the option.
+                */
+               if (*cp == '!') {
+                       cp++;
+                       *lsrp++ = IPOPT_SSRR;
+               } else
+                       *lsrp++ = IPOPT_LSRR;
+               if (*cp != '@')
+                       return -1;
+               lsrp++;         /* skip over length, we'll fill it in later */
+               *lsrp++ = 4;
+               cp++;
+               *protop = IPPROTO_IP;
+               *optp = IP_OPTIONS;
+               break;
+#ifdef INET6
+       case AF_INET6:
+#ifdef IPV6_PKTOPTIONS
+               /* RFC2292 */
+               cmsg = inet6_rthdr_init(rhbuf, IPV6_RTHDR_TYPE_0);
+               if (*cp != '@')
+                       return -1;
+               cp++;
+               *protop = IPPROTO_IPV6;
+               *optp = IPV6_PKTOPTIONS;
+               break;
+#else
+               /* no RFC2292 */
+               return -1;
+#endif
+#endif
+       default:
+               return -1;
+       }
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = ai->ai_family;
+       hints.ai_socktype = SOCK_STREAM;
+
+       for (c = 0;;) {
+               if (c == ':')
+                       cp2 = 0;
+               else for (cp2 = cp; (c = *cp2) != '\0'; cp2++) {
+                       if (c == ',') {
+                               *cp2++ = '\0';
+                               if (*cp2 == '@')
+                                       cp2++;
+                       } else if (c == '@') {
+                               *cp2++ = '\0';
+                       }
+#if 0  /*colon conflicts with IPv6 address*/
+                       else if (c == ':') {
+                               *cp2++ = '\0';
+                       }
+#endif
+                       else
+                               continue;
+                       break;
+               }
+               if (!c)
+                       cp2 = 0;
+
+               error = getaddrinfo(cp, NULL, &hints, &res);
+               if (error) {
+                       fprintf(stderr, "%s: %s\n", cp, gai_strerror(error));
+                       return -1;
+               }
+               if (ai->ai_family != res->ai_family) {
+                       freeaddrinfo(res);
+                       return -1;
+               }
+               if (ai->ai_family == AF_INET) {
+                       /*
+                        * Check to make sure there is space for address
+                        */
+                       if (lsrp + 4 > lsrep) {
+                               freeaddrinfo(res);
+                               return -1;
+                       }
+                       sin = (struct sockaddr_in *)res->ai_addr;
+                       memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
+                       lsrp += sizeof(struct in_addr);
+               }
+#ifdef INET6
+               else if (ai->ai_family == AF_INET6) {
+                       sin6 = (struct sockaddr_in6 *)res->ai_addr;
+                       inet6_rthdr_add(cmsg, &sin6->sin6_addr,
+                               IPV6_RTHDR_LOOSE);
+               }
+#endif
+               else {
+                       freeaddrinfo(res);
+                       return -1;
+               }
+               freeaddrinfo(res);
+               if (cp2)
+                       cp = cp2;
+               else
+                       break;
+       }
+       switch (ai->ai_family) {
+       case AF_INET:
+               /* record the last hop */
+               if (lsrp + 4 > lsrep)
+                       return -1;
+               sin = (struct sockaddr_in *)ai->ai_addr;
+               memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
+               lsrp += sizeof(struct in_addr);
+               lsr[IPOPT_OLEN] = lsrp - lsr;
+               if (lsr[IPOPT_OLEN] <= 7 || lsr[IPOPT_OLEN] > 40)
+                       return -1;
+               *lsrp++ = IPOPT_NOP;    /*32bit word align*/
+               len = lsrp - lsr;
+               *cpp = lsr;
+               break;
+#ifdef INET6
+       case AF_INET6:
+               inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
+               len = cmsg->cmsg_len;
+               *cpp = rhbuf;
+               break;
+#endif
+       default:
+               return -1;
+       }
+       return len;
+}
diff --git a/usr.bin/telnet/defines.h b/usr.bin/telnet/defines.h
new file mode 100644 (file)
index 0000000..93f6aa0
--- /dev/null
@@ -0,0 +1,59 @@
+/*     $NetBSD: defines.h,v 1.8 2003/08/07 11:16:09 agc Exp $  */
+
+/*
+ * Copyright (c) 1988, 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: @(#)defines.h     8.1 (Berkeley) 6/6/93
+ */
+
+#define        settimer(x)     clocks.x = clocks.system++
+
+#ifndef TN3270
+
+#define        SetIn3270()
+
+#endif /* !defined(TN3270) */
+
+#define        NETADD(c)       { *netoring.supply = (c); ring_supplied(&netoring, 1); }
+#define        NET2ADD(c1,c2)  { NETADD((c1)); NETADD((c2)); }
+#define        NETBYTES()      (ring_full_count(&netoring))
+#define        NETROOM()       (ring_empty_count(&netoring))
+
+#define        TTYADD(c)       if (!(SYNCHing||flushout)) { \
+                               *ttyoring.supply = c; \
+                               ring_supplied(&ttyoring, 1); \
+                       }
+#define        TTYBYTES()      (ring_full_count(&ttyoring))
+#define        TTYROOM()       (ring_empty_count(&ttyoring))
+
+/*     Various modes */
+#define        MODE_LOCAL_CHARS(m)     ((m)&(MODE_EDIT|MODE_TRAPSIG))
+#define        MODE_LOCAL_ECHO(m)      ((m)&MODE_ECHO)
+#define        MODE_COMMAND_LINE(m)    ((m)==-1)
+
+#define        CONTROL(x)      ((x)&0x1f)              /* CTRL(x) is not portable */
diff --git a/usr.bin/telnet/externs.h b/usr.bin/telnet/externs.h
new file mode 100644 (file)
index 0000000..6647bfc
--- /dev/null
@@ -0,0 +1,413 @@
+/*     $NetBSD: externs.h,v 1.37 2012/01/10 23:39:11 joerg Exp $       */
+
+/*
+ * Copyright (c) 1988, 1990, 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: @(#)externs.h     8.3 (Berkeley) 5/30/95
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/termios.h>
+
+#include <string.h>
+
+#if defined(IPSEC)
+#include <netipsec/ipsec.h>
+#if defined(IPSEC_POLICY_IPSEC)
+extern char *ipsec_policy_in;
+extern char *ipsec_policy_out;
+#endif
+#endif
+
+#ifndef        _POSIX_VDISABLE
+# ifdef sun
+#  include <sys/param.h>       /* pick up VDISABLE definition, mayby */
+# endif
+# ifdef VDISABLE
+#  define _POSIX_VDISABLE VDISABLE
+# else
+#  define _POSIX_VDISABLE ((cc_t)'\377')
+# endif
+#endif
+
+#define        SUBBUFSIZE      256
+
+#include <sys/cdefs.h>
+
+extern int
+    autologin,         /* Autologin enabled */
+    skiprc,            /* Don't process the ~/.telnetrc file */
+    eight,             /* use eight bit mode (binary in and/or out */
+    family,            /* address family of peer */
+    flushout,          /* flush output */
+    connected,         /* Are we connected to the other side? */
+    globalmode,                /* Mode tty should be in */
+    In3270,            /* Are we in 3270 mode? */
+    telnetport,                /* Are we connected to the telnet port? */
+    localflow,         /* Flow control handled locally */
+    restartany,                /* If flow control, restart output on any character */
+    localchars,                /* we recognize interrupt/quit */
+    donelclchars,      /* the user has set "localchars" */
+    showoptions,
+    net,               /* Network file descriptor */
+    tin,               /* Terminal input file descriptor */
+    tout,              /* Terminal output file descriptor */
+    crlf,              /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+    autoflush,         /* flush output when interrupting? */
+    autosynch,         /* send interrupt characters with SYNCH? */
+    SYNCHing,          /* Is the stream in telnet SYNCH mode? */
+    donebinarytoggle,  /* the user has put us in binary */
+    dontlecho,         /* do we suppress local echoing right now? */
+    crmod,
+    netdata,           /* Print out network data flow */
+    prettydump,                /* Print "netdata" output in user readable format */
+#ifdef TN3270
+    cursesdata,                /* Print out curses data flow */
+    apitrace,          /* Trace API transactions */
+#endif /* defined(TN3270) */
+    termdata,          /* Print out terminal data flow */
+    telnet_debug,      /* Debug level */
+    doaddrlookup,      /* do a reverse address lookup? */
+    clienteof;         /* Client received EOF */
+
+extern cc_t escape;    /* Escape to command mode */
+extern cc_t rlogin;    /* Rlogin mode escape character */
+#ifdef KLUDGELINEMODE
+extern cc_t echoc;     /* Toggle local echoing */
+#endif
+
+extern char
+    *prompt;           /* Prompt for command. */
+
+extern char
+    doopt[],
+    dont[],
+    will[],
+    wont[],
+    options[],         /* All the little options */
+    *hostname;         /* Who are we connected to? */
+
+#ifdef ENCRYPTION
+extern void (*encrypt_output)(unsigned char *, int);
+extern int (*decrypt_input)(int);
+#endif /* ENCRYPTION */
+
+/*
+ * 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;}
+
+/*
+ * 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
+
+
+extern FILE
+    *NetTrace;         /* Where debugging output goes */
+extern char
+    NetTraceFile[];    /* Name of file where debugging output goes */
+
+extern jmp_buf
+    toplevel;          /* For error conditions. */
+
+
+/* authenc.c */
+int telnet_net_write(unsigned char *, int);
+void net_encrypt(void);
+int telnet_spin(void);
+char *telnet_getenv(char *);
+char *telnet_gets(char *, char *, int, int);
+
+/* commands.c */
+int send_tncmd(void (*)(int, int), const char *, char *);
+void _setlist_init(void);
+void set_escape_char(char *);
+int set_mode(int);
+int clear_mode(int);
+int modehelp(int);
+int suspend(int, char *[]);
+int shell(int, char *[]);
+int quit(int, char *[]);
+int logout(int, char *[]);
+int env_cmd(int, char *[]);
+struct env_lst *env_find(const unsigned char *);
+void env_init(void);
+struct env_lst *env_define(const unsigned char *, unsigned char *);
+struct env_lst *env_undefine(const unsigned char *, unsigned char *);
+struct env_lst *env_export(const unsigned char *, unsigned char *);
+struct env_lst *env_unexport(const unsigned char *, unsigned char *);
+struct env_lst *env_send(const unsigned char *, unsigned char *);
+struct env_lst *env_list(const unsigned char *, unsigned char *);
+unsigned char *env_default(int, int );
+unsigned char *env_getvalue(const unsigned char *);
+void env_varval(const unsigned char *);
+int auth_cmd(int, char *[]);
+int ayt_status(void);
+int encrypt_cmd(int, char *[]);
+int tn(int, char *[]);
+void command(int, const char *, int);
+void cmdrc(const char *, const char *);
+struct addrinfo;
+int sourceroute(struct addrinfo *, char *, char **, int *, int*);
+
+/* main.c */
+void tninit(void);
+void usage(void) __dead;
+
+/* network.c */
+void init_network(void);
+int stilloob(void);
+void setneturg(void);
+int netflush(void);
+
+/* sys_bsd.c */
+void init_sys(void);
+int TerminalWrite(char *, int);
+int TerminalRead(unsigned char *, int);
+int TerminalAutoFlush(void);
+int TerminalSpecialChars(int);
+void TerminalFlushOutput(void);
+void TerminalSaveState(void);
+cc_t *tcval(int);
+void TerminalDefaultChars(void);
+void TerminalRestoreState(void);
+void TerminalNewMode(int);
+void TerminalSpeeds(long *, long *);
+int TerminalWindowSize(long *, long *);
+int NetClose(int);
+void NetNonblockingIO(int, int);
+void NetSigIO(int, int);
+void NetSetPgrp(int);
+void sys_telnet_init(void);
+int process_rings(int , int , int , int , int , int);
+
+/* telnet.c */
+void init_telnet(void);
+void send_do(int, int );
+void send_dont(int, int );
+void send_will(int, int );
+void send_wont(int, int );
+void willoption(int);
+void wontoption(int);
+char **mklist(char *, char *);
+int is_unique(char *, char **, char **);
+int setup_term(char *, int, int *);
+char *gettermname(void);
+void lm_will(unsigned char *, int);
+void lm_wont(unsigned char *, int);
+void lm_do(unsigned char *, int);
+void lm_dont(unsigned char *, int);
+void lm_mode(unsigned char *, int, int );
+void slc_init(void);
+void slcstate(void);
+void slc_mode_export(int);
+void slc_mode_import(int);
+void slc_import(int);
+void slc_export(void);
+void slc(unsigned char *, int);
+void slc_check(void);
+void slc_start_reply(void);
+void slc_add_reply(unsigned int, unsigned int, cc_t);
+void slc_end_reply(void);
+int slc_update(void);
+void env_opt(unsigned char *, int);
+void env_opt_start(void);
+void env_opt_start_info(void);
+void env_opt_add(unsigned char *);
+int opt_welldefined(const char *);
+void env_opt_end(int);
+int telrcv(void);
+int rlogin_susp(void);
+int Scheduler(int);
+void telnet(const char *);
+void xmitAO(void);
+void xmitEL(void);
+void xmitEC(void);
+int dosynch(char *);
+int get_status(char *);
+void intp(void);
+void sendbrk(void);
+void sendabort(void);
+void sendsusp(void);
+void sendeof(void);
+void sendayt(void);
+void sendnaws(void);
+void tel_enter_binary(int);
+void tel_leave_binary(int);
+
+/* terminal.c */
+void init_terminal(void);
+int ttyflush(int);
+int getconnmode(void);
+void setconnmode(int);
+void setcommandmode(void);
+
+/* utilities.c */
+void upcase(char *);
+int SetSockOpt(int, int, int, int);
+void SetNetTrace(char *);
+void Dump(int, unsigned char *, int);
+void printoption(const char *, int, int );
+void optionstatus(void);
+void printsub(int, unsigned char *, int);
+void EmptyTerminal(void);
+void SetForExit(void);
+void Exit(int) __attribute__((__noreturn__));
+void ExitString(const char *, int) __attribute__((__noreturn__));
+
+
+extern struct  termios new_tc;
+
+# define termEofChar           new_tc.c_cc[VEOF]
+# define termEraseChar         new_tc.c_cc[VERASE]
+# define termIntChar           new_tc.c_cc[VINTR]
+# define termKillChar          new_tc.c_cc[VKILL]
+# define termQuitChar          new_tc.c_cc[VQUIT]
+
+#  define termSuspChar         new_tc.c_cc[VSUSP]
+#  define termFlushChar                new_tc.c_cc[VDISCARD]
+#  define termWerasChar                new_tc.c_cc[VWERASE]
+#  define termRprntChar                new_tc.c_cc[VREPRINT]
+#  define termLiteralNextChar  new_tc.c_cc[VLNEXT]
+#  define termStartChar                new_tc.c_cc[VSTART]
+#  define termStopChar         new_tc.c_cc[VSTOP]
+#  define termForw1Char                new_tc.c_cc[VEOL]
+#  define termForw2Char                new_tc.c_cc[VEOL]
+#  define termAytChar          new_tc.c_cc[VSTATUS]
+
+# define termEofCharp          &termEofChar
+# define termEraseCharp                &termEraseChar
+# define termIntCharp          &termIntChar
+# define termKillCharp         &termKillChar
+# define termQuitCharp         &termQuitChar
+# define termSuspCharp         &termSuspChar
+# define termFlushCharp                &termFlushChar
+# define termWerasCharp                &termWerasChar
+# define termRprntCharp                &termRprntChar
+# define termLiteralNextCharp  &termLiteralNextChar
+# define termStartCharp                &termStartChar
+# define termStopCharp         &termStopChar
+# define termForw1Charp                &termForw1Char
+# define termForw2Charp                &termForw2Char
+# define termAytCharp          &termAytChar
+
+
+/* Tn3270 section */
+#if    defined(TN3270)
+
+extern int
+    HaveInput,         /* Whether an asynchronous I/O indication came in */
+    noasynchtty,       /* Don't do signals on I/O (SIGURG, SIGIO) */
+    noasynchnet,       /* Don't do signals on I/O (SIGURG, SIGIO) */
+    sigiocount,                /* Count of SIGIO receptions */
+    shell_active;      /* Subshell is active */
+
+extern char
+    *Ibackp,           /* Oldest byte of 3270 data */
+    Ibuf[],            /* 3270 buffer */
+    *Ifrontp,          /* Where next 3270 byte goes */
+    tline[200],
+    *transcom;         /* Transparent command */
+
+/* tn3270.c */
+void init_3270(void);
+int DataToNetwork(char *, int, int);
+void inputAvailable(int);
+void outputPurge(void);
+int DataToTerminal(char *, int);
+int Push3270(void);
+void Finish3270(void);
+void StringToTerminal(char *);
+int _putchar(int);
+void SetIn3270(void);
+int tn3270_ttype(void);
+int settranscom(int, char *[]);
+int shell_continue(void);
+int DataFromTerminal(char *, int);
+int DataFromNetwork(char *, int, int);
+void ConnectScreen(void);
+int DoTerminalOutput(void);
+
+#endif /* defined(TN3270) */
diff --git a/usr.bin/telnet/general.h b/usr.bin/telnet/general.h
new file mode 100644 (file)
index 0000000..27cf458
--- /dev/null
@@ -0,0 +1,43 @@
+/*     $NetBSD: general.h,v 1.6 2003/08/07 11:16:09 agc Exp $  */
+
+/*
+ * Copyright (c) 1988, 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: @(#)general.h     8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Some general definitions.
+ */
+
+
+#define        numberof(x)     (sizeof x/sizeof x[0])
+#define        highestof(x)    (numberof(x)-1)
+
+#define        ClearElement(x)         memset((char *)&x, 0, sizeof x)
+#define        ClearArray(x)           memset((char *)x, 0, sizeof x)
diff --git a/usr.bin/telnet/main.c b/usr.bin/telnet/main.c
new file mode 100644 (file)
index 0000000..ac4551c
--- /dev/null
@@ -0,0 +1,361 @@
+/*     $NetBSD: main.c,v 1.29 2012/03/20 20:34:59 matt Exp $   */
+
+/*
+ * Copyright (c) 1988, 1990, 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) 1988, 1990, 1993\
+ The Regents of the University of California.  All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c     8.3 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: main.c,v 1.29 2012/03/20 20:34:59 matt Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <unistd.h>
+
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+#ifdef AUTHENTICATION
+#include <libtelnet/auth.h>
+#endif
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
+
+/* These values need to be the same as defined in libtelnet/kerberos5.c */
+/* Either define them in both places, or put in some common header file. */
+#define OPTS_FORWARD_CREDS     0x00000002
+#define OPTS_FORWARDABLE_CREDS 0x00000001
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+char *ipsec_policy_in = NULL;
+char *ipsec_policy_out = NULL;
+#endif
+
+int family = AF_UNSPEC;
+
+int main(int, char *[]);
+
+/*
+ * Initialize variables.
+ */
+void
+tninit(void)
+{
+    init_terminal();
+
+    init_network();
+
+    init_telnet();
+
+    init_sys();
+
+#ifdef TN3270
+    init_3270();
+#endif
+}
+
+       void
+usage(void)
+{
+       fprintf(stderr, "usage: %s %s%s%s%s\n",
+           prompt,
+#ifdef AUTHENTICATION
+           "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c]",
+           "\n\t[-d] [-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ",
+#else
+           "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char]",
+           "\n\t[-l user] [-n tracefile] ",
+#endif
+#ifdef TN3270
+# ifdef AUTHENTICATION
+           "[-noasynch] [-noasynctty]\n\t[-noasyncnet] [-r] [-t transcom] ",
+# else
+           "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t[-t transcom]",
+# endif
+#else
+           "[-r] ",
+#endif
+#ifdef ENCRYPTION
+           "[-x] "
+#endif
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+           "\n\t[-P policy] [host-name [port]]"
+#else
+           "\n\t[host-name [port]]"
+#endif
+       );
+       exit(1);
+}
+
+/*
+ * main.  Parse arguments, invoke the protocol or command parser.
+ */
+
+
+int
+main(int argc, char *argv[])
+{
+       extern char *optarg;
+       extern int optind;
+       int ch;
+       char *user;
+#ifdef FORWARD
+       extern int forward_flags;
+#endif /* FORWARD */
+
+       tninit();               /* Clear out things */
+
+       TerminalSaveState();
+
+       if ((prompt = strrchr(argv[0], '/')) != NULL)
+               ++prompt;
+       else
+               prompt = argv[0];
+
+       user = NULL;
+
+       rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE;
+       autologin = -1;
+
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+#define IPSECOPT       "P:"
+#else
+#define IPSECOPT
+#endif
+       while ((ch = getopt(argc, argv, "468EKLNS:X:acde:fFk:l:n:rt:x"
+                       IPSECOPT)) != -1) {
+#undef IPSECOPT
+               switch(ch) {
+               case '4':
+                       family = AF_INET;
+                       break;
+               case '6':
+                       family = AF_INET6;
+                       break;
+               case '8':
+                       eight = 3;      /* binary output and input */
+                       break;
+               case 'E':
+                       rlogin = escape = _POSIX_VDISABLE;
+                       break;
+               case 'K':
+#ifdef AUTHENTICATION
+                       autologin = 0;
+#endif
+                       break;
+               case 'L':
+                       eight |= 2;     /* binary output only */
+                       break;
+               case 'N':
+                       doaddrlookup = 0;
+                       break;
+               case 'S':
+                   {
+                       fprintf(stderr,
+                          "%s: Warning: -S ignored, no parsetos() support.\n",
+                                                               prompt);
+                   }
+                       break;
+               case 'X':
+#ifdef AUTHENTICATION
+                       auth_disable_name(optarg);
+#endif
+                       break;
+               case 'a':
+                       autologin = 1;
+                       break;
+               case 'c':
+                       skiprc = 1;
+                       break;
+               case 'd':
+                       telnet_debug = 1;
+                       break;
+               case 'e':
+                       set_escape_char(optarg);
+                       break;
+               case 'f':
+#if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD)
+                       if (forward_flags & OPTS_FORWARD_CREDS) {
+                           fprintf(stderr,
+                                   "%s: Only one of -f and -F allowed.\n",
+                                   prompt);
+                           usage();
+                       }
+                       forward_flags |= OPTS_FORWARD_CREDS;
+#else
+                       fprintf(stderr,
+                        "%s: Warning: -f ignored, no Kerberos V5 support.\n",
+                               prompt);
+#endif
+                       break;
+               case 'F':
+#if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD)
+                       if (forward_flags & OPTS_FORWARD_CREDS) {
+                           fprintf(stderr,
+                                   "%s: Only one of -f and -F allowed.\n",
+                                   prompt);
+                           usage();
+                       }
+                       forward_flags |= OPTS_FORWARD_CREDS;
+                       forward_flags |= OPTS_FORWARDABLE_CREDS;
+#else
+                       fprintf(stderr,
+                        "%s: Warning: -F ignored, no Kerberos V5 support.\n",
+                               prompt);
+#endif
+                       break;
+               case 'k':
+                       fprintf(stderr,
+                          "%s: Warning: -k ignored, no Kerberos V4 support.\n",
+                                                               prompt);
+                       break;
+               case 'l':
+                       if(autologin == 0) {
+                               autologin = -1;
+                       }
+                       user = optarg;
+                       break;
+               case 'n':
+#ifdef TN3270
+                       /* distinguish between "-n oasynch" and "-noasynch" */
+                       if (argv[optind - 1][0] == '-' && argv[optind - 1][1]
+                           == 'n' && argv[optind - 1][2] == 'o') {
+                               if (!strcmp(optarg, "oasynch")) {
+                                       noasynchtty = 1;
+                                       noasynchnet = 1;
+                               } else if (!strcmp(optarg, "oasynchtty"))
+                                       noasynchtty = 1;
+                               else if (!strcmp(optarg, "oasynchnet"))
+                                       noasynchnet = 1;
+                       } else
+#endif /* defined(TN3270) */
+                               SetNetTrace(optarg);
+                       break;
+               case 'r':
+                       rlogin = '~';
+                       break;
+               case 't':
+#ifdef TN3270
+                       (void)strlcpy(tline, optarg, sizeof(tline));
+                       transcom = tline;
+#else
+                       fprintf(stderr,
+                          "%s: Warning: -t ignored, no TN3270 support.\n",
+                                                               prompt);
+#endif
+                       break;
+               case 'x':
+#ifdef ENCRYPTION
+                       encrypt_auto(1);
+                       decrypt_auto(1);
+#else  /* ENCRYPTION */
+                       fprintf(stderr,
+                           "%s: Warning: -x ignored, no ENCRYPT support.\n",
+                                                               prompt);
+#endif /* ENCRYPTION */
+                       break;
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+               case 'P':
+                       if (!strncmp("in", optarg, 2))
+                               ipsec_policy_in = strdup(optarg);
+                       else if (!strncmp("out", optarg, 3)) 
+                               ipsec_policy_out = strdup(optarg);
+                       else
+                               usage();
+                       break;
+#endif
+               case '?':
+               default:
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+
+       if (autologin == -1) {          /* esc@magic.fi; force  */
+#if defined(AUTHENTICATION)
+               autologin = 1;
+#endif
+#if defined(ENCRYPTION)
+               encrypt_auto(1);
+               decrypt_auto(1);
+#endif
+       }
+
+       if (autologin == -1)
+               autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1;
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc) {
+               static char ml[] = "-l";
+               char *args[7];
+               char ** volatile argp;  /* avoid longjmp clobbering */
+
+               argp = args;
+               if (argc > 2)
+                       usage();
+               *argp++ = prompt;
+               if (user) {
+                       *argp++ = ml;
+                       *argp++ = user;
+               }
+               *argp++ = argv[0];              /* host */
+               if (argc > 1)
+                       *argp++ = argv[1];      /* port */
+               *argp = 0;
+
+               if (setjmp(toplevel) != 0)
+                       Exit(0);
+               if (tn(argp - args, args) == 1)
+                       return (0);
+               else
+                       return (1);
+       }
+       (void)setjmp(toplevel);
+       for (;;) {
+#ifdef TN3270
+               if (shell_active)
+                       shell_continue();
+               else
+#endif
+                       command(1, 0, 0);
+       }
+}
diff --git a/usr.bin/telnet/network.c b/usr.bin/telnet/network.c
new file mode 100644 (file)
index 0000000..d8fe3e0
--- /dev/null
@@ -0,0 +1,179 @@
+/*     $NetBSD: network.c,v 1.17 2004/03/20 23:26:05 heas Exp $        */
+
+/*
+ * Copyright (c) 1988, 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[] = "@(#)network.c  8.2 (Berkeley) 12/15/93";
+#else
+__RCSID("$NetBSD: network.c,v 1.17 2004/03/20 23:26:05 heas Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <arpa/telnet.h>
+
+#include "ring.h"
+#include "defines.h"
+#include "externs.h"
+
+Ring           netoring, netiring;
+unsigned char  netobuf[2*BUFSIZ], netibuf[BUFSIZ];
+
+/*
+ * Initialize internal network data structures.
+ */
+
+void
+init_network(void)
+{
+    if (ring_init(&netoring, netobuf, sizeof netobuf) != 1) {
+       exit(1);
+    }
+    if (ring_init(&netiring, netibuf, sizeof netibuf) != 1) {
+       exit(1);
+    }
+    NetTrace = stdout;
+}
+
+
+/*
+ * Check to see if any out-of-band data exists on a socket (for
+ * Telnet "synch" processing).
+ */
+
+int
+stilloob(void)
+{
+    struct pollfd set[1];
+    int value;
+
+    set[0].fd = net;
+    set[0].events = POLLPRI;
+    do {
+       value = poll(set, 1, 0);
+    } while ((value == -1) && (errno == EINTR));
+
+    if (value < 0) {
+       perror("poll");
+       (void) quit(0, NULL);
+       /* NOTREACHED */
+    }
+    if (set[0].revents & POLLPRI) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+
+/*
+ *  setneturg()
+ *
+ *     Sets "neturg" to the current location.
+ */
+
+void
+setneturg(void)
+{
+    ring_mark(&netoring);
+}
+
+
+/*
+ *  netflush
+ *             Send as much data as possible to the network,
+ *     handling requests for urgent data.
+ *
+ *             The return value indicates whether we did any
+ *     useful work.
+ */
+
+
+int
+netflush(void)
+{
+    int n, n1;
+
+#ifdef ENCRYPTION
+    if (encrypt_output)
+       ring_encrypt(&netoring, encrypt_output);
+#endif /* ENCRYPTION */
+    if ((n1 = n = ring_full_consecutive(&netoring)) > 0) {
+       if (!ring_at_mark(&netoring)) {
+           n = send(net, (char *)netoring.consume, n, 0); /* normal write */
+       } else {
+           /*
+            * 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).
+            */
+           n = send(net, (char *)netoring.consume, 1, MSG_OOB);/* URGENT data */
+       }
+    }
+    if (n < 0) {
+       if (errno != ENOBUFS && errno != EWOULDBLOCK) {
+           setcommandmode();
+           perror(hostname);
+           (void)NetClose(net);
+           ring_clear_mark(&netoring);
+           ExitString("Connection closed by foreign host.\n", 1);
+           /*NOTREACHED*/
+       }
+       n = 0;
+    }
+    if (netdata && n) {
+       Dump('>', netoring.consume, n);
+    }
+    if (n) {
+       ring_consumed(&netoring, n);
+       /*
+        * If we sent all, and more to send, then recurse to pick
+        * up the other half.
+        */
+       if ((n1 == n) && ring_full_consecutive(&netoring)) {
+           (void) netflush();
+       }
+       return 1;
+    } else {
+       return 0;
+    }
+}
diff --git a/usr.bin/telnet/ring.c b/usr.bin/telnet/ring.c
new file mode 100644 (file)
index 0000000..71d5b51
--- /dev/null
@@ -0,0 +1,339 @@
+/*     $NetBSD: ring.c,v 1.13 2003/08/07 11:16:10 agc Exp $    */
+
+/*
+ * Copyright (c) 1988, 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[] = "@(#)ring.c     8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: ring.c,v 1.13 2003/08/07 11:16:10 agc Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * This defines a structure for a ring buffer.
+ *
+ * The circular buffer has two parts:
+ *(((
+ *     full:   [consume, supply)
+ *     empty:  [supply, consume)
+ *]]]
+ *
+ */
+
+#include       <stdio.h>
+#include       <string.h>
+#include       <strings.h>
+#include       <errno.h>
+#include       <sys/types.h>
+#include       <sys/ioctl.h>
+#include       <sys/socket.h>
+
+#include       "ring.h"
+#include       "general.h"
+
+/* Internal macros */
+
+#if    !defined(MIN)
+#define        MIN(a,b)        (((a)<(b))? (a):(b))
+#endif /* !defined(MIN) */
+
+#define        ring_subtract(d,a,b)    (((a)-(b) >= 0)? \
+                                       (a)-(b): (((a)-(b))+(d)->size))
+
+#define        ring_increment(d,a,c)   (((a)+(c) < (d)->top)? \
+                                       (a)+(c) : (((a)+(c))-(d)->size))
+
+#define        ring_decrement(d,a,c)   (((a)-(c) >= (d)->bottom)? \
+                                       (a)-(c) : (((a)-(c))-(d)->size))
+
+
+/*
+ * The following is a clock, used to determine full, empty, etc.
+ *
+ * There is some trickiness here.  Since the ring buffers are initialized
+ * to ZERO on allocation, we need to make sure, when interpreting the
+ * clock, that when the times are EQUAL, then the buffer is FULL.
+ */
+static u_long ring_clock = 0;
+
+
+#define        ring_empty(d) (((d)->consume == (d)->supply) && \
+                               ((d)->consumetime >= (d)->supplytime))
+#define        ring_full(d) (((d)->supply == (d)->consume) && \
+                               ((d)->supplytime > (d)->consumetime))
+
+
+
+
+
+/* Buffer state transition routines */
+
+int
+ring_init(Ring *ring, unsigned char *buffer, int count)
+{
+    memset((char *)ring, 0, sizeof *ring);
+
+    ring->size = count;
+
+    ring->supply = ring->consume = ring->bottom = buffer;
+
+    ring->top = ring->bottom+ring->size;
+
+#ifdef ENCRYPTION
+    ring->clearto = 0;
+#endif /* ENCRYPTION */
+
+    return 1;
+}
+
+/* Mark routines */
+
+/*
+ * Mark the most recently supplied byte.
+ */
+
+void
+ring_mark(Ring *ring)
+{
+    ring->mark = ring_decrement(ring, ring->supply, 1);
+}
+
+/*
+ * Is the ring pointing to the mark?
+ */
+
+int
+ring_at_mark(Ring *ring)
+{
+    if (ring->mark == ring->consume) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+/*
+ * Clear any mark set on the ring.
+ */
+
+void
+ring_clear_mark(Ring *ring)
+{
+    ring->mark = 0;
+}
+
+/*
+ * Add characters from current segment to ring buffer.
+ */
+void
+ring_supplied(Ring *ring, int count)
+{
+    ring->supply = ring_increment(ring, ring->supply, count);
+    ring->supplytime = ++ring_clock;
+}
+
+/*
+ * We have just consumed "c" bytes.
+ */
+void
+ring_consumed(Ring *ring, int count)
+{
+    if (count == 0)    /* don't update anything */
+       return;
+
+    if (ring->mark &&
+               (ring_subtract(ring, ring->mark, ring->consume) < count)) {
+       ring->mark = 0;
+    }
+#ifdef ENCRYPTION
+    if (ring->consume < ring->clearto &&
+               ring->clearto <= ring->consume + count)
+       ring->clearto = 0;
+    else if (ring->consume + count > ring->top &&
+               ring->bottom <= ring->clearto &&
+               ring->bottom + ((ring->consume + count) - ring->top))
+       ring->clearto = 0;
+#endif /* ENCRYPTION */
+    ring->consume = ring_increment(ring, ring->consume, count);
+    ring->consumetime = ++ring_clock;
+    /*
+     * Try to encourage "ring_empty_consecutive()" to be large.
+     */
+    if (ring_empty(ring)) {
+       ring->consume = ring->supply = ring->bottom;
+    }
+}
+
+
+
+/* Buffer state query routines */
+
+
+/* Number of bytes that may be supplied */
+int
+ring_empty_count(Ring *ring)
+{
+    if (ring_empty(ring)) {    /* if empty */
+           return ring->size;
+    } else {
+       return ring_subtract(ring, ring->consume, ring->supply);
+    }
+}
+
+/* number of CONSECUTIVE bytes that may be supplied */
+int
+ring_empty_consecutive(Ring *ring)
+{
+    if ((ring->consume < ring->supply) || ring_empty(ring)) {
+                           /*
+                            * if consume is "below" supply, or empty, then
+                            * return distance to the top
+                            */
+       return ring_subtract(ring, ring->top, ring->supply);
+    } else {
+                                   /*
+                                    * else, return what we may.
+                                    */
+       return ring_subtract(ring, ring->consume, ring->supply);
+    }
+}
+
+/* Return the number of bytes that are available for consuming
+ * (but don't give more than enough to get to cross over set mark)
+ */
+
+int
+ring_full_count(Ring *ring)
+{
+    if ((ring->mark == 0) || (ring->mark == ring->consume)) {
+       if (ring_full(ring)) {
+           return ring->size;  /* nothing consumed, but full */
+       } else {
+           return ring_subtract(ring, ring->supply, ring->consume);
+       }
+    } else {
+       return ring_subtract(ring, ring->mark, ring->consume);
+    }
+}
+
+/*
+ * Return the number of CONSECUTIVE bytes available for consuming.
+ * However, don't return more than enough to cross over set mark.
+ */
+int
+ring_full_consecutive(Ring *ring)
+{
+    if ((ring->mark == 0) || (ring->mark == ring->consume)) {
+       if ((ring->supply < ring->consume) || ring_full(ring)) {
+           return ring_subtract(ring, ring->top, ring->consume);
+       } else {
+           return ring_subtract(ring, ring->supply, ring->consume);
+       }
+    } else {
+       if (ring->mark < ring->consume) {
+           return ring_subtract(ring, ring->top, ring->consume);
+       } else {        /* Else, distance to mark */
+           return ring_subtract(ring, ring->mark, ring->consume);
+       }
+    }
+}
+
+/*
+ * Move data into the "supply" portion of of the ring buffer.
+ */
+void
+ring_supply_data(Ring *ring, unsigned char *buffer, int count)
+{
+    int i;
+
+    while (count) {
+       i = MIN(count, ring_empty_consecutive(ring));
+       memmove(ring->supply, buffer, i);
+       ring_supplied(ring, i);
+       count -= i;
+       buffer += i;
+    }
+}
+
+#ifdef notdef
+
+/*
+ * Move data from the "consume" portion of the ring buffer
+ */
+void
+ring_consume_data(Ring *ring, unsigned char *buffer, int count)
+{
+    int i;
+
+    while (count) {
+       i = MIN(count, ring_full_consecutive(ring));
+       memmove(buffer, ring->consume, i);
+       ring_consumed(ring, i);
+       count -= i;
+       buffer += i;
+    }
+}
+#endif
+
+#ifdef ENCRYPTION
+void
+ring_encrypt(Ring *ring, void (*encryptor)(unsigned char *, int))
+{
+       unsigned char *s, *c;
+
+       if (ring_empty(ring) || ring->clearto == ring->supply)
+               return;
+
+       if (!(c = ring->clearto))
+               c = ring->consume;
+
+       s = ring->supply;
+
+       if (s <= c) {
+               (*encryptor)(c, ring->top - c);
+               (*encryptor)(ring->bottom, s - ring->bottom);
+       } else
+               (*encryptor)(c, s - c);
+
+       ring->clearto = ring->supply;
+}
+
+void
+ring_clearto(Ring *ring)
+{
+
+       if (!ring_empty(ring))
+               ring->clearto = ring->supply;
+       else
+               ring->clearto = 0;
+}
+#endif /* ENCRYPTION */
diff --git a/usr.bin/telnet/ring.h b/usr.bin/telnet/ring.h
new file mode 100644 (file)
index 0000000..86f03cc
--- /dev/null
@@ -0,0 +1,104 @@
+/*     $NetBSD: ring.h,v 1.10 2003/08/07 11:16:10 agc Exp $    */
+
+/*
+ * Copyright (c) 1988, 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: @(#)ring.h        8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * This defines a structure for a ring buffer.
+ *
+ * The circular buffer has two parts:
+ *(((
+ *     full:   [consume, supply)
+ *     empty:  [supply, consume)
+ *]]]
+ *
+ */
+typedef struct {
+    unsigned char      *consume,       /* where data comes out of */
+                       *supply,        /* where data comes in to */
+                       *bottom,        /* lowest address in buffer */
+                       *top,           /* highest address+1 in buffer */
+                       *mark;          /* marker (user defined) */
+#ifdef ENCRYPTION
+    unsigned char      *clearto;       /* Data to this point is clear text */
+    unsigned char      *encryyptedto;  /* Data is encrypted to here */
+#endif /* ENCRYPTION */
+    int                size;           /* size in bytes of buffer */
+    u_long     consumetime,    /* help us keep straight full, empty, etc. */
+               supplytime;
+} Ring;
+
+/* Ring buffer structures which are shared */
+
+extern Ring
+    netoring,
+    netiring,
+    ttyoring,
+    ttyiring;
+
+/* Here are some functions and macros to deal with the ring buffer */
+
+/* Initialization routine */
+extern int
+       ring_init(Ring *ring, unsigned char *buffer, int count);
+
+/* Data movement routines */
+extern void
+       ring_supply_data(Ring *ring, unsigned char *buffer, int count);
+#ifdef notdef
+extern void
+       ring_consume_data(Ring *ring, unsigned char *buffer, int count);
+#endif
+
+/* Buffer state transition routines */
+extern void
+       ring_supplied(Ring *ring, int count),
+       ring_consumed(Ring *ring, int count);
+
+/* Buffer state query routines */
+extern int
+       ring_empty_count(Ring *ring),
+       ring_empty_consecutive(Ring *ring),
+       ring_full_count(Ring *ring),
+       ring_full_consecutive(Ring *ring),
+       ring_at_mark(Ring *ring);
+
+#ifdef ENCRYPTION
+extern void
+       ring_encrypt(Ring *ring, void (*func)(unsigned char *, int)),
+       ring_clearto(Ring *ring);
+#endif /* ENCRYPTION */
+
+extern void
+       ring_clear_mark(Ring *ring),
+       ring_mark(Ring *ring);
diff --git a/usr.bin/telnet/sys_bsd.c b/usr.bin/telnet/sys_bsd.c
new file mode 100644 (file)
index 0000000..b237de5
--- /dev/null
@@ -0,0 +1,739 @@
+/*     $NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos Exp $    */
+
+/*
+ * Copyright (c) 1988, 1990, 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
+from: static char sccsid[] = "@(#)sys_bsd.c    8.4 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * The following routines try to encapsulate what is system dependent
+ * (at least between 4.x and dos) which is used in telnet.c.
+ */
+
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <arpa/telnet.h>
+
+#include "ring.h"
+#include "defines.h"
+#include "externs.h"
+#include "types.h"
+
+#define        SIG_FUNC_RET    void
+
+SIG_FUNC_RET susp(int);
+SIG_FUNC_RET ayt(int);
+
+SIG_FUNC_RET intr(int);
+SIG_FUNC_RET intr2(int);
+SIG_FUNC_RET sendwin(int);
+
+
+int
+       tout,                   /* Output file descriptor */
+       tin,                    /* Input file descriptor */
+       net;
+
+struct termios old_tc = { .c_iflag = 0 };
+extern struct termios new_tc;
+
+# ifndef       TCSANOW
+#  ifdef TCSETS
+#   define     TCSANOW         TCSETS
+#   define     TCSADRAIN       TCSETSW
+#   define     tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
+#  else
+#   ifdef TCSETA
+#    define    TCSANOW         TCSETA
+#    define    TCSADRAIN       TCSETAW
+#    define    tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
+#   else
+#    define    TCSANOW         TIOCSETA
+#    define    TCSADRAIN       TIOCSETAW
+#    define    tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
+#   endif
+#  endif
+#  define      tcsetattr(f, a, t) ioctl(f, a, (char *)t)
+#  define      cfgetospeed(ptr)        ((ptr)->c_cflag&CBAUD)
+#  ifdef CIBAUD
+#   define     cfgetispeed(ptr)        (((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
+#  else
+#   define     cfgetispeed(ptr)        cfgetospeed(ptr)
+#  endif
+# endif /* TCSANOW */
+
+
+void
+init_sys(void)
+{
+    tout = fileno(stdout);
+    tin = fileno(stdin);
+
+    errno = 0;
+}
+
+
+int
+TerminalWrite(char *buf, int  n)
+{
+    return write(tout, buf, n);
+}
+
+int
+TerminalRead(unsigned char *buf, int  n)
+{
+    return read(tin, buf, n);
+}
+
+/*
+ *
+ */
+
+int
+TerminalAutoFlush(void)
+{
+    return 1;
+}
+
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+#endif
+/*
+ * TerminalSpecialChars()
+ *
+ * Look at an input character to see if it is a special character
+ * and decide what to do.
+ *
+ * Output:
+ *
+ *     0       Don't add this character.
+ *     1       Do add this character
+ */
+
+int
+TerminalSpecialChars(int c)
+{
+    if (c == termIntChar) {
+       intp();
+       return 0;
+    } else if (c == termQuitChar) {
+#ifdef KLUDGELINEMODE
+       if (kludgelinemode)
+           sendbrk();
+       else
+#endif
+           sendabort();
+       return 0;
+    } else if (c == termEofChar) {
+       if (my_want_state_is_will(TELOPT_LINEMODE)) {
+           sendeof();
+           return 0;
+       }
+       return 1;
+    } else if (c == termSuspChar) {
+       sendsusp();
+       return(0);
+    } else if (c == termFlushChar) {
+       xmitAO();               /* Transmit Abort Output */
+       return 0;
+    } else if (!MODE_LOCAL_CHARS(globalmode)) {
+       if (c == termKillChar) {
+           xmitEL();
+           return 0;
+       } else if (c == termEraseChar) {
+           xmitEC();           /* Transmit Erase Character */
+           return 0;
+       }
+    }
+    return 1;
+}
+
+
+/*
+ * Flush output to the terminal
+ */
+
+void
+TerminalFlushOutput(void)
+{
+    int com = 0;
+    (void) ioctl(fileno(stdout), TIOCFLUSH, &com);
+}
+
+void
+TerminalSaveState(void)
+{
+    tcgetattr(0, &old_tc);
+
+    new_tc = old_tc;
+}
+
+cc_t *
+tcval(int func)
+{
+    switch(func) {
+    case SLC_IP:       return(&termIntChar);
+    case SLC_ABORT:    return(&termQuitChar);
+    case SLC_EOF:      return(&termEofChar);
+    case SLC_EC:       return(&termEraseChar);
+    case SLC_EL:       return(&termKillChar);
+    case SLC_XON:      return(&termStartChar);
+    case SLC_XOFF:     return(&termStopChar);
+    case SLC_FORW1:    return(&termForw1Char);
+    case SLC_FORW2:    return(&termForw2Char);
+    case SLC_AO:       return(&termFlushChar);
+    case SLC_SUSP:     return(&termSuspChar);
+    case SLC_EW:       return(&termWerasChar);
+    case SLC_RP:       return(&termRprntChar);
+    case SLC_LNEXT:    return(&termLiteralNextChar);
+    case SLC_AYT:      return(&termAytChar);
+
+    case SLC_SYNCH:
+    case SLC_BRK:
+    case SLC_EOR:
+    default:
+       return((cc_t *)0);
+    }
+}
+
+void
+TerminalDefaultChars(void)
+{
+    memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
+}
+
+#ifdef notdef
+void
+TerminalRestoreState(void)
+{
+}
+#endif
+
+/*
+ * TerminalNewMode - set up terminal to a specific mode.
+ *     MODE_ECHO: do local terminal echo
+ *     MODE_FLOW: do local flow control
+ *     MODE_TRAPSIG: do local mapping to TELNET IAC sequences
+ *     MODE_EDIT: do local line editing
+ *
+ *     Command mode:
+ *             MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
+ *             local echo
+ *             local editing
+ *             local xon/xoff
+ *             local signal mapping
+ *
+ *     Linemode:
+ *             local/no editing
+ *     Both Linemode and Single Character mode:
+ *             local/remote echo
+ *             local/no xon/xoff
+ *             local/no signal mapping
+ */
+
+
+void
+TerminalNewMode(int f)
+{
+    static int prevmode = 0;
+    struct termios tmp_tc;
+    int onoff;
+    int old;
+    cc_t esc;
+
+    globalmode = f&~MODE_FORCE;
+    if (prevmode == f)
+       return;
+
+    /*
+     * Write any outstanding data before switching modes
+     * ttyflush() returns 0 only when there is no more data
+     * left to write out, it returns -1 if it couldn't do
+     * anything at all, otherwise it returns 1 + the number
+     * of characters left to write.
+#ifndef        USE_TERMIO
+     * We would really like to ask the kernel to wait for the output
+     * to drain, like we can do with the TCSADRAIN, but we don't have
+     * that option.  The only ioctl that waits for the output to
+     * drain, TIOCSETP, also flushes the input queue, which is NOT
+     * what we want (TIOCSETP is like TCSADFLUSH).
+#endif
+     */
+    old = ttyflush(SYNCHing|flushout);
+    if (old < 0 || old > 1) {
+       tcgetattr(tin, &tmp_tc);
+       do {
+           /*
+            * Wait for data to drain, then flush again.
+            */
+           tcsetattr(tin, TCSADRAIN, &tmp_tc);
+           old = ttyflush(SYNCHing|flushout);
+       } while (old < 0 || old > 1);
+    }
+
+    old = prevmode;
+    prevmode = f&~MODE_FORCE;
+    tmp_tc = new_tc;
+
+    if (f&MODE_ECHO) {
+       tmp_tc.c_lflag |= ECHO;
+       tmp_tc.c_oflag |= ONLCR;
+       if (crlf)
+               tmp_tc.c_iflag |= ICRNL;
+    } else {
+       tmp_tc.c_lflag &= ~ECHO;
+       tmp_tc.c_oflag &= ~ONLCR;
+# ifdef notdef
+       if (crlf)
+               tmp_tc.c_iflag &= ~ICRNL;
+# endif
+    }
+
+    if ((f&MODE_FLOW) == 0) {
+       tmp_tc.c_iflag &= ~(IXOFF|IXON);        /* Leave the IXANY bit alone */
+    } else {
+       if (restartany < 0) {
+               tmp_tc.c_iflag |= IXOFF|IXON;   /* Leave the IXANY bit alone */
+       } else if (restartany > 0) {
+               tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
+       } else {
+               tmp_tc.c_iflag |= IXOFF|IXON;
+               tmp_tc.c_iflag &= ~IXANY;
+       }
+    }
+
+    if ((f&MODE_TRAPSIG) == 0) {
+       tmp_tc.c_lflag &= ~ISIG;
+       localchars = 0;
+    } else {
+       tmp_tc.c_lflag |= ISIG;
+       localchars = 1;
+    }
+
+    if (f&MODE_EDIT) {
+       tmp_tc.c_lflag |= ICANON;
+    } else {
+       tmp_tc.c_lflag &= ~ICANON;
+       tmp_tc.c_iflag &= ~ICRNL;
+       tmp_tc.c_cc[VMIN] = 1;
+       tmp_tc.c_cc[VTIME] = 0;
+    }
+
+    if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
+       tmp_tc.c_lflag &= ~IEXTEN;
+    }
+
+    if (f&MODE_SOFT_TAB) {
+# ifdef        OXTABS
+       tmp_tc.c_oflag |= OXTABS;
+# endif
+# ifdef        TABDLY
+       tmp_tc.c_oflag &= ~TABDLY;
+       tmp_tc.c_oflag |= TAB3;
+# endif
+    } else {
+# ifdef        OXTABS
+       tmp_tc.c_oflag &= ~OXTABS;
+# endif
+# ifdef        TABDLY
+       tmp_tc.c_oflag &= ~TABDLY;
+# endif
+    }
+
+    if (f&MODE_LIT_ECHO) {
+# ifdef        ECHOCTL
+       tmp_tc.c_lflag &= ~ECHOCTL;
+# endif
+    } else {
+# ifdef        ECHOCTL
+       tmp_tc.c_lflag |= ECHOCTL;
+# endif
+    }
+
+    if (f == -1) {
+       onoff = 0;
+    } else {
+       if (f & MODE_INBIN)
+               tmp_tc.c_iflag &= ~ISTRIP;
+       else
+               tmp_tc.c_iflag |= ISTRIP;
+       if (f & MODE_OUTBIN) {
+               tmp_tc.c_cflag &= ~(CSIZE|PARENB);
+               tmp_tc.c_cflag |= CS8;
+               tmp_tc.c_oflag &= ~OPOST;
+       } else {
+               tmp_tc.c_cflag &= ~(CSIZE|PARENB);
+               tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
+               tmp_tc.c_oflag |= OPOST;
+       }
+       onoff = 1;
+    }
+
+    if (f != -1) {
+       (void) signal(SIGTSTP, susp);
+       (void) signal(SIGINFO, ayt);
+#if    defined(USE_TERMIO) && defined(NOKERNINFO)
+       tmp_tc.c_lflag |= NOKERNINFO;
+#endif
+       /*
+        * We don't want to process ^Y here.  It's just another
+        * character that we'll pass on to the back end.  It has
+        * to process it because it will be processed when the
+        * user attempts to read it, not when we send it.
+        */
+# ifdef        VDSUSP
+       tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
+# endif
+       /*
+        * If the VEOL character is already set, then use VEOL2,
+        * otherwise use VEOL.
+        */
+       esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
+       if ((tmp_tc.c_cc[VEOL] != esc)
+# ifdef        VEOL2
+           && (tmp_tc.c_cc[VEOL2] != esc)
+# endif
+           ) {
+               if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
+                   tmp_tc.c_cc[VEOL] = esc;
+# ifdef        VEOL2
+               else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
+                   tmp_tc.c_cc[VEOL2] = esc;
+# endif
+       }
+    } else {
+       (void) signal(SIGINFO, (void (*)(int)) ayt_status);
+       (void) signal(SIGTSTP, SIG_DFL);
+       (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
+       tmp_tc = old_tc;
+    }
+    if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
+       tcsetattr(tin, TCSANOW, &tmp_tc);
+
+    ioctl(tin, FIONBIO, (char *)&onoff);
+    ioctl(tout, FIONBIO, (char *)&onoff);
+#if    defined(TN3270)
+    if (noasynchtty == 0) {
+       ioctl(tin, FIOASYNC, (char *)&onoff);
+    }
+#endif /* defined(TN3270) */
+
+}
+
+void
+TerminalSpeeds(long *ispeed, long *ospeed)
+{
+    long in, out;
+
+    out = cfgetospeed(&old_tc);
+    in = cfgetispeed(&old_tc);
+    if (in == 0)
+       in = out;
+
+       *ispeed = in;
+       *ospeed = out;
+}
+
+int
+TerminalWindowSize(long *rows, long *cols)
+{
+    struct winsize ws;
+
+    if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
+       *rows = ws.ws_row;
+       *cols = ws.ws_col;
+       return 1;
+    }
+    return 0;
+}
+
+int
+NetClose(int fd)
+{
+    return close(fd);
+}
+
+
+void
+NetNonblockingIO(int fd, int onoff)
+{
+    ioctl(fd, FIONBIO, (char *)&onoff);
+}
+
+#ifdef TN3270
+void
+NetSigIO(int fd, int onoff)
+{
+    ioctl(fd, FIOASYNC, (char *)&onoff);       /* hear about input */
+}
+
+void
+NetSetPgrp(int fd)
+{
+    int myPid;
+
+    myPid = getpid();
+    fcntl(fd, F_SETOWN, myPid);
+}
+#endif /*defined(TN3270)*/
+\f
+/*
+ * Various signal handling routines.
+ */
+
+/* ARGSUSED */
+SIG_FUNC_RET
+intr(int sig)
+{
+    if (localchars) {
+       intp();
+       return;
+    }
+    setcommandmode();
+    longjmp(toplevel, -1);
+}
+
+/* ARGSUSED */
+SIG_FUNC_RET
+intr2(int sig)
+{
+    if (localchars) {
+#ifdef KLUDGELINEMODE
+       if (kludgelinemode)
+           sendbrk();
+       else
+#endif
+           sendabort();
+       return;
+    }
+}
+
+/* ARGSUSED */
+SIG_FUNC_RET
+susp(int sig)
+{
+    if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
+       return;
+    if (localchars)
+       sendsusp();
+}
+
+/* ARGSUSED */
+SIG_FUNC_RET
+sendwin(int sig)
+{
+    if (connected) {
+       sendnaws();
+    }
+}
+
+/* ARGSUSED */
+SIG_FUNC_RET
+ayt(int sig)
+{
+    if (connected)
+       sendayt();
+    else
+       ayt_status();
+}
+
+\f
+void
+sys_telnet_init(void)
+{
+    (void) signal(SIGINT, intr);
+    (void) signal(SIGQUIT, intr2);
+    (void) signal(SIGPIPE, SIG_IGN);
+    (void) signal(SIGWINCH, sendwin);
+    (void) signal(SIGTSTP, susp);
+    (void) signal(SIGINFO, ayt);
+
+    setconnmode(0);
+
+    NetNonblockingIO(net, 1);
+
+#ifdef TN3270
+    if (noasynchnet == 0) {                    /* DBX can't handle! */
+       NetSigIO(net, 1);
+       NetSetPgrp(net);
+    }
+#endif /* defined(TN3270) */
+
+    if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
+       perror("SetSockOpt");
+    }
+}
+
+/*
+ * Process rings -
+ *
+ *     This routine tries to fill up/empty our various rings.
+ *
+ *     The parameter specifies whether this is a poll operation,
+ *     or a block-until-something-happens operation.
+ *
+ *     The return value is 1 if something happened, 0 if not, < 0 if an
+ *     error occurred.
+ */
+
+int
+process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
+    int dopoll)                /* If 0, then block until something to do */
+{
+    struct pollfd set[3];
+    int c;
+               /* One wants to be a bit careful about setting returnValue
+                * to one, since a one implies we did some useful work,
+                * and therefore probably won't be called to block next
+                * time (TN3270 mode only).
+                */
+    int returnValue = 0;
+
+    set[0].fd = net;
+    set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
+       (netex ? POLLPRI : 0);
+    set[1].fd = tout;
+    set[1].events = ttyout ? POLLOUT : 0;
+    set[2].fd = tin;
+    set[2].events = ttyin ? POLLIN : 0;
+
+    if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
+       if (c == -1) {
+                   /*
+                    * we can get EINTR if we are in line mode,
+                    * and the user does an escape (TSTP), or
+                    * some other signal generator.
+                    */
+           if (errno == EINTR) {
+               return 0;
+           }
+#ifdef TN3270
+                   /*
+                    * we can get EBADF if we were in transparent
+                    * mode, and the transcom process died.
+                   */
+           if (errno == EBADF)
+               return 0;
+#endif /* defined(TN3270) */
+                   /* I don't like this, does it ever happen? */
+           printf("sleep(5) from telnet, after poll\r\n");
+           sleep(5);
+       }
+       return 0;
+    }
+
+    /*
+     * Any urgent data?
+     */
+    if (set[0].revents & POLLPRI) {
+       SYNCHing = 1;
+       (void) ttyflush(1);     /* flush already enqueued data */
+    }
+
+    /*
+     * Something to read from the network...
+     */
+    if (set[0].revents & POLLIN) {
+       int canread;
+
+       canread = ring_empty_consecutive(&netiring);
+       c = recv(net, (char *)netiring.supply, canread, 0);
+       if (c < 0 && errno == EWOULDBLOCK) {
+           c = 0;
+       } else if (c <= 0) {
+           return -1;
+       }
+       if (netdata) {
+           Dump('<', netiring.supply, c);
+       }
+       if (c)
+           ring_supplied(&netiring, c);
+       returnValue = 1;
+    }
+
+    /*
+     * Something to read from the tty...
+     */
+    if (set[2].revents & POLLIN) {
+       c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
+       if (c < 0 && errno == EIO)
+           c = 0;
+       if (c < 0 && errno == EWOULDBLOCK) {
+           c = 0;
+       } else {
+           if (c < 0) {
+               return -1;
+           }
+           if (c == 0) {
+               /* must be an EOF... */
+               if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
+                   *ttyiring.supply = termEofChar;
+                   c = 1;
+               } else {
+                   clienteof = 1;
+                   shutdown(net, 1);
+                   return 0;
+               }
+           }
+           if (termdata) {
+               Dump('<', ttyiring.supply, c);
+           }
+           ring_supplied(&ttyiring, c);
+       }
+       returnValue = 1;                /* did something useful */
+    }
+
+    if (set[0].revents & POLLOUT) {
+       returnValue |= netflush();
+    }
+
+    if (set[1].revents & (POLLHUP|POLLNVAL))
+       return(-1);
+
+    if (set[1].revents & POLLOUT) {
+       returnValue |= (ttyflush(SYNCHing|flushout) > 0);
+    }
+
+    return returnValue;
+}
diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1
new file mode 100644 (file)
index 0000000..fa946c3
--- /dev/null
@@ -0,0 +1,1414 @@
+.\"    $NetBSD: telnet.1,v 1.33 2012/04/08 22:00:39 wiz Exp $
+.\"
+.\" Copyright (c) 1983, 1990, 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: @(#)telnet.1      8.4 (Berkeley) 2/3/94
+.\"
+.Dd October 28, 2003
+.Dt TELNET 1
+.Os
+.Sh NAME
+.Nm telnet
+.Nd user interface to the
+.Tn TELNET
+protocol
+.Sh SYNOPSIS
+.Nm
+.Op Fl 468acdEFfKLNrx
+.Op Fl e Ar escapechar
+.Op Fl k Ar realm
+.Op Fl l Ar user
+.Op Fl n Ar tracefile
+.Op Fl P Ar policy
+.Op Fl S Ar tos
+.Op Fl X Ar authtype
+.Oo
+.Ar host
+.Op Ar port
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+command
+is used to communicate with another host using the
+.Tn TELNET
+protocol.
+If
+.Nm
+is invoked without the
+.Ar host
+argument, it enters command mode,
+indicated by its prompt
+.Pq Nm telnet\&\*[Gt] .
+In this mode, it accepts and executes the commands listed below.
+If it is invoked with arguments, it performs an
+.Ic open
+command with those arguments.
+.Pp
+Options:
+.Bl -tag -width indent
+.It Fl 4
+Forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+Forces
+.Nm
+to use IPv6 addresses only.
+.It Fl 8
+Specifies an 8-bit data path.
+This causes an attempt to
+negotiate the
+.Dv TELNET BINARY
+option on both input and output.
+.It Fl E
+Stops any character from being recognized as an escape character.
+.It Fl F
+If Kerberos V5 authentication is being used, the
+.Fl F
+option allows the local credentials to be forwarded
+to the remote system, including any credentials that
+have already been forwarded into the local environment.
+.It Fl K
+Specifies no automatic login to the remote system.
+.It Fl L
+Specifies an 8-bit data path on output.
+This causes the
+BINARY option to be negotiated on output.
+.It Fl N
+Numeric host address.
+No attempt will be made to look up
+symbolic names for host addresses.
+.It Fl S Ar tos
+Sets the IP type-of-service (TOS) option for the telnet
+connection to the value
+.Ar tos ,
+which can be a numeric TOS value
+or, on systems that support it, a symbolic
+TOS name found in the /etc/iptos file.
+.It Fl X Ar atype
+Disables the
+.Ar atype
+type of authentication.
+.It Fl a
+Attempt automatic login.
+Currently, this sends the user name via the
+.Ev USER
+variable
+of the
+.Ev ENVIRON
+option if supported by the remote system.
+The name used is that of the current user as returned by
+.Xr getlogin 2
+if it agrees with the current user ID,
+otherwise it is the name associated with the user ID.
+.It Fl c
+Disables the reading of the user's
+.Pa \&.telnetrc
+file.
+(See the
+.Ic toggle skiprc
+command on this man page.)
+.It Fl d
+Sets the initial value of the
+.Ic debug
+toggle to
+.Dv TRUE .
+.It Fl e Ar escape char
+Sets the initial
+.Nm
+escape character to
+.Ar escape char .
+If
+.Ar escape char
+is omitted, then
+there will be no escape character.
+.It Fl f
+If Kerberos V5 authentication is being used, the
+.Fl f
+option allows the local credentials to be forwarded to the remote system.
+.It Fl k Ar realm
+If Kerberos authentication is being used, the
+.Fl k
+option requests that telnet obtain tickets for the remote host in
+realm
+.Ar realm
+instead of the remote host's realm, as determined by
+.Xr krb_realmofhost 3 .
+.It Fl l Ar user
+When connecting to the remote system, if the remote system
+understands the
+.Ev ENVIRON
+option, then
+.Ar user
+will be sent to the remote system as the value for the variable USER.
+This option implies the
+.Fl a
+option.
+This option may also be used with the
+.Ic open
+command.
+.It Fl n Ar tracefile
+Opens
+.Ar tracefile
+for recording trace information.
+See the
+.Ic set tracefile
+command below.
+.It Fl P Ar policy
+Use IPsec policy specification string
+.Ar policy ,
+for the connections.
+See
+.Xr ipsec_set_policy 3
+for details.
+.It Fl r
+Specifies a user interface similar to
+.Xr rlogin 1 .
+In this
+mode, the escape character is set to the tilde (~) character,
+unless modified by the
+.Fl e
+option.
+.It Fl x
+Turns on encryption of the data stream if possible.
+This
+option is not available outside of the United States and
+Canada.
+.It Ar host
+Indicates the official name, an alias, or the Internet address
+of a remote host.
+.It Ar port
+Indicates a port number (address of an application).
+If a number is
+not specified, the default
+.Nm
+port is used.
+.El
+.Pp
+When in rlogin mode, a line of the form ~.
+disconnects from the
+remote host; ~ is the telnet escape character.
+Similarly, the line ~^Z suspends the telnet session.
+The line ~^] escapes to the normal telnet escape prompt.
+.Pp
+Once a connection has been opened,
+.Nm
+will attempt to enable the
+.Dv TELNET LINEMODE
+option.
+If this fails, then
+.Nm
+will revert to one of two input modes:
+either \*(Lqcharacter at a time\*(Rq
+or \*(Lqold line by line\*(Rq
+depending on what the remote system supports.
+.Pp
+When
+.Dv LINEMODE
+is enabled, character processing is done on the
+local system, under the control of the remote system.
+When input
+editing or character echoing is to be disabled, the remote system
+will relay that information.
+The remote system will also relay
+changes to any special characters that happen on the remote
+system, so that they can take effect on the local system.
+.Pp
+In \*(Lqcharacter at a time\*(Rq mode, most
+text typed is immediately sent to the remote host for processing.
+.Pp
+In \*(Lqold line by line\*(Rq mode, all text is echoed locally,
+and (normally) only completed lines are sent to the remote host.
+The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used
+to turn off and on the local echo
+(this would mostly be used to enter passwords
+without the password being echoed).
+.Pp
+If the
+.Dv LINEMODE
+option is enabled, or if the
+.Ic localchars
+toggle is
+.Dv TRUE
+(the default for \*(Lqold line by line\*(Lq; see below),
+the user's
+.Ic quit  ,
+.Ic intr ,
+and
+.Ic flush
+characters are trapped locally, and sent as
+.Tn TELNET
+protocol sequences to the remote side.
+If
+.Dv LINEMODE
+has ever been enabled, then the user's
+.Ic susp
+and
+.Ic eof
+are also sent as
+.Tn TELNET
+protocol sequences,
+and
+.Ic quit
+is sent as a
+.Dv TELNET ABORT
+instead of
+.Dv BREAK .
+There are options (see
+.Ic toggle
+.Ic autoflush
+and
+.Ic toggle
+.Ic autosynch
+below)
+which cause this action to flush subsequent output to the terminal
+(until the remote host acknowledges the
+.Tn TELNET
+sequence) and flush previous terminal input
+(in the case of
+.Ic quit
+and
+.Ic intr  ) .
+.Pp
+While connected to a remote host,
+.Nm
+command mode may be entered by typing the
+.Nm
+\*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq).
+When in command mode, the normal terminal editing conventions are available.
+.Pp
+The following
+.Nm
+commands are available.
+Only enough of each command to uniquely identify it need be typed
+(this is also true for arguments to the
+.Ic mode  ,
+.Ic set ,
+.Ic toggle  ,
+.Ic unset ,
+.Ic slc  ,
+.Ic environ ,
+and
+.Ic display
+commands).
+.Pp
+.Bl -tag -width "mode type"
+.It Ic auth Ar argument ...
+The auth command manipulates the information sent through the
+.Dv TELNET AUTHENTICATE
+option.
+Valid arguments for the
+auth command are as follows:
+.Bl -tag -width "disable type"
+.It Ic disable Ar type
+Disables the specified type of authentication.
+To obtain a list of available types, use the
+.Ic auth disable \&?
+command.
+.It Ic enable Ar type
+Enables the specified type of authentication.
+To obtain a list of available types, use the
+.Ic auth enable \&?
+command.
+.It Ic status
+Lists the current status of the various types of
+authentication.
+.El
+.It Ic close
+Close a
+.Tn TELNET
+session and return to command mode.
+.It Ic display Ar argument ...
+Displays all, or some, of the
+.Ic set
+and
+.Ic toggle
+values (see below).
+.It Ic encrypt Ar argument ...
+The encrypt command manipulates the information sent through the
+.Dv TELNET ENCRYPT
+option.
+.Pp
+Note:  Because of export controls, the
+.Dv TELNET ENCRYPT
+option is not supported outside of the United States and Canada.
+.Pp
+Valid arguments for the encrypt command are:
+.Bl -tag -width Ar
+.It Ic disable Ar type Ic [input|output]
+Disables the specified type of encryption.
+If you omit the input and output, both input and output
+are disabled.
+To obtain a list of available types, use the
+.Ic encrypt disable \&?
+command.
+.It Ic enable Ar type Ic [input|output]
+Enables the specified type of encryption.
+If you omit input and output, both input and output are
+enabled.
+To obtain a list of available types, use the
+.Ic encrypt enable \&?
+command.
+.It Ic input
+This is the same as the
+.Ic encrypt start input
+command.
+.It Ic -input
+This is the same as the
+.Ic encrypt stop input
+command.
+.It Ic output
+This is the same as the
+.Ic encrypt start output
+command.
+.It Ic -output
+This is the same as the
+.Ic encrypt stop output
+command.
+.It Ic start Ic [input|output]
+Attempts to start encryption.
+If you omit
+.Ic input
+and
+.Ic output ,
+both input and output are enabled.
+To obtain a list of available types, use the
+.Ic encrypt enable \&?
+command.
+.It Ic status
+Lists the current status of encryption.
+.It Ic stop Ic [input|output]
+Stops encryption.
+If you omit input and output,
+encryption is on both input and output.
+.It Ic type Ar type
+Sets the default type of encryption to be used
+with later
+.Ic encrypt start
+or
+.Ic encrypt stop
+commands.
+.El
+.It Ic environ Ar arguments ...
+The
+.Ic environ
+command is used to manipulate the
+variables that may be sent through the
+.Dv TELNET ENVIRON
+option.
+The initial set of variables is taken from the users
+environment, with only the
+.Ev DISPLAY
+and
+.Ev PRINTER
+variables being exported by default.
+The
+.Ev USER
+variable is also exported if the
+.Fl a
+or
+.Fl l
+options are used.
+.Pp
+Valid arguments for the
+.Ic environ
+command are:
+.Bl -tag -width Fl
+.It Ic define Ar variable value
+Define the variable
+.Ar variable
+to have a value of
+.Ar value .
+Any variables defined by this command are automatically exported.
+The
+.Ar value
+may be enclosed in single or double quotes so
+that tabs and spaces may be included.
+.It Ic undefine Ar variable
+Remove
+.Ar variable
+from the list of environment variables.
+.It Ic export Ar variable
+Mark the variable
+.Ar variable
+to be exported to the remote side.
+.It Ic unexport Ar variable
+Mark the variable
+.Ar variable
+to not be exported unless
+explicitly asked for by the remote side.
+.It Ic list
+List the current set of environment variables.
+Those marked with a
+.Cm *
+will be sent automatically,
+other variables will only be sent if explicitly requested.
+.It Ic \&?
+Prints out help information for the
+.Ic environ
+command.
+.El
+.It Ic logout
+Sends the
+.Dv TELNET LOGOUT
+option to the remote side.
+This command is similar to a
+.Ic close
+command; however, if the remote side does not support the
+.Dv LOGOUT
+option, nothing happens.
+If, however, the remote side does support the
+.Dv LOGOUT
+option, this command should cause the remote side to close the
+.Tn TELNET
+connection.
+If the remote side also supports the concept of
+suspending a user's session for later reattachment,
+the logout argument indicates that you
+should terminate the session immediately.
+.It Ic mode Ar type
+.Ar Type
+is one of several options, depending on the state of the
+.Tn TELNET
+session.
+The remote host is asked for permission to go into the requested mode.
+If the remote host is capable of entering that mode, the requested
+mode will be entered.
+.Bl -tag -width Ar
+.It Ic character
+Disable the
+.Dv TELNET LINEMODE
+option, or, if the remote side does not understand the
+.Dv LINEMODE
+option, then enter \*(Lqcharacter at a time\*(Lq mode.
+.It Ic line
+Enable the
+.Dv TELNET LINEMODE
+option, or, if the remote side does not understand the
+.Dv LINEMODE
+option, then attempt to enter \*(Lqold-line-by-line\*(Lq mode.
+.It Ic isig Pq Ic \-isig
+Attempt to enable (disable) the
+.Dv TRAPSIG
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic edit Pq Ic \-edit
+Attempt to enable (disable) the
+.Dv EDIT
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic softtabs Pq Ic \-softtabs
+Attempt to enable (disable) the
+.Dv SOFT_TAB
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic litecho Pq Ic \-litecho
+Attempt to enable (disable) the
+.Dv LIT_ECHO
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic \&?
+Prints out help information for the
+.Ic mode
+command.
+.El
+.It Ic open Ar host Oo Fl l Ar user Oc Oo Fl a Oc Oo Oo \&- Oc Ns Ar port Oc
+Open a connection to the named host.
+If no port number
+is specified,
+.Nm
+will attempt to contact a
+.Tn TELNET
+server at the default port.
+The host specification may be either a host name (see
+.Xr hosts 5 )
+or an Internet address specified in the \*(Lqdot notation\*(Rq (see
+.Xr inet 3 ) .
+The
+.Fl l
+option may be used to specify the user name
+to be passed to the remote system via the
+.Ev ENVIRON
+option.
+If a port is specified
+.Nm
+omits any automatic initialisation of
+.Tn TELNET
+options.
+When the port number is preceded by a minus sign,
+the initial option negotiation is done.
+.Pp
+After establishing a connection, the file
+.Pa \&.telnetrc
+in the
+user's home directory is read.
+Lines beginning with a # are
+comment lines.
+Blank lines are ignored.
+Lines that begin
+without white space are the start of a machine entry.
+The first thing on such a line is a string identifying the machine
+that is being connected to.
+It may be the hostname or numeric address specified as the argument
+.Ar host ,
+the canonical name of that string as determined by
+.Xr getaddrinfo 3 ,
+or the string
+.Dq DEFAULT
+indicating all hosts.
+The rest of the line, and successive
+lines that begin with white space are assumed to be
+.Nm
+commands and are processed as if they had been typed
+in manually to the
+.Nm
+command prompt.
+.It Ic quit
+Close any open
+.Tn TELNET
+session and exit
+.Nm .
+An end of file (in command mode) will also close a session and exit.
+.It Ic send Ar arguments
+Sends one or more special character sequences to the remote host.
+The following are the arguments which may be specified
+(more than one argument may be specified at a time):
+.Pp
+.Bl -tag -width escape
+.It Ic abort
+Sends the
+.Dv TELNET ABORT
+(Abort
+processes)
+sequence.
+.It Ic ao
+Sends the
+.Dv TELNET AO
+(Abort Output) sequence, which should cause the remote system to flush
+all output
+.Em from
+the remote system
+.Em to
+the user's terminal.
+.It Ic ayt
+Sends the
+.Dv TELNET AYT
+(Are You There)
+sequence, to which the remote system may or may not choose to respond.
+.It Ic brk
+Sends the
+.Dv TELNET BRK
+(Break) sequence, which may have significance to the remote
+system.
+.It Ic ec
+Sends the
+.Dv TELNET EC
+(Erase Character)
+sequence, which should cause the remote system to erase the last character
+entered.
+.It Ic el
+Sends the
+.Dv TELNET EL
+(Erase Line)
+sequence, which should cause the remote system to erase the line currently
+being entered.
+.It Ic eof
+Sends the
+.Dv TELNET EOF
+(End Of File)
+sequence.
+.It Ic eor
+Sends the
+.Dv TELNET EOR
+(End of Record)
+sequence.
+.It Ic escape
+Sends the current
+.Nm
+escape character (initially \*(Lq^\*(Rq).
+.It Ic ga
+Sends the
+.Dv TELNET GA
+(Go Ahead)
+sequence, which likely has no significance to the remote system.
+.It Ic getstatus
+If the remote side supports the
+.Dv TELNET STATUS
+command,
+.Ic getstatus
+will send the subnegotiation to request that the server send
+its current option status.
+.It Ic ip
+Sends the
+.Dv TELNET IP
+(Interrupt Process) sequence, which should cause the remote
+system to abort the currently running process.
+.It Ic nop
+Sends the
+.Dv TELNET NOP
+(No OPeration)
+sequence.
+.It Ic susp
+Sends the
+.Dv TELNET SUSP
+(SUSPend process)
+sequence.
+.It Ic synch
+Sends the
+.Dv TELNET SYNCH
+sequence.
+This sequence causes the remote system to discard all previously typed
+(but not yet read) input.
+This sequence is sent as
+.Tn TCP
+urgent
+data (and may not work if the remote system is a
+.Bx 4.2
+system -- if
+it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal).
+.It Ic do Ar cmd
+.It Ic dont Ar cmd
+.It Ic will Ar cmd
+.It Ic wont Ar cmd
+Sends the
+.Dv TELNET DO
+.Ar cmd
+sequence.
+.Ar Cmd
+can be either a decimal number between 0 and 255,
+or a symbolic name for a specific
+.Dv TELNET
+command.
+.Ar Cmd
+can also be either
+.Ic help
+or
+.Ic \&?
+to print out help information, including
+a list of known symbolic names.
+.It Ic \&?
+Prints out help information for the
+.Ic send
+command.
+.El
+.It Ic set Ar argument value
+.It Ic unset Ar argument value
+The
+.Ic set
+command will set any one of a number of
+.Nm
+variables to a specific value or to
+.Dv TRUE .
+The special value
+.Ic off
+turns off the function associated with
+the variable, this is equivalent to using the
+.Ic unset
+command.
+The
+.Ic unset
+command will disable or set to
+.Dv FALSE
+any of the specified functions.
+The values of variables may be interrogated with the
+.Ic display
+command.
+The variables which may be set or unset, but not toggled, are
+listed here.
+In addition, any of the variables for the
+.Ic toggle
+command may be explicitly set or unset using
+the
+.Ic set
+and
+.Ic unset
+commands.
+.Bl -tag -width escape
+.It Ic ayt
+If
+.Tn TELNET
+is in localchars mode, or
+.Dv LINEMODE
+is enabled, and the status character is typed, a
+.Dv TELNET AYT
+sequence (see
+.Ic send ayt
+above) is sent to the
+remote host.
+The initial value for the "Are You There"
+character is the terminal's status character.
+.It Ic echo
+This is the value (initially \*(Lq^E\*(Rq) which, when in
+\*(Lqline by line\*(Rq mode, toggles between doing local echoing
+of entered characters (for normal processing), and suppressing
+echoing of entered characters (for entering, say, a password).
+.It Ic eof
+If
+.Nm
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Rq mode, entering this character
+as the first character on a line will cause this character to be
+sent to the remote system.
+The initial value of the eof character is taken to be the terminal's
+.Ic eof
+character.
+.It Ic erase
+If
+.Nm
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below),
+.Sy and
+if
+.Nm
+is operating in \*(Lqcharacter at a time\*(Rq mode, then when this
+character is typed, a
+.Dv TELNET EC
+sequence (see
+.Ic send
+.Ic ec
+above)
+is sent to the remote system.
+The initial value for the erase character is taken to be
+the terminal's
+.Ic erase
+character.
+.It Ic escape
+This is the
+.Nm
+escape character (initially \*(Lq^[\*(Rq) which causes entry
+into
+.Nm
+command mode (when connected to a remote system).
+.It Ic flushoutput
+If
+.Nm
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below)
+and the
+.Ic flushoutput
+character is typed, a
+.Dv TELNET AO
+sequence (see
+.Ic send
+.Ic ao
+above)
+is sent to the remote host.
+The initial value for the flush character is taken to be
+the terminal's
+.Ic flush
+character.
+.It Ic forw1
+.It Ic forw2
+If
+.Tn TELNET
+is operating in
+.Dv LINEMODE ,
+these are the
+characters that, when typed, cause partial lines to be
+forwarded to the remote system.
+The initial value for
+the forwarding characters are taken from the terminal's
+eol and eol2 characters.
+.It Ic interrupt
+If
+.Nm
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below)
+and the
+.Ic interrupt
+character is typed, a
+.Dv TELNET IP
+sequence (see
+.Ic send
+.Ic ip
+above)
+is sent to the remote host.
+The initial value for the interrupt character is taken to be
+the terminal's
+.Ic intr
+character.
+.It Ic kill
+If
+.Nm
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below),
+.Ic and
+if
+.Nm
+is operating in \*(Lqcharacter at a time\*(Rq mode, then when this
+character is typed, a
+.Dv TELNET EL
+sequence (see
+.Ic send
+.Ic el
+above)
+is sent to the remote system.
+The initial value for the kill character is taken to be
+the terminal's
+.Ic kill
+character.
+.It Ic lnext
+If
+.Nm
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Lq mode, then this character is taken to
+be the terminal's
+.Ic lnext
+character.
+The initial value for the lnext character is taken to be
+the terminal's
+.Ic lnext
+character.
+.It Ic quit
+If
+.Nm
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below)
+and the
+.Ic quit
+character is typed, a
+.Dv TELNET BRK
+sequence (see
+.Ic send
+.Ic brk
+above)
+is sent to the remote host.
+The initial value for the quit character is taken to be
+the terminal's
+.Ic quit
+character.
+.It Ic reprint
+If
+.Nm
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Lq mode, then this character is taken to
+be the terminal's
+.Ic reprint
+character.
+The initial value for the reprint character is taken to be
+the terminal's
+.Ic reprint
+character.
+.It Ic rlogin
+This is the rlogin escape character.
+If set, the normal
+.Tn TELNET
+escape character is ignored unless it is
+preceded by this character at the beginning of a line.
+This character, at the beginning of a line followed by
+a "."  closes the connection; when followed by a ^Z it
+suspends the
+.Nm
+command.
+The initial state is to
+disable the rlogin escape character.
+.It Ic start
+If the
+.Dv TELNET TOGGLE-FLOW-CONTROL
+option has been enabled,
+then this character is taken to
+be the terminal's
+.Ic start
+character.
+The initial value for the start character is taken to be
+the terminal's
+.Ic start
+character.
+.It Ic stop
+If the
+.Dv TELNET TOGGLE-FLOW-CONTROL
+option has been enabled,
+then this character is taken to
+be the terminal's
+.Ic stop
+character.
+The initial value for the stop character is taken to be
+the terminal's
+.Ic stop
+character.
+.It Ic susp
+If
+.Nm
+is in
+.Ic localchars
+mode, or
+.Dv LINEMODE
+is enabled, and the
+.Ic suspend
+character is typed, a
+.Dv TELNET SUSP
+sequence (see
+.Ic send
+.Ic susp
+above)
+is sent to the remote host.
+The initial value for the suspend character is taken to be
+the terminal's
+.Ic suspend
+character.
+.It Ic tracefile
+This is the file to which the output, caused by
+.Ic netdata
+or
+.Ic option
+tracing being
+.Dv TRUE ,
+will be written.
+If it is set to
+.Dq Fl ,
+then tracing information will be written to standard output (the default).
+.It Ic worderase
+If
+.Nm
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Lq mode, then this character is taken to
+be the terminal's
+.Ic worderase
+character.
+The initial value for the worderase character is taken to be
+the terminal's
+.Ic worderase
+character.
+.It Ic \&?
+Displays the legal
+.Ic set
+.Pq Ic unset
+commands.
+.El
+.It Ic slc Ar state
+The
+.Ic slc
+command (Set Local Characters) is used to set
+or change the state of the special
+characters when the
+.Dv TELNET LINEMODE
+option has
+been enabled.
+Special characters are characters that get
+mapped to
+.Tn TELNET
+commands sequences (like
+.Ic ip
+or
+.Ic quit  )
+or line editing characters (like
+.Ic erase
+and
+.Ic kill  ) .
+By default, the local special characters are exported.
+.Bl -tag -width Fl
+.It Ic check
+Verify the current settings for the current special characters.
+The remote side is requested to send all the current special
+character settings, and if there are any discrepancies with
+the local side, the local side will switch to the remote value.
+.It Ic export
+Switch to the local defaults for the special characters.
+The
+local default characters are those of the local terminal at
+the time when
+.Nm
+was started.
+.It Ic import
+Switch to the remote defaults for the special characters.
+The remote default characters are those of the remote system
+at the time when the
+.Tn TELNET
+connection was established.
+.It Ic \&?
+Prints out help information for the
+.Ic slc
+command.
+.El
+.It Ic status
+Show the current status of
+.Nm .
+This includes the peer one is connected to, as well
+as the current mode.
+.It Ic toggle Ar arguments ...
+Toggle (between
+.Dv TRUE
+and
+.Dv FALSE )
+various flags that control how
+.Nm
+responds to events.
+These flags may be set explicitly to
+.Dv TRUE
+or
+.Dv FALSE
+using the
+.Ic set
+and
+.Ic unset
+commands listed above.
+More than one argument may be specified.
+The state of these flags may be interrogated with the
+.Ic display
+command.
+Valid arguments are:
+.Bl -tag -width Ar
+.It Ic authdebug
+Turns on debugging information for the authentication code.
+.It Ic autoflush
+If
+.Ic autoflush
+and
+.Ic localchars
+are both
+.Dv TRUE ,
+then when the
+.Ic ao  ,
+or
+.Ic quit
+characters are recognized (and transformed into
+.Tn TELNET
+sequences; see
+.Ic set
+above for details),
+.Nm
+refuses to display any data on the user's terminal
+until the remote system acknowledges (via a
+.Dv TELNET TIMING MARK
+option)
+that it has processed those
+.Tn TELNET
+sequences.
+The initial value for this toggle is
+.Dv TRUE
+if the terminal user had not
+done an "stty noflsh", otherwise
+.Dv FALSE
+(see
+.Xr stty 1 ) .
+.It Ic autodecrypt
+When the
+.Dv TELNET ENCRYPT
+option is negotiated, by
+default the actual encryption (decryption) of the data
+stream does not start automatically.
+The autoencrypt
+(autodecrypt) command states that encryption of the
+output (input) stream should be enabled as soon as
+possible.
+.Pp
+Note:  Because of export controls, the
+.Dv TELNET ENCRYPT
+option is not supported outside the United States and Canada.
+.It Ic autologin
+If the remote side supports the
+.Dv TELNET AUTHENTICATION
+option
+.Tn TELNET
+attempts to use it to perform automatic authentication.
+If the
+.Dv AUTHENTICATION
+option is not supported, the user's login
+name are propagated through the
+.Dv TELNET ENVIRON
+option.
+This command is the same as specifying the
+.Fl a
+option on the
+.Ic open
+command.
+.It Ic autosynch
+If
+.Ic autosynch
+and
+.Ic localchars
+are both
+.Dv TRUE ,
+then when either the
+.Ic intr
+or
+.Ic quit
+characters is typed (see
+.Ic set
+above for descriptions of the
+.Ic intr
+and
+.Ic quit
+characters), the resulting
+.Tn TELNET
+sequence sent is followed by the
+.Dv TELNET SYNCH
+sequence.
+This procedure
+.Ic should
+cause the remote system to begin throwing away all previously
+typed input until both of the
+.Tn TELNET
+sequences have been read and acted upon.
+The initial value of this toggle is
+.Dv FALSE .
+.It Ic binary
+Enable or disable the
+.Dv TELNET BINARY
+option on both input and output.
+.It Ic inbinary
+Enable or disable the
+.Dv TELNET BINARY
+option on input.
+.It Ic outbinary
+Enable or disable the
+.Dv TELNET BINARY
+option on output.
+.It Ic crlf
+If this is
+.Dv TRUE ,
+then carriage returns will be sent as
+.Li \*[Lt]CR\*[Gt]\*[Lt]LF\*[Gt] .
+If this is
+.Dv FALSE ,
+then carriage returns will be send as
+.Li \*[Lt]CR\*[Gt]\*[Lt]NUL\*[Gt] .
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic crmod
+Toggle carriage return mode.
+When this mode is enabled, most carriage return characters received from
+the remote host will be mapped into a carriage return followed by
+a line feed.
+This mode does not affect those characters typed by the user, only
+those received from the remote host.
+This mode is not very useful unless the remote host
+only sends carriage return, but never line feed.
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic debug
+Toggles socket level debugging (useful only to the
+.Ic super user  ) .
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic encdebug
+Turns on debugging information for the encryption code.
+.It Ic localchars
+If this is
+.Dv TRUE ,
+then the
+.Ic flush  ,
+.Ic interrupt ,
+.Ic quit  ,
+.Ic erase ,
+and
+.Ic kill
+characters (see
+.Ic set
+above) are recognized locally, and transformed into (hopefully) appropriate
+.Tn TELNET
+control sequences
+(respectively
+.Ic ao  ,
+.Ic ip ,
+.Ic brk  ,
+.Ic ec ,
+and
+.Ic el  ;
+see
+.Ic send
+above).
+The initial value for this toggle is
+.Dv TRUE
+in \*(Lqold line by line\*(Rq mode,
+and
+.Dv FALSE
+in \*(Lqcharacter at a time\*(Rq mode.
+When the
+.Dv LINEMODE
+option is enabled, the value of
+.Ic localchars
+is ignored, and assumed to always be
+.Dv TRUE .
+If
+.Dv LINEMODE
+has ever been enabled, then
+.Ic quit
+is sent as
+.Ic abort  ,
+and
+.Ic eof and
+.Ic suspend
+are sent as
+.Ic eof and
+.Ic susp
+(see
+.Ic send
+above).
+.It Ic netdata
+Toggles the display of all network data (in hexadecimal format).
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic options
+Toggles the display of some internal
+.Nm
+protocol processing (having to do with
+.Tn TELNET
+options).
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic prettydump
+When the
+.Ic netdata
+toggle is enabled, if
+.Ic prettydump
+is enabled the output from the
+.Ic netdata
+command will be formatted in a more user readable format.
+Spaces are put between each character in the output, and the
+beginning of any
+.Tn TELNET
+escape sequence is preceded by a '*' to aid in locating them.
+.It Ic skiprc
+When the skiprc toggle is
+.Dv TRUE ,
+.Tn TELNET
+skips the reading of the
+.Pa \&.telnetrc
+file in the users home
+directory when connections are opened.
+The initial
+value for this toggle is
+.Dv FALSE .
+.It Ic termdata
+Toggles the display of all terminal data (in hexadecimal format).
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic verbose_encrypt
+When the
+.Ic verbose_encrypt
+toggle is
+.Dv TRUE ,
+.Nm
+prints out a message each time encryption is enabled or
+disabled.
+The initial value for this toggle is
+.Dv FALSE .
+Note:  Because of export controls, data encryption
+is not supported outside of the United States and Canada.
+.It Ic \&?
+Displays the legal
+.Ic toggle
+commands.
+.El
+.It Ic z
+Suspend
+.Nm  .
+This command only works when the user is using the
+.Xr csh 1 .
+.It Ic \&! Op Ar command
+Execute a single command in a subshell on the local
+system.
+If
+.Ar command
+is omitted, then an interactive
+subshell is invoked.
+.It Ic \&? Op Ar command
+Get help.
+With no arguments,
+.Nm
+prints a help summary.
+If a command is specified,
+.Nm
+will print the help information for just that command.
+.El
+.Sh ENVIRONMENT
+.Nm
+uses at least the
+.Ev HOME ,
+.Ev SHELL ,
+.Ev DISPLAY ,
+and
+.Ev TERM
+environment variables.
+Other environment variables may be propagated
+to the other side via the
+.Dv TELNET ENVIRON
+option.
+.Sh FILES
+.Bl -tag -width ~/.telnetrc -compact
+.It Pa ~/.telnetrc
+user customized telnet startup values
+.El
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
+IPsec support was added by WIDE/KAME project, in 1999.
+.Sh NOTES
+On some remote systems, echo has to be turned off manually when in
+\*(Lqold line by line\*(Rq mode.
+.Pp
+In \*(Lqold line by line\*(Rq mode or
+.Dv LINEMODE
+the terminal's
+.Ic eof
+character is only recognized (and sent to the remote system)
+when it is the first character on a line.
diff --git a/usr.bin/telnet/telnet.c b/usr.bin/telnet/telnet.c
new file mode 100644 (file)
index 0000000..bfc363e
--- /dev/null
@@ -0,0 +1,2648 @@
+/*     $NetBSD: telnet.c,v 1.36 2012/01/10 13:49:32 christos Exp $     */
+
+/*
+ * Copyright (c) 1988, 1990, 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[] = "@(#)telnet.c   8.4 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: telnet.c,v 1.36 2012/01/10 13:49:32 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <signal.h>
+#include <term.h>
+#include <unistd.h>
+/* By the way, we need to include curses.h before telnet.h since,
+ * among other things, telnet.h #defines 'DO', which is a variable
+ * declared in curses.h.
+ */
+
+#include <arpa/telnet.h>
+
+#include <ctype.h>
+
+#include "ring.h"
+#include "defines.h"
+#include "externs.h"
+#include "types.h"
+#include "general.h"
+
+#include <libtelnet/misc.h>
+#ifdef AUTHENTICATION
+#include <libtelnet/auth.h>
+#endif
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
+
+#define        strip(x) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x))
+
+static unsigned char   subbuffer[SUBBUFSIZE],
+                       *subpointer, *subend;    /* buffer for sub-options */
+#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_PEEK()       ((*subpointer)&0xff)
+#define        SB_EOF()        (subpointer >= subend)
+#define        SB_LEN()        (subend - subpointer)
+
+char   options[256];           /* The combined options */
+char   do_dont_resp[256];
+char   will_wont_resp[256];
+
+int
+       eight = 0,
+       autologin = 0,  /* Autologin anyone? */
+       skiprc = 0,
+       connected,
+       showoptions,
+       In3270,         /* Are we in 3270 mode? */
+       ISend,          /* trying to send network data in */
+       telnet_debug = 0,
+       crmod,
+       netdata,        /* Print out network data flow */
+       crlf,           /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+#ifdef TN3270
+       noasynchtty = 0,/* User specified "-noasynch" on command line */
+       noasynchnet = 0,/* User specified "-noasynch" on command line */
+       askedSGA = 0,   /* We have talked about suppress go ahead */
+#endif /* defined(TN3270) */
+       telnetport,
+       SYNCHing,       /* we are in TELNET SYNCH mode */
+       flushout,       /* flush output */
+       autoflush = 0,  /* flush output when interrupting? */
+       autosynch,      /* send interrupt characters with SYNCH? */
+       localflow,      /* we handle flow control locally */
+       restartany,     /* if flow control enabled, restart on any character */
+       localchars,     /* we recognize interrupt/quit */
+       donelclchars,   /* the user has set "localchars" */
+       donebinarytoggle,       /* the user has put us in binary */
+       dontlecho,      /* do we suppress local echoing right now? */
+       globalmode,
+       doaddrlookup = 1, /* do a reverse address lookup? */
+       clienteof = 0;
+
+char *prompt = 0;
+
+cc_t escape;
+cc_t rlogin;
+#ifdef KLUDGELINEMODE
+cc_t echoc;
+#endif
+
+/*
+ * Telnet receiver states for fsm
+ */
+#define        TS_DATA         0
+#define        TS_IAC          1
+#define        TS_WILL         2
+#define        TS_WONT         3
+#define        TS_DO           4
+#define        TS_DONT         5
+#define        TS_CR           6
+#define        TS_SB           7               /* sub-option collection */
+#define        TS_SE           8               /* looking for sub-option end */
+
+static int     telrcv_state;
+#ifdef OLD_ENVIRON
+unsigned char telopt_environ = TELOPT_NEW_ENVIRON;
+#else
+# define telopt_environ TELOPT_NEW_ENVIRON
+#endif
+
+jmp_buf        toplevel = { 0 };
+
+int    flushline;
+int    linemode;
+
+#ifdef KLUDGELINEMODE
+int    kludgelinemode = 1;
+#endif
+
+static void dooption(int);
+static void dontoption(int);
+static void suboption(void);
+static int telsnd(void);
+static void netclear(void);
+static void doflush(void);
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+
+Clocks clocks;
+\f
+#ifdef notdef
+Modelist modelist[] = {
+       { "telnet command mode", COMMAND_LINE },
+       { "character-at-a-time mode", 0 },
+       { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
+       { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
+       { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
+       { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
+       { "3270 mode", 0 },
+};
+#endif
+
+\f
+/*
+ * Initialize telnet environment.
+ */
+
+void
+init_telnet(void)
+{
+    env_init();
+
+    SB_CLEAR();
+    ClearArray(options);
+
+    connected = In3270 = ISend = localflow = donebinarytoggle = 0;
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+    auth_encrypt_connect(connected);
+#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
+    restartany = -1;
+
+    SYNCHing = 0;
+
+    /* Don't change NetTrace */
+
+    escape = CONTROL(']');
+    rlogin = _POSIX_VDISABLE;
+#ifdef KLUDGELINEMODE
+    echoc = CONTROL('E');
+#endif
+
+    flushline = 1;
+    telrcv_state = TS_DATA;
+}
+\f
+
+#ifdef notdef
+#include <stdarg.h>
+
+/*VARARGS*/
+static void
+printring(Ring *ring, char *format, ...)
+    va_dcl
+{
+    va_list ap;
+    char buffer[100];          /* where things go */
+    char *ptr;
+    char *string;
+    int i;
+
+    va_start(ap, format);
+
+    ptr = buffer;
+
+    while ((i = *format++) != 0) {
+       if (i == '%') {
+           i = *format++;
+           switch (i) {
+           case 'c':
+               *ptr++ = va_arg(ap, int);
+               break;
+           case 's':
+               string = va_arg(ap, char *);
+               ring_supply_data(ring, buffer, ptr-buffer);
+               ring_supply_data(ring, string, strlen(string));
+               ptr = buffer;
+               break;
+           case 0:
+               ExitString("printring: trailing %%.\n", 1);
+               /*NOTREACHED*/
+           default:
+               ExitString("printring: unknown format character.\n", 1);
+               /*NOTREACHED*/
+           }
+       } else {
+           *ptr++ = i;
+       }
+    }
+    va_end(ap);
+    ring_supply_data(ring, buffer, ptr-buffer);
+}
+#endif
+
+/*
+ * These routines are in charge of sending option negotiations
+ * to the other side.
+ *
+ * The basic idea is that we send the negotiation if either side
+ * is in disagreement as to what the current state should be.
+ */
+
+void
+send_do(int c, int init)
+{
+    if (init) {
+       if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
+                               my_want_state_is_do(c))
+           return;
+       set_my_want_state_do(c);
+       do_dont_resp[c]++;
+    }
+    NET2ADD(IAC, DO);
+    NETADD(c);
+    printoption("SENT", DO, c);
+}
+
+void
+send_dont(int c, int init)
+{
+    if (init) {
+       if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
+                               my_want_state_is_dont(c))
+           return;
+       set_my_want_state_dont(c);
+       do_dont_resp[c]++;
+    }
+    NET2ADD(IAC, DONT);
+    NETADD(c);
+    printoption("SENT", DONT, c);
+}
+
+void
+send_will(int c, int init)
+{
+    if (init) {
+       if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
+                               my_want_state_is_will(c))
+           return;
+       set_my_want_state_will(c);
+       will_wont_resp[c]++;
+    }
+    NET2ADD(IAC, WILL);
+    NETADD(c);
+    printoption("SENT", WILL, c);
+}
+
+void
+send_wont(int c, int init)
+{
+    if (init) {
+       if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
+                               my_want_state_is_wont(c))
+           return;
+       set_my_want_state_wont(c);
+       will_wont_resp[c]++;
+    }
+    NET2ADD(IAC, WONT);
+    NETADD(c);
+    printoption("SENT", WONT, c);
+}
+
+
+void
+willoption(int option)
+{
+       int new_state_ok = 0;
+
+       if (do_dont_resp[option]) {
+           --do_dont_resp[option];
+           if (do_dont_resp[option] && my_state_is_do(option))
+               --do_dont_resp[option];
+       }
+
+       if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
+
+           switch (option) {
+
+           case TELOPT_ECHO:
+#          if defined(TN3270)
+               /*
+                * The following is a pain in the rear-end.
+                * Various IBM servers (some versions of Wiscnet,
+                * possibly Fibronics/Spartacus, and who knows who
+                * else) will NOT allow us to send "DO SGA" too early
+                * in the setup proceedings.  On the other hand,
+                * 4.2 servers (telnetd) won't set SGA correctly.
+                * So, we are stuck.  Empirically (but, based on
+                * a VERY small sample), the IBM servers don't send
+                * out anything about ECHO, so we postpone our sending
+                * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
+                * DO send).
+                 */
+               {
+                   if (askedSGA == 0) {
+                       askedSGA = 1;
+                       if (my_want_state_is_dont(TELOPT_SGA))
+                           send_do(TELOPT_SGA, 1);
+                   }
+               }
+                   /* Fall through */
+           case TELOPT_EOR:
+#endif     /* defined(TN3270) */
+           case TELOPT_BINARY:
+           case TELOPT_SGA:
+               settimer(modenegotiated);
+               /* FALL THROUGH */
+           case TELOPT_STATUS:
+#ifdef AUTHENTICATION
+           case TELOPT_AUTHENTICATION:
+#ifdef ENCRYPTION
+           case TELOPT_ENCRYPT:
+#endif /* ENCRYPTION */
+#endif
+               new_state_ok = 1;
+               break;
+
+           case TELOPT_TM:
+               if (flushout)
+                   flushout = 0;
+               /*
+                * Special case for TM.  If we get back a WILL,
+                * pretend we got back a WONT.
+                */
+               set_my_want_state_dont(option);
+               set_my_state_dont(option);
+               return;                 /* Never reply to TM will's/wont's */
+
+           case TELOPT_LINEMODE:
+           default:
+               break;
+           }
+
+           if (new_state_ok) {
+               set_my_want_state_do(option);
+               send_do(option, 0);
+               setconnmode(0);         /* possibly set new tty mode */
+           } else {
+               do_dont_resp[option]++;
+               send_dont(option, 0);
+           }
+       }
+       set_my_state_do(option);
+#ifdef ENCRYPTION
+       if (option == TELOPT_ENCRYPT)
+               encrypt_send_support();
+#endif /* ENCRYPTION */
+}
+
+void
+wontoption(int option)
+{
+       if (do_dont_resp[option]) {
+           --do_dont_resp[option];
+           if (do_dont_resp[option] && my_state_is_dont(option))
+               --do_dont_resp[option];
+       }
+
+       if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
+
+           switch (option) {
+
+#ifdef KLUDGELINEMODE
+           case TELOPT_SGA:
+               if (!kludgelinemode)
+                   break;
+               /* FALL THROUGH */
+#endif
+           case TELOPT_ECHO:
+               settimer(modenegotiated);
+               break;
+
+           case TELOPT_TM:
+               if (flushout)
+                   flushout = 0;
+               set_my_want_state_dont(option);
+               set_my_state_dont(option);
+               return;         /* Never reply to TM will's/wont's */
+
+           default:
+               break;
+           }
+           set_my_want_state_dont(option);
+           if (my_state_is_do(option))
+               send_dont(option, 0);
+           setconnmode(0);                     /* Set new tty mode */
+       } else if (option == TELOPT_TM) {
+           /*
+            * Special case for TM.
+            */
+           if (flushout)
+               flushout = 0;
+           set_my_want_state_dont(option);
+       }
+       set_my_state_dont(option);
+}
+
+static void
+dooption(int option)
+{
+       int new_state_ok = 0;
+
+       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) {
+         if (my_want_state_is_wont(option)) {
+
+           switch (option) {
+
+           case TELOPT_TM:
+               /*
+                * Special case for TM.  We send a WILL, but pretend
+                * we sent WONT.
+                */
+               send_will(option, 0);
+               set_my_want_state_wont(TELOPT_TM);
+               set_my_state_wont(TELOPT_TM);
+               return;
+
+#      if defined(TN3270)
+           case TELOPT_EOR:            /* end of record */
+#      endif   /* defined(TN3270) */
+           case TELOPT_BINARY:         /* binary mode */
+           case TELOPT_NAWS:           /* window size */
+           case TELOPT_TSPEED:         /* terminal speed */
+           case TELOPT_LFLOW:          /* local flow control */
+           case TELOPT_TTYPE:          /* terminal type option */
+           case TELOPT_SGA:            /* no big deal */
+#ifdef ENCRYPTION
+           case TELOPT_ENCRYPT:        /* encryption variable option */
+#endif /* ENCRYPTION */
+               new_state_ok = 1;
+               break;
+
+           case TELOPT_NEW_ENVIRON:    /* New environment variable option */
+#ifdef OLD_ENVIRON
+               if (my_state_is_will(TELOPT_OLD_ENVIRON))
+                       send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */
+               goto env_common;
+           case TELOPT_OLD_ENVIRON:    /* Old environment variable option */
+               if (my_state_is_will(TELOPT_NEW_ENVIRON))
+                       break;          /* Don't enable if new one is in use! */
+           env_common:
+               telopt_environ = option;
+#endif
+               new_state_ok = 1;
+               break;
+
+#ifdef AUTHENTICATION
+           case TELOPT_AUTHENTICATION:
+               if (autologin)
+                       new_state_ok = 1;
+               break;
+#endif
+
+           case TELOPT_XDISPLOC:       /* X Display location */
+               if (env_getvalue((const unsigned char *)"DISPLAY"))
+                   new_state_ok = 1;
+               break;
+
+           case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+               kludgelinemode = 0;
+               send_do(TELOPT_SGA, 1);
+#endif
+               set_my_want_state_will(TELOPT_LINEMODE);
+               send_will(option, 0);
+               set_my_state_will(TELOPT_LINEMODE);
+               slc_init();
+               return;
+
+           case TELOPT_ECHO:           /* We're never going to echo... */
+           default:
+               break;
+           }
+
+           if (new_state_ok) {
+               set_my_want_state_will(option);
+               send_will(option, 0);
+               setconnmode(0);                 /* Set new tty mode */
+           } else {
+               will_wont_resp[option]++;
+               send_wont(option, 0);
+           }
+         } else {
+           /*
+            * Handle options that need more things done after the
+            * other side has acknowledged the option.
+            */
+           switch (option) {
+           case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+               kludgelinemode = 0;
+               send_do(TELOPT_SGA, 1);
+#endif
+               set_my_state_will(option);
+               slc_init();
+               send_do(TELOPT_SGA, 0);
+               return;
+           }
+         }
+       }
+       set_my_state_will(option);
+}
+
+static void
+dontoption(int 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_LINEMODE:
+               linemode = 0;   /* put us back to the default state */
+               break;
+#ifdef OLD_ENVIRON
+           case TELOPT_NEW_ENVIRON:
+               /*
+                * The new environ option wasn't recognized, try
+                * the old one.
+                */
+               send_will(TELOPT_OLD_ENVIRON, 1);
+               telopt_environ = TELOPT_OLD_ENVIRON;
+               break;
+#endif
+           }
+           /* we always accept a DONT */
+           set_my_want_state_wont(option);
+           if (my_state_is_will(option))
+               send_wont(option, 0);
+           setconnmode(0);                     /* Set new tty mode */
+       }
+       set_my_state_wont(option);
+}
+
+/*
+ * Given a buffer returned by tgetent(), this routine will turn
+ * the pipe separated list of names in the buffer into an array
+ * of pointers to null terminated names.  We toss out any bad,
+ * duplicate, or verbose names (names with spaces).
+ */
+
+static char name_unknown[] = "UNKNOWN";
+static char *unknown[] = { 0, 0 };
+
+char **
+mklist(char *buf, char *name)
+{
+       int n;
+       char c, *cp, **argvp, *cp2, **argv, **avt;
+
+       if (name) {
+               if ((int)strlen(name) > 40) {
+                       name = 0;
+                       unknown[0] = name_unknown;
+               } else {
+                       unknown[0] = name;
+                       upcase(name);
+               }
+       } else
+               unknown[0] = name_unknown;
+       /*
+        * Count up the number of names.
+        */
+       for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
+               if (*cp == '|')
+                       n++;
+       }
+       /*
+        * Allocate an array to put the name pointers into
+        */
+       argv = (char **)malloc((n+3)*sizeof(char *));
+       if (argv == 0)
+               return(unknown);
+
+       /*
+        * Fill up the array of pointers to names.
+        */
+       *argv = 0;
+       argvp = argv+1;
+       n = 0;
+       for (cp = cp2 = buf; (c = *cp);  cp++) {
+               if (c == '|' || c == ':') {
+                       *cp++ = '\0';
+                       /*
+                        * Skip entries that have spaces or are over 40
+                        * characters long.  If this is our environment
+                        * name, then put it up front.  Otherwise, as
+                        * long as this is not a duplicate name (case
+                        * insensitive) add it to the list.
+                        */
+                       if (n || (cp - cp2 > 41))
+                               ;
+                       else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
+                               *argv = cp2;
+                       else if (is_unique(cp2, argv+1, argvp))
+                               *argvp++ = cp2;
+                       if (c == ':')
+                               break;
+                       /*
+                        * Skip multiple delimiters. Reset cp2 to
+                        * the beginning of the next name. Reset n,
+                        * the flag for names with spaces.
+                        */
+                       while ((c = *cp) == '|')
+                               cp++;
+                       cp2 = cp;
+                       n = 0;
+               }
+               /*
+                * Skip entries with spaces or non-ascii values.
+                * Convert lower case letters to upper case.
+                */
+               if ((c == ' ') || !isascii(c))
+                       n = 1;
+               else if (islower((unsigned char)c))
+                       *cp = toupper((unsigned char)c);
+       }
+
+       /*
+        * Check for an old V6 2 character name.  If the second
+        * name points to the beginning of the buffer, and is
+        * only 2 characters long, move it to the end of the array.
+        */
+       if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
+               --argvp;
+               for (avt = &argv[1]; avt < argvp; avt++)
+                       *avt = *(avt+1);
+               *argvp++ = buf;
+       }
+
+       /*
+        * Duplicate last name, for TTYPE option, and null
+        * terminate the array.  If we didn't find a match on
+        * our terminal name, put that name at the beginning.
+        */
+       cp = *(argvp-1);
+       *argvp++ = cp;
+       *argvp = 0;
+
+       if (*argv == 0) {
+               if (name)
+                       *argv = name;
+               else {
+                       --argvp;
+                       for (avt = argv; avt < argvp; avt++)
+                               *avt = *(avt+1);
+               }
+       }
+       if (*argv)
+               return(argv);
+       else
+               return(unknown);
+}
+
+int
+is_unique(char *name, char **as, char **ae)
+{
+       char **ap;
+       int n;
+
+       n = strlen(name) + 1;
+       for (ap = as; ap < ae; ap++)
+               if (strncasecmp(*ap, name, n) == 0)
+                       return(0);
+       return (1);
+}
+
+#ifdef TERMCAP
+char *termbuf;
+
+/*ARGSUSED*/
+int
+setupterm(char *tname, int fd, int *errp)
+{
+       char zz[1024], *zz_ptr;
+       char *ext_tc, *newptr;
+       size_t len;
+       
+       if ((termbuf = malloc(1024)) == NULL)
+               goto error;
+       
+       if (tgetent(termbuf, tname) == 1) {
+               /* check for ZZ capability, indicating termcap truncated */
+               zz_ptr = zz;
+               if (tgetstr("ZZ", &zz_ptr) != NULL) {
+                       /* it was, fish back the full termcap */
+                       sscanf(zz, "%p", &ext_tc);
+                       len = strlen(ext_tc) + 1;
+                       if ((newptr = realloc(termbuf, len)) == NULL)
+                               goto error;
+
+                       memcpy(newptr, ext_tc, len);
+                       termbuf = newptr;
+               }
+
+               if (errp)
+                       *errp = 1;
+               return(0);
+       }
+  error:
+       if (errp)
+               *errp = 0;
+       return(-1);
+}
+#else
+#define        termbuf ttytype
+extern char ttytype[];
+#endif
+
+int resettermname = 1;
+
+char *
+gettermname(void)
+{
+       char *tname;
+       static char **tnamep = 0;
+       static char **next;
+       int err;
+
+       if (resettermname) {
+               resettermname = 0;
+               if (tnamep && tnamep != unknown)
+                       free(tnamep);
+               if ((tname = (char *)env_getvalue((const unsigned char *)"TERM")) &&
+                               (setupterm(tname, 1, &err) == 0)) {
+                       tnamep = mklist(termbuf, tname);
+               } else {
+                       if (tname && ((int)strlen(tname) <= 40)) {
+                               unknown[0] = tname;
+                               upcase(tname);
+                       } else
+                               unknown[0] = name_unknown;
+                       tnamep = unknown;
+               }
+               next = tnamep;
+       }
+       if (*next == 0)
+               next = tnamep;
+       return(*next++);
+}
+/*
+ * suboption()
+ *
+ *     Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ *     Currently we recognize:
+ *
+ *             Terminal type, send request.
+ *             Terminal speed (send request).
+ *             Local flow control (is request).
+ *             Linemode
+ */
+
+static void
+suboption(void)
+{
+    unsigned char subchar;
+
+    printsub('<', subbuffer, SB_LEN()+2);
+    switch (subchar = SB_GET()) {
+    case TELOPT_TTYPE:
+       if (my_want_state_is_wont(TELOPT_TTYPE))
+           return;
+       if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
+           return;
+       } else {
+           char *name;
+           unsigned char temp[50];
+           int len;
+
+#ifdef TN3270
+           if (tn3270_ttype()) {
+               return;
+           }
+#endif /* defined(TN3270) */
+           name = gettermname();
+           len = strlen(name) + 4 + 2;
+           if (len < NETROOM()) {
+               sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
+                               TELQUAL_IS, name, IAC, SE);
+               ring_supply_data(&netoring, temp, len);
+               printsub('>', &temp[2], len-2);
+           } else {
+               ExitString("No room in buffer for terminal type.\n", 1);
+               /*NOTREACHED*/
+           }
+       }
+       break;
+    case TELOPT_TSPEED:
+       if (my_want_state_is_wont(TELOPT_TSPEED))
+           return;
+       if (SB_EOF())
+           return;
+       if (SB_GET() == TELQUAL_SEND) {
+           long osp, isp;
+           unsigned char temp[50];
+           int len;
+
+           TerminalSpeeds(&isp, &osp);
+
+           sprintf((char *)temp, "%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED,
+                   TELQUAL_IS, osp, isp, IAC, SE);
+           len = strlen((char *)temp+4) + 4;   /* temp[3] is 0 ... */
+
+           if (len < NETROOM()) {
+               ring_supply_data(&netoring, temp, len);
+               printsub('>', temp+2, len - 2);
+           }
+/*@*/      else printf("lm_will: not enough room in buffer\n");
+       }
+       break;
+    case TELOPT_LFLOW:
+       if (my_want_state_is_wont(TELOPT_LFLOW))
+           return;
+       if (SB_EOF())
+           return;
+       switch(SB_GET()) {
+       case LFLOW_RESTART_ANY:
+           restartany = 1;
+           break;
+       case LFLOW_RESTART_XON:
+           restartany = 0;
+           break;
+       case LFLOW_ON:
+           localflow = 1;
+           break;
+       case LFLOW_OFF:
+           localflow = 0;
+           break;
+       default:
+           return;
+       }
+       setcommandmode();
+       setconnmode(0);
+       break;
+
+    case TELOPT_LINEMODE:
+       if (my_want_state_is_wont(TELOPT_LINEMODE))
+           return;
+       if (SB_EOF())
+           return;
+       switch (SB_GET()) {
+       case WILL:
+           lm_will(subpointer, SB_LEN());
+           break;
+       case WONT:
+           lm_wont(subpointer, SB_LEN());
+           break;
+       case DO:
+           lm_do(subpointer, SB_LEN());
+           break;
+       case DONT:
+           lm_dont(subpointer, SB_LEN());
+           break;
+       case LM_SLC:
+           slc(subpointer, SB_LEN());
+           break;
+       case LM_MODE:
+           lm_mode(subpointer, SB_LEN(), 0);
+           break;
+       default:
+           break;
+       }
+       break;
+
+#ifdef OLD_ENVIRON
+    case TELOPT_OLD_ENVIRON:
+#endif
+    case TELOPT_NEW_ENVIRON:
+       if (SB_EOF())
+           return;
+       switch(SB_PEEK()) {
+       case TELQUAL_IS:
+       case TELQUAL_INFO:
+           if (my_want_state_is_dont(subchar))
+               return;
+           break;
+       case TELQUAL_SEND:
+           if (my_want_state_is_wont(subchar)) {
+               return;
+           }
+           break;
+       default:
+           return;
+       }
+       env_opt(subpointer, SB_LEN());
+       break;
+
+    case TELOPT_XDISPLOC:
+       if (my_want_state_is_wont(TELOPT_XDISPLOC))
+           return;
+       if (SB_EOF())
+           return;
+       if (SB_GET() == TELQUAL_SEND) {
+           unsigned char temp[50], *dp;
+           int len;
+
+           if ((dp = env_getvalue((const unsigned char *)"DISPLAY")) == NULL) {
+               /*
+                * Something happened, we no longer have a DISPLAY
+                * variable.  So, turn off the option.
+                */
+               send_wont(TELOPT_XDISPLOC, 1);
+               break;
+           }
+           sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
+                   TELQUAL_IS, dp, IAC, SE);
+           len = strlen((char *)temp+4) + 4;   /* temp[3] is 0 ... */
+
+           if (len < NETROOM()) {
+               ring_supply_data(&netoring, temp, len);
+               printsub('>', temp+2, len - 2);
+           }
+/*@*/      else printf("lm_will: not enough room in buffer\n");
+       }
+       break;
+
+#ifdef AUTHENTICATION
+       case TELOPT_AUTHENTICATION: {
+               if (!autologin)
+                       break;
+               if (SB_EOF())
+                       return;
+               switch(SB_GET()) {
+               case TELQUAL_IS:
+                       if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
+                               return;
+                       auth_is(subpointer, SB_LEN());
+                       break;
+               case TELQUAL_SEND:
+                       if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
+                               return;
+                       auth_send(subpointer, SB_LEN());
+                       break;
+               case TELQUAL_REPLY:
+                       if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
+                               return;
+                       auth_reply(subpointer, SB_LEN());
+                       break;
+               case TELQUAL_NAME:
+                       if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
+                               return;
+                       auth_name(subpointer, SB_LEN());
+                       break;
+               }
+       }
+       break;
+#endif
+#ifdef ENCRYPTION
+    case TELOPT_ENCRYPT:
+       if (SB_EOF())
+               return;
+       switch(SB_GET()) {
+       case ENCRYPT_START:
+               if (my_want_state_is_dont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_start(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_END:
+               if (my_want_state_is_dont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_end();
+               break;
+       case ENCRYPT_SUPPORT:
+               if (my_want_state_is_wont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_support(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_REQSTART:
+               if (my_want_state_is_wont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_request_start(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_REQEND:
+               if (my_want_state_is_wont(TELOPT_ENCRYPT))
+                       return;
+               /*
+                * 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_IS:
+               if (my_want_state_is_dont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_is(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_REPLY:
+               if (my_want_state_is_wont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_reply(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_ENC_KEYID:
+               if (my_want_state_is_dont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_enc_keyid(subpointer, SB_LEN());
+               break;
+       case ENCRYPT_DEC_KEYID:
+               if (my_want_state_is_wont(TELOPT_ENCRYPT))
+                       return;
+               encrypt_dec_keyid(subpointer, SB_LEN());
+               break;
+       default:
+               break;
+       }
+       break;
+#endif /* ENCRYPTION */
+    default:
+       break;
+    }
+}
+
+static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
+
+void
+lm_will(unsigned char *cmd, int len)
+{
+    if (len < 1) {
+/*@*/  printf("lm_will: no command!!!\n");     /* Should not happen... */
+       return;
+    }
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:       /* We shouldn't ever get this... */
+    default:
+       str_lm[3] = DONT;
+       str_lm[4] = cmd[0];
+       if ((size_t)NETROOM() > sizeof(str_lm)) {
+           ring_supply_data(&netoring, str_lm, sizeof(str_lm));
+           printsub('>', &str_lm[2], sizeof(str_lm)-2);
+       }
+/*@*/  else printf("lm_will: not enough room in buffer\n");
+       break;
+    }
+}
+
+void
+lm_wont(unsigned char *cmd, int len)
+{
+    if (len < 1) {
+/*@*/  printf("lm_wont: no command!!!\n");     /* Should not happen... */
+       return;
+    }
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:       /* We shouldn't ever get this... */
+    default:
+       /* We are always DONT, so don't respond */
+       return;
+    }
+}
+
+void
+lm_do(unsigned char *cmd, int len)
+{
+    if (len < 1) {
+/*@*/  printf("lm_do: no command!!!\n");       /* Should not happen... */
+       return;
+    }
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:
+    default:
+       str_lm[3] = WONT;
+       str_lm[4] = cmd[0];
+       if ((size_t)NETROOM() > sizeof(str_lm)) {
+           ring_supply_data(&netoring, str_lm, sizeof(str_lm));
+           printsub('>', &str_lm[2], sizeof(str_lm)-2);
+       }
+/*@*/  else printf("lm_do: not enough room in buffer\n");
+       break;
+    }
+}
+
+void
+lm_dont(unsigned char *cmd, int len)
+{
+    if (len < 1) {
+/*@*/  printf("lm_dont: no command!!!\n");     /* Should not happen... */
+       return;
+    }
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:
+    default:
+       /* we are always WONT, so don't respond */
+       break;
+    }
+}
+
+static unsigned char str_lm_mode[] = {
+       IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
+};
+
+void
+lm_mode(unsigned char *cmd, int len, int init)
+{
+       if (len != 1)
+               return;
+       if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
+               return;
+       if (*cmd&MODE_ACK)
+               return;
+       linemode = *cmd&(MODE_MASK&~MODE_ACK);
+       str_lm_mode[4] = linemode;
+       if (!init)
+           str_lm_mode[4] |= MODE_ACK;
+       if ((size_t)NETROOM() > sizeof(str_lm_mode)) {
+           ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
+           printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
+       }
+/*@*/  else printf("lm_mode: not enough room in buffer\n");
+       setconnmode(0); /* set changed mode */
+}
+
+\f
+
+/*
+ * slc()
+ * Handle special character suboption of LINEMODE.
+ */
+
+struct spc {
+       cc_t val;
+       cc_t *valp;
+       char flags;     /* Current flags & level */
+       char mylevel;   /* Maximum level & flags */
+} spc_data[NSLC+1];
+
+#define SLC_IMPORT     0
+#define        SLC_EXPORT      1
+#define SLC_RVALUE     2
+static int slc_mode = SLC_EXPORT;
+
+void
+slc_init(void)
+{
+       struct spc *spcp;
+
+       localchars = 1;
+       for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
+               spcp->val = 0;
+               spcp->valp = 0;
+               spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
+       }
+
+#define        initfunc(func, flags) { \
+                                       spcp = &spc_data[func]; \
+                                       if ((spcp->valp = tcval(func)) != NULL){ \
+                                           spcp->val = *spcp->valp; \
+                                           spcp->mylevel = SLC_VARIABLE|flags; \
+                                       } else { \
+                                           spcp->val = 0; \
+                                           spcp->mylevel = SLC_DEFAULT; \
+                                       } \
+                                   }
+
+       initfunc(SLC_SYNCH, 0);
+       /* No BRK */
+       initfunc(SLC_AO, 0);
+       initfunc(SLC_AYT, 0);
+       /* No EOR */
+       initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
+       initfunc(SLC_EOF, 0);
+       initfunc(SLC_SUSP, SLC_FLUSHIN);
+       initfunc(SLC_EC, 0);
+       initfunc(SLC_EL, 0);
+       initfunc(SLC_EW, 0);
+       initfunc(SLC_RP, 0);
+       initfunc(SLC_LNEXT, 0);
+       initfunc(SLC_XON, 0);
+       initfunc(SLC_XOFF, 0);
+       initfunc(SLC_FORW1, 0);
+       initfunc(SLC_FORW2, 0);
+       /* No FORW2 */
+
+       initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
+#undef initfunc
+
+       if (slc_mode == SLC_EXPORT)
+               slc_export();
+       else
+               slc_import(1);
+
+}
+
+void
+slcstate(void)
+{
+    printf("Special characters are %s values\n",
+               slc_mode == SLC_IMPORT ? "remote default" :
+               slc_mode == SLC_EXPORT ? "local" :
+                                        "remote");
+}
+
+void
+slc_mode_export(int n)
+{
+    slc_mode = SLC_EXPORT;
+    if (my_state_is_will(TELOPT_LINEMODE))
+       slc_export();
+}
+
+void
+slc_mode_import(int def)
+{
+    slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
+    if (my_state_is_will(TELOPT_LINEMODE))
+       slc_import(def);
+}
+
+unsigned char slc_import_val[] = {
+       IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
+};
+unsigned char slc_import_def[] = {
+       IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
+};
+
+void
+slc_import(int def)
+{
+    if ((size_t)NETROOM() > sizeof(slc_import_val)) {
+       if (def) {
+           ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
+           printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
+       } else {
+           ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
+           printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
+       }
+    }
+/*@*/ else printf("slc_import: not enough room\n");
+}
+
+void
+slc_export(void)
+{
+    struct spc *spcp;
+
+    TerminalDefaultChars();
+
+    slc_start_reply();
+    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+       if (spcp->mylevel != SLC_NOSUPPORT) {
+           if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+               spcp->flags = SLC_NOSUPPORT;
+           else
+               spcp->flags = spcp->mylevel;
+           if (spcp->valp)
+               spcp->val = *spcp->valp;
+           slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+       }
+    }
+    slc_end_reply();
+    (void)slc_update();
+    setconnmode(1);    /* Make sure the character values are set */
+}
+
+void
+slc(unsigned char *cp, int len)
+{
+       struct spc *spcp;
+       int func,level;
+
+       slc_start_reply();
+
+       for (; len >= 3; len -=3, cp +=3) {
+
+               func = cp[SLC_FUNC];
+
+               if (func == 0) {
+                       /*
+                        * Client side: always ignore 0 function.
+                        */
+                       continue;
+               }
+               if (func > NSLC) {
+                       if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
+                               slc_add_reply(func, SLC_NOSUPPORT, 0);
+                       continue;
+               }
+
+               spcp = &spc_data[func];
+
+               level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
+
+               if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
+                   ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
+                       continue;
+               }
+
+               if (level == (SLC_DEFAULT|SLC_ACK)) {
+                       /*
+                        * This is an error condition, the SLC_ACK
+                        * bit should never be set for the SLC_DEFAULT
+                        * level.  Our best guess to recover is to
+                        * ignore the SLC_ACK bit.
+                        */
+                       cp[SLC_FLAGS] &= ~SLC_ACK;
+               }
+
+               if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
+                       spcp->val = (cc_t)cp[SLC_VALUE];
+                       spcp->flags = cp[SLC_FLAGS];    /* include SLC_ACK */
+                       continue;
+               }
+
+               level &= ~SLC_ACK;
+
+               if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
+                       spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
+                       spcp->val = (cc_t)cp[SLC_VALUE];
+               }
+               if (level == SLC_DEFAULT) {
+                       if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
+                               spcp->flags = spcp->mylevel;
+                       else
+                               spcp->flags = SLC_NOSUPPORT;
+               }
+               slc_add_reply(func, spcp->flags, spcp->val);
+       }
+       slc_end_reply();
+       if (slc_update())
+               setconnmode(1); /* set the  new character values */
+}
+
+void
+slc_check(void)
+{
+    struct spc *spcp;
+
+    slc_start_reply();
+    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+       if (spcp->valp && spcp->val != *spcp->valp) {
+           spcp->val = *spcp->valp;
+           if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+               spcp->flags = SLC_NOSUPPORT;
+           else
+               spcp->flags = spcp->mylevel;
+           slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+       }
+    }
+    slc_end_reply();
+    setconnmode(1);
+}
+
+
+unsigned char slc_reply[128];
+unsigned char *slc_replyp;
+
+void
+slc_start_reply(void)
+{
+       slc_replyp = slc_reply;
+       *slc_replyp++ = IAC;
+       *slc_replyp++ = SB;
+       *slc_replyp++ = TELOPT_LINEMODE;
+       *slc_replyp++ = LM_SLC;
+}
+
+void
+slc_add_reply(unsigned int func, unsigned int flags, cc_t value)
+{
+       if ((size_t)(slc_replyp - slc_reply) + 6 > sizeof(slc_reply))
+               return;
+       if ((*slc_replyp++ = func) == IAC)
+               *slc_replyp++ = IAC;
+       if ((*slc_replyp++ = flags) == IAC)
+               *slc_replyp++ = IAC;
+       if ((*slc_replyp++ = (unsigned char)value) == IAC)
+               *slc_replyp++ = IAC;
+}
+
+void
+slc_end_reply(void)
+{
+    int len;
+
+    len = slc_replyp - slc_reply;
+    if (len <= 4 || ((size_t)len + 2 > sizeof(slc_reply)))
+       return;
+    *slc_replyp++ = IAC;
+    *slc_replyp++ = SE;
+    len += 2;
+    if (NETROOM() > len) {
+       ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
+       printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
+    }
+/*@*/else printf("slc_end_reply: not enough room\n");
+}
+
+int
+slc_update(void)
+{
+       struct spc *spcp;
+       int need_update = 0;
+
+       for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+               if (!(spcp->flags&SLC_ACK))
+                       continue;
+               spcp->flags &= ~SLC_ACK;
+               if (spcp->valp && (*spcp->valp != spcp->val)) {
+                       *spcp->valp = spcp->val;
+                       need_update = 1;
+               }
+       }
+       return(need_update);
+}
+
+#ifdef OLD_ENVIRON
+# ifdef        ENV_HACK
+/*
+ * Earlier version of telnet/telnetd from the BSD code had
+ * the definitions of VALUE and VAR reversed.  To ensure
+ * maximum interoperability, we assume that the server is
+ * an older BSD server, until proven otherwise.  The newer
+ * BSD servers should be able to handle either definition,
+ * so it is better to use the wrong values if we don't
+ * know what type of server it is.
+ */
+int env_auto = 1;
+int old_env_var = OLD_ENV_VAR;
+int old_env_value = OLD_ENV_VALUE;
+# else
+#  define old_env_var OLD_ENV_VAR
+#  define old_env_value OLD_ENV_VALUE
+# endif
+#endif
+
+void
+env_opt(unsigned char *buf, int len)
+{
+       unsigned char *ep = 0, *epc = 0;
+       int i;
+
+       switch(buf[0]&0xff) {
+       case TELQUAL_SEND:
+               env_opt_start();
+               if (len == 1) {
+                       env_opt_add(NULL);
+               } else for (i = 1; i < len; i++) {
+                       switch (buf[i]&0xff) {
+#ifdef OLD_ENVIRON
+                       case OLD_ENV_VAR:
+# ifdef        ENV_HACK
+                               if (telopt_environ == TELOPT_OLD_ENVIRON
+                                   && env_auto) {
+                                       /* Server has the same definitions */
+                                       old_env_var = OLD_ENV_VAR;
+                                       old_env_value = OLD_ENV_VALUE;
+                               }
+                               /* FALL THROUGH */
+# endif
+                       case OLD_ENV_VALUE:
+                               /*
+                                * Although OLD_ENV_VALUE is not legal, we will
+                                * still recognize it, just in case it is an
+                                * old server that has VAR & VALUE mixed up...
+                                */
+                               /* FALL THROUGH */
+#else
+                       case NEW_ENV_VAR:
+#endif
+                       case ENV_USERVAR:
+                               if (ep) {
+                                       *epc = 0;
+                                       env_opt_add(ep);
+                               }
+                               ep = epc = &buf[i+1];
+                               break;
+                       case ENV_ESC:
+                               i++;
+                               /*FALL THROUGH*/
+                       default:
+                               if (epc)
+                                       *epc++ = buf[i];
+                               break;
+                       }
+               }
+               if (ep) {
+                       *epc = 0;
+                       env_opt_add(ep);
+               }
+               env_opt_end(1);
+               break;
+
+       case TELQUAL_IS:
+       case TELQUAL_INFO:
+               /* Ignore for now.  We shouldn't get it anyway. */
+               break;
+
+       default:
+               break;
+       }
+}
+
+#define        OPT_REPLY_SIZE  256
+unsigned char *opt_reply;
+unsigned char *opt_replyp;
+unsigned char *opt_replyend;
+
+void
+env_opt_start(void)
+{
+       unsigned char *p;
+
+       if (opt_reply) {
+               p = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
+               if (p == NULL)
+                       free(opt_reply);
+       } else
+               p = (unsigned char *)malloc(OPT_REPLY_SIZE);
+       opt_reply = p;
+       if (opt_reply == NULL) {
+/*@*/          printf("env_opt_start: malloc()/realloc() failed!!!\n");
+               opt_reply = opt_replyp = opt_replyend = NULL;
+               return;
+       }
+       opt_replyp = opt_reply;
+       opt_replyend = opt_reply + OPT_REPLY_SIZE;
+       *opt_replyp++ = IAC;
+       *opt_replyp++ = SB;
+       *opt_replyp++ = telopt_environ;
+       *opt_replyp++ = TELQUAL_IS;
+}
+
+void
+env_opt_start_info(void)
+{
+       env_opt_start();
+       if (opt_replyp)
+           opt_replyp[-1] = TELQUAL_INFO;
+}
+
+void
+env_opt_add(unsigned char *ep)
+{
+       unsigned char *vp, c;
+       unsigned int len, olen, elen;
+
+       if (opt_reply == NULL)          /*XXX*/
+               return;                 /*XXX*/
+
+       if (ep == NULL || *ep == '\0') {
+               /* Send user defined variables first. */
+               env_default(1, 0);
+               while ((ep = env_default(0, 0)) != NULL)
+                       env_opt_add(ep);
+
+               /* Now add the list of well know variables.  */
+               env_default(1, 1);
+               while ((ep = env_default(0, 1)) != NULL)
+                       env_opt_add(ep);
+               return;
+       }
+       vp = env_getvalue(ep);
+       elen = 2 * (vp ? strlen((char *)vp) : 0) +
+               2 * strlen((char *)ep) + 6;
+       if ((unsigned int)(opt_replyend - opt_replyp) < elen)
+       {
+               unsigned char *p;
+               len = opt_replyend - opt_reply + elen;
+               olen = opt_replyp - opt_reply;
+               p = (unsigned char *)realloc(opt_reply, len);
+               if (p == NULL)
+                       free(opt_reply);
+               opt_reply = p;
+               if (opt_reply == NULL) {
+/*@*/                  printf("env_opt_add: realloc() failed!!!\n");
+                       opt_reply = opt_replyp = opt_replyend = NULL;
+                       return;
+               }
+               opt_replyp = opt_reply + olen;
+               opt_replyend = opt_reply + len;
+       }
+       if (opt_welldefined(ep))
+#ifdef OLD_ENVIRON
+               if (telopt_environ == TELOPT_OLD_ENVIRON)
+                       *opt_replyp++ = old_env_var;
+               else
+#endif
+                       *opt_replyp++ = NEW_ENV_VAR;
+       else
+               *opt_replyp++ = ENV_USERVAR;
+       for (;;) {
+               while ((c = *ep++) != '\0') {
+                       switch(c&0xff) {
+                       case IAC:
+                               *opt_replyp++ = IAC;
+                               break;
+                       case NEW_ENV_VAR:
+                       case NEW_ENV_VALUE:
+                       case ENV_ESC:
+                       case ENV_USERVAR:
+                               *opt_replyp++ = ENV_ESC;
+                               break;
+                       }
+                       *opt_replyp++ = c;
+               }
+               if ((ep = vp) != NULL) {
+#ifdef OLD_ENVIRON
+                       if (telopt_environ == TELOPT_OLD_ENVIRON)
+                               *opt_replyp++ = old_env_value;
+                       else
+#endif
+                               *opt_replyp++ = NEW_ENV_VALUE;
+                       vp = NULL;
+               } else
+                       break;
+       }
+}
+
+int
+opt_welldefined(const char *ep)
+{
+       if ((strcmp(ep, "USER") == 0) ||
+           (strcmp(ep, "DISPLAY") == 0) ||
+           (strcmp(ep, "PRINTER") == 0) ||
+           (strcmp(ep, "SYSTEMTYPE") == 0) ||
+           (strcmp(ep, "JOB") == 0) ||
+           (strcmp(ep, "ACCT") == 0))
+               return(1);
+       return(0);
+}
+void
+env_opt_end(int emptyok)
+{
+       int len;
+
+       len = opt_replyp - opt_reply + 2;
+       if (emptyok || len > 6) {
+               *opt_replyp++ = IAC;
+               *opt_replyp++ = SE;
+               if (NETROOM() > len) {
+                       ring_supply_data(&netoring, opt_reply, len);
+                       printsub('>', &opt_reply[2], len - 2);
+               }
+/*@*/          else printf("slc_end_reply: not enough room\n");
+       }
+       if (opt_reply) {
+               free(opt_reply);
+               opt_reply = opt_replyp = opt_replyend = NULL;
+       }
+}
+
+\f
+
+int
+telrcv(void)
+{
+    int c;
+    int scc;
+    unsigned char *sbp = NULL;
+    int count;
+    int returnValue = 0;
+
+    scc = 0;
+    count = 0;
+    while (TTYROOM() > 2) {
+       if (scc == 0) {
+           if (count) {
+               ring_consumed(&netiring, count);
+               returnValue = 1;
+               count = 0;
+           }
+           sbp = netiring.consume;
+           scc = ring_full_consecutive(&netiring);
+           if (scc == 0) {
+               /* No more data coming in */
+               break;
+           }
+       }
+
+       c = *sbp++ & 0xff, scc--; count++;
+#ifdef ENCRYPTION
+       if (decrypt_input)
+               c = (*decrypt_input)(c);
+#endif /* ENCRYPTION */
+
+       switch (telrcv_state) {
+
+       case TS_CR:
+           telrcv_state = TS_DATA;
+           if (c == '\0') {
+               break;  /* Ignore \0 after CR */
+           }
+           else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
+               TTYADD(c);
+               break;
+           }
+           /* Else, fall through */
+
+       case TS_DATA:
+           if (c == IAC) {
+               telrcv_state = TS_IAC;
+               break;
+           }
+#          if defined(TN3270)
+           if (In3270) {
+               *Ifrontp++ = c;
+               while (scc > 0) {
+                   c = *sbp++ & 0377, scc--; count++;
+#ifdef ENCRYPTION
+                   if (decrypt_input)
+                       c = (*decrypt_input)(c);
+#endif /* ENCRYPTION */
+                   if (c == IAC) {
+                       telrcv_state = TS_IAC;
+                       break;
+                   }
+                   *Ifrontp++ = c;
+               }
+           } else
+#          endif /* defined(TN3270) */
+                   /*
+                    * The 'crmod' hack (see following) is needed
+                    * since we can't * set CRMOD on output only.
+                    * Machines like MULTICS like to send \r without
+                    * \n; since we must turn off CRMOD to get proper
+                    * input, the mapping is done here (sigh).
+                    */
+           if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
+               if (scc > 0) {
+                   c = *sbp&0xff;
+#ifdef ENCRYPTION
+                   if (decrypt_input)
+                       c = (*decrypt_input)(c);
+#endif /* ENCRYPTION */
+                   if (c == 0) {
+                       sbp++, scc--; count++;
+                       /* a "true" CR */
+                       TTYADD('\r');
+                   } else if (my_want_state_is_dont(TELOPT_ECHO) &&
+                                       (c == '\n')) {
+                       sbp++, scc--; count++;
+                       TTYADD('\n');
+                   } else {
+#ifdef ENCRYPTION
+                       if (decrypt_input)
+                           (*decrypt_input)(-1);
+#endif /* ENCRYPTION */
+
+                       TTYADD('\r');
+                       if (crmod) {
+                               TTYADD('\n');
+                       }
+                   }
+               } else {
+                   telrcv_state = TS_CR;
+                   TTYADD('\r');
+                   if (crmod) {
+                           TTYADD('\n');
+                   }
+               }
+           } else {
+               TTYADD(c);
+           }
+           continue;
+
+       case TS_IAC:
+process_iac:
+           switch (c) {
+
+           case WILL:
+               telrcv_state = TS_WILL;
+               continue;
+
+           case WONT:
+               telrcv_state = TS_WONT;
+               continue;
+
+           case DO:
+               telrcv_state = TS_DO;
+               continue;
+
+           case DONT:
+               telrcv_state = TS_DONT;
+               continue;
+
+           case DM:
+                   /*
+                    * We may have missed an urgent notification,
+                    * so make sure we flush whatever is in the
+                    * buffer currently.
+                    */
+               printoption("RCVD", IAC, DM);
+               SYNCHing = 1;
+               (void) ttyflush(1);
+               SYNCHing = stilloob();
+               settimer(gotDM);
+               break;
+
+           case SB:
+               SB_CLEAR();
+               telrcv_state = TS_SB;
+               continue;
+
+#          if defined(TN3270)
+           case EOR:
+               if (In3270) {
+                   if (Ibackp == Ifrontp) {
+                       Ibackp = Ifrontp = Ibuf;
+                       ISend = 0;      /* should have been! */
+                   } else {
+                       Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
+                       ISend = 1;
+                   }
+               }
+               printoption("RCVD", IAC, EOR);
+               break;
+#          endif /* defined(TN3270) */
+
+           case IAC:
+#          if !defined(TN3270)
+               TTYADD(IAC);
+#          else /* !defined(TN3270) */
+               if (In3270) {
+                   *Ifrontp++ = IAC;
+               } else {
+                   TTYADD(IAC);
+               }
+#          endif /* !defined(TN3270) */
+               break;
+
+           case NOP:
+           case GA:
+           default:
+               printoption("RCVD", IAC, c);
+               break;
+           }
+           telrcv_state = TS_DATA;
+           continue;
+
+       case TS_WILL:
+           printoption("RCVD", WILL, c);
+           willoption(c);
+           SetIn3270();
+           telrcv_state = TS_DATA;
+           continue;
+
+       case TS_WONT:
+           printoption("RCVD", WONT, c);
+           wontoption(c);
+           SetIn3270();
+           telrcv_state = TS_DATA;
+           continue;
+
+       case TS_DO:
+           printoption("RCVD", DO, c);
+           dooption(c);
+           SetIn3270();
+           if (c == TELOPT_NAWS) {
+               sendnaws();
+           } else if (c == TELOPT_LFLOW) {
+               localflow = 1;
+               setcommandmode();
+               setconnmode(0);
+           }
+           telrcv_state = TS_DATA;
+           continue;
+
+       case TS_DONT:
+           printoption("RCVD", DONT, c);
+           dontoption(c);
+           flushline = 1;
+           setconnmode(0);     /* set new tty mode (maybe) */
+           SetIn3270();
+           telrcv_state = TS_DATA;
+           continue;
+
+       case TS_SB:
+           if (c == IAC) {
+               telrcv_state = TS_SE;
+           } else {
+               SB_ACCUM(c);
+           }
+           continue;
+
+       case TS_SE:
+           if (c != SE) {
+               if (c != IAC) {
+                   /*
+                    * This is an error.  We only expect to get
+                    * "IAC IAC" or "IAC SE".  Several things may
+                    * have happened.  An IAC was not doubled, the
+                    * IAC SE was left off, or another option got
+                    * inserted into the suboption are all possibilities.
+                    * If we assume that the IAC was not doubled,
+                    * and really the IAC SE was left off, we could
+                    * get into an infinite loop here.  So, instead,
+                    * we terminate the suboption, and process the
+                    * partial suboption if we can.
+                    */
+                   SB_ACCUM(IAC);
+                   SB_ACCUM(c);
+                   subpointer -= 2;
+                   SB_TERM();
+
+                   printoption("In SUBOPTION processing, RCVD", IAC, c);
+                   suboption();        /* handle sub-option */
+                   SetIn3270();
+                   telrcv_state = TS_IAC;
+                   goto process_iac;
+               }
+               SB_ACCUM(c);
+               telrcv_state = TS_SB;
+           } else {
+               SB_ACCUM(IAC);
+               SB_ACCUM(SE);
+               subpointer -= 2;
+               SB_TERM();
+               suboption();    /* handle sub-option */
+               SetIn3270();
+               telrcv_state = TS_DATA;
+           }
+       }
+    }
+    if (count)
+       ring_consumed(&netiring, count);
+    return returnValue||count;
+}
+
+static int bol = 1, local = 0;
+
+int
+rlogin_susp(void)
+{
+    if (local) {
+       local = 0;
+       bol = 1;
+       command(0, "z\n", 2);
+       return(1);
+    }
+    return(0);
+}
+
+static int
+telsnd(void)
+{
+    int tcc;
+    int count;
+    int returnValue = 0;
+    unsigned char *tbp = NULL;
+
+    tcc = 0;
+    count = 0;
+    while (NETROOM() > 2) {
+       int sc;
+       int c;
+
+       if (tcc == 0) {
+           if (count) {
+               ring_consumed(&ttyiring, count);
+               returnValue = 1;
+               count = 0;
+           }
+           tbp = ttyiring.consume;
+           tcc = ring_full_consecutive(&ttyiring);
+           if (tcc == 0) {
+               break;
+           }
+       }
+       c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
+       if (rlogin != _POSIX_VDISABLE) {
+               if (bol) {
+                       bol = 0;
+                       if (sc == rlogin) {
+                               local = 1;
+                               continue;
+                       }
+               } else if (local) {
+                       local = 0;
+                       if (sc == '.' || c == termEofChar) {
+                               bol = 1;
+                               command(0, "close\n", 6);
+                               continue;
+                       }
+                       if (sc == termSuspChar) {
+                               bol = 1;
+                               command(0, "z\n", 2);
+                               continue;
+                       }
+                       if (sc == escape) {
+                               command(0, (char *)tbp, tcc);
+                               bol = 1;
+                               count += tcc;
+                               tcc = 0;
+                               flushline = 1;
+                               break;
+                       }
+                       if (sc != rlogin) {
+                               ++tcc;
+                               --tbp;
+                               --count;
+                               c = sc = rlogin;
+                       }
+               }
+               if ((sc == '\n') || (sc == '\r'))
+                       bol = 1;
+       } else if (sc == escape && escape != _POSIX_VDISABLE) {
+           /*
+            * Double escape is a pass through of a single escape character.
+            */
+           if (tcc && strip(*tbp) == escape) {
+               tbp++;
+               tcc--;
+               count++;
+               bol = 0;
+           } else {
+               command(0, (char *)tbp, tcc);
+               bol = 1;
+               count += tcc;
+               tcc = 0;
+               flushline = 1;
+               break;
+           }
+       } else
+           bol = 0;
+#ifdef KLUDGELINEMODE
+       if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
+           if (tcc > 0 && strip(*tbp) == echoc) {
+               tcc--; tbp++; count++;
+           } else {
+               dontlecho = !dontlecho;
+               settimer(echotoggle);
+               setconnmode(0);
+               flushline = 1;
+               break;
+           }
+       }
+#endif
+       if (sc != _POSIX_VDISABLE && MODE_LOCAL_CHARS(globalmode)) {
+           if (TerminalSpecialChars(sc) == 0) {
+               bol = 1;
+               break;
+           }
+       }
+       if (my_want_state_is_wont(TELOPT_BINARY)) {
+           switch (c) {
+           case '\n':
+                   /*
+                    * If we are in CRMOD mode (\r ==> \n)
+                    * on our local machine, then probably
+                    * a newline (unix) is CRLF (TELNET).
+                    */
+               if (MODE_LOCAL_CHARS(globalmode)) {
+                   NETADD('\r');
+               }
+               NETADD('\n');
+               bol = flushline = 1;
+               break;
+           case '\r':
+               if (!crlf) {
+                   NET2ADD('\r', '\0');
+               } else {
+                   NET2ADD('\r', '\n');
+               }
+               bol = flushline = 1;
+               break;
+           case IAC:
+               NET2ADD(IAC, IAC);
+               break;
+           default:
+               NETADD(c);
+               break;
+           }
+       } else if (c == IAC) {
+           NET2ADD(IAC, IAC);
+       } else {
+           NETADD(c);
+       }
+    }
+    if (count)
+       ring_consumed(&ttyiring, count);
+    return returnValue||count;         /* Non-zero if we did anything */
+}
+\f
+/*
+ * Scheduler()
+ *
+ * Try to do something.
+ *
+ * If we do something useful, return 1; else return 0.
+ *
+ */
+
+
+int
+Scheduler(int block)                   /* should we block in the select ? */
+{
+               /* One wants to be a bit careful about setting returnValue
+                * to one, since a one implies we did some useful work,
+                * and therefore probably won't be called to block next
+                * time (TN3270 mode only).
+                */
+    int returnValue;
+    int netin, netout, netex, ttyin, ttyout;
+
+    /* Decide which rings should be processed */
+
+    netout = ring_full_count(&netoring) &&
+           (flushline ||
+               (my_want_state_is_wont(TELOPT_LINEMODE)
+#ifdef KLUDGELINEMODE
+                       && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
+#endif
+               ) ||
+                       my_want_state_is_will(TELOPT_BINARY));
+    ttyout = ring_full_count(&ttyoring);
+
+#ifdef TN3270
+    ttyin = ring_empty_count(&ttyiring) && (clienteof == 0) && (shell_active == 0);
+#else  /* defined(TN3270) */
+    ttyin = ring_empty_count(&ttyiring) && (clienteof == 0);
+#endif /* defined(TN3270) */
+
+#ifdef TN3270
+    netin = ring_empty_count(&netiring);
+#   else /* !defined(TN3270) */
+    netin = !ISend && ring_empty_count(&netiring);
+#   endif /* !defined(TN3270) */
+
+    netex = !SYNCHing;
+
+    /* If we have seen a signal recently, reset things */
+#   ifdef TN3270
+    if (HaveInput) {
+       HaveInput = 0;
+       (void) signal(SIGIO, inputAvailable);
+    }
+#endif /* defined(TN3270) */
+
+    /* Call to system code to process rings */
+
+    returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
+
+    /* Now, look at the input rings, looking for work to do. */
+
+    if (ring_full_count(&ttyiring)) {
+#   if defined(TN3270)
+       if (In3270) {
+           int c;
+
+           c = DataFromTerminal(ttyiring.consume,
+                                       ring_full_consecutive(&ttyiring));
+           if (c) {
+               returnValue = 1;
+               ring_consumed(&ttyiring, c);
+           }
+       } else {
+#   endif /* defined(TN3270) */
+           returnValue |= telsnd();
+#   if defined(TN3270)
+       }
+#   endif /* defined(TN3270) */
+    }
+
+    if (ring_full_count(&netiring)) {
+#      if !defined(TN3270)
+       returnValue |= telrcv();
+#      else /* !defined(TN3270) */
+       returnValue = Push3270();
+#      endif /* !defined(TN3270) */
+    }
+    return returnValue;
+}
+\f
+/*
+ * Select from tty and network...
+ */
+void
+telnet(const char *user)
+{
+    sys_telnet_init();
+
+#if    defined(AUTHENTICATION) || defined(ENCRYPTION)
+    {
+       static char local_host[MAXHOSTNAMELEN + 1] = { 0 };
+
+       if (!local_host[0]) {
+               gethostname(local_host, sizeof(local_host));
+               local_host[sizeof(local_host)-1] = 0;
+       }
+       auth_encrypt_init(local_host, hostname, "TELNET", 0);
+       auth_encrypt_user(user);
+    }
+#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
+#   if !defined(TN3270)
+    if (telnetport) {
+#ifdef AUTHENTICATION
+       if (autologin)
+               send_will(TELOPT_AUTHENTICATION, 1);
+#endif
+#ifdef ENCRYPTION
+       send_do(TELOPT_ENCRYPT, 1);
+       send_will(TELOPT_ENCRYPT, 1);
+#endif /* ENCRYPTION */
+       send_do(TELOPT_SGA, 1);
+       send_will(TELOPT_TTYPE, 1);
+       send_will(TELOPT_NAWS, 1);
+       send_will(TELOPT_TSPEED, 1);
+       send_will(TELOPT_LFLOW, 1);
+       send_will(TELOPT_LINEMODE, 1);
+       send_will(TELOPT_NEW_ENVIRON, 1);
+       send_do(TELOPT_STATUS, 1);
+       if (env_getvalue((const unsigned char *)"DISPLAY"))
+           send_will(TELOPT_XDISPLOC, 1);
+       if (eight)
+           tel_enter_binary(eight);
+    }
+#   endif /* !defined(TN3270) */
+
+#   if !defined(TN3270)
+    for (;;) {
+       int schedValue;
+
+       while ((schedValue = Scheduler(0)) != 0) {
+           if (schedValue == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+
+       if (Scheduler(1) == -1) {
+           setcommandmode();
+           return;
+       }
+    }
+#   else /* !defined(TN3270) */
+    for (;;) {
+       int schedValue;
+
+       while (!In3270 && !shell_active) {
+           if (Scheduler(1) == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+
+       while ((schedValue = Scheduler(0)) != 0) {
+           if (schedValue == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+               /* If there is data waiting to go out to terminal, don't
+                * schedule any more data for the terminal.
+                */
+       if (ring_full_count(&ttyoring)) {
+           schedValue = 1;
+       } else {
+           if (shell_active) {
+               if (shell_continue() == 0) {
+                   ConnectScreen();
+               }
+           } else if (In3270) {
+               schedValue = DoTerminalOutput();
+           }
+       }
+       if (schedValue && (shell_active == 0)) {
+           if (Scheduler(1) == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+    }
+#   endif /* !defined(TN3270) */
+}
+\f
+#if    0       /* XXX - this not being in is a bug */
+/*
+ * 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.
+ */
+
+static 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;
+    }
+}
+#endif /* 0 */
+
+/*
+ * 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.
+ */
+
+static void
+netclear(void)
+{
+#if    0       /* XXX */
+    char *thisitem, *next;
+    char *good;
+#define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
+                               ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+    thisitem = netobuf;
+
+    while ((next = nextitem(thisitem)) <= netobuf.send) {
+       thisitem = next;
+    }
+
+    /* Now, thisitem is first before/at boundary. */
+
+    good = netobuf;    /* where the good bytes go */
+
+    while (netoring.add > 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);
+       }
+    }
+
+#endif /* 0 */
+}
+\f
+/*
+ * These routines add various telnet commands to the data stream.
+ */
+
+static void
+doflush(void)
+{
+    NET2ADD(IAC, DO);
+    NETADD(TELOPT_TM);
+    flushline = 1;
+    flushout = 1;
+    (void) ttyflush(1);                        /* Flush/drop output */
+    /* do printoption AFTER flush, otherwise the output gets tossed... */
+    printoption("SENT", DO, TELOPT_TM);
+}
+
+void
+xmitAO(void)
+{
+    NET2ADD(IAC, AO);
+    printoption("SENT", IAC, AO);
+    if (autoflush) {
+       doflush();
+    }
+}
+
+
+void
+xmitEL(void)
+{
+    NET2ADD(IAC, EL);
+    printoption("SENT", IAC, EL);
+}
+
+void
+xmitEC(void)
+{
+    NET2ADD(IAC, EC);
+    printoption("SENT", IAC, EC);
+}
+
+
+int
+dosynch(char *s)
+{
+    netclear();                        /* clear the path to the network */
+    NETADD(IAC);
+    setneturg();
+    NETADD(DM);
+    printoption("SENT", IAC, DM);
+    return 1;
+}
+
+int want_status_response = 0;
+
+int
+get_status(char *s)
+{
+    unsigned char tmp[16];
+    unsigned char *cp;
+
+    if (my_want_state_is_dont(TELOPT_STATUS)) {
+       printf("Remote side does not support STATUS option\n");
+       return 0;
+    }
+    cp = tmp;
+
+    *cp++ = IAC;
+    *cp++ = SB;
+    *cp++ = TELOPT_STATUS;
+    *cp++ = TELQUAL_SEND;
+    *cp++ = IAC;
+    *cp++ = SE;
+    if (NETROOM() >= cp - tmp) {
+       ring_supply_data(&netoring, tmp, cp-tmp);
+       printsub('>', tmp+2, cp - tmp - 2);
+    }
+    ++want_status_response;
+    return 1;
+}
+
+void
+intp(void)
+{
+    NET2ADD(IAC, IP);
+    printoption("SENT", IAC, IP);
+    flushline = 1;
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch(NULL);
+    }
+}
+
+void
+sendbrk(void)
+{
+    NET2ADD(IAC, BREAK);
+    printoption("SENT", IAC, BREAK);
+    flushline = 1;
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch(NULL);
+    }
+}
+
+void
+sendabort(void)
+{
+    NET2ADD(IAC, ABORT);
+    printoption("SENT", IAC, ABORT);
+    flushline = 1;
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch(NULL);
+    }
+}
+
+void
+sendsusp(void)
+{
+    NET2ADD(IAC, SUSP);
+    printoption("SENT", IAC, SUSP);
+    flushline = 1;
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch(NULL);
+    }
+}
+
+void
+sendeof(void)
+{
+    NET2ADD(IAC, xEOF);
+    printoption("SENT", IAC, xEOF);
+}
+
+void
+sendayt(void)
+{
+    NET2ADD(IAC, AYT);
+    printoption("SENT", IAC, AYT);
+}
+
+/*
+ * Send a window size update to the remote system.
+ */
+
+void
+sendnaws(void)
+{
+    long rows, cols;
+    unsigned char tmp[16];
+    unsigned char *cp;
+
+    if (my_state_is_wont(TELOPT_NAWS))
+       return;
+
+#define        PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
+                           if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
+
+    if (TerminalWindowSize(&rows, &cols) == 0) {       /* Failed */
+       return;
+    }
+
+    cp = tmp;
+
+    *cp++ = IAC;
+    *cp++ = SB;
+    *cp++ = TELOPT_NAWS;
+    PUTSHORT(cp, cols);
+    PUTSHORT(cp, rows);
+    *cp++ = IAC;
+    *cp++ = SE;
+    if (NETROOM() >= cp - tmp) {
+       ring_supply_data(&netoring, tmp, cp-tmp);
+       printsub('>', tmp+2, cp - tmp - 2);
+    }
+}
+
+void
+tel_enter_binary(int rw)
+{
+    if (rw&1)
+       send_do(TELOPT_BINARY, 1);
+    if (rw&2)
+       send_will(TELOPT_BINARY, 1);
+}
+
+void
+tel_leave_binary(int rw)
+{
+    if (rw&1)
+       send_dont(TELOPT_BINARY, 1);
+    if (rw&2)
+       send_wont(TELOPT_BINARY, 1);
+}
diff --git a/usr.bin/telnet/terminal.c b/usr.bin/telnet/terminal.c
new file mode 100644 (file)
index 0000000..7010e06
--- /dev/null
@@ -0,0 +1,220 @@
+/*     $NetBSD: terminal.c,v 1.15 2005/02/19 23:28:41 christos Exp $   */
+
+/*
+ * Copyright (c) 1988, 1990, 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[] = "@(#)terminal.c 8.2 (Berkeley) 2/16/95";
+#else
+__RCSID("$NetBSD: terminal.c,v 1.15 2005/02/19 23:28:41 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <arpa/telnet.h>
+#include <sys/types.h>
+
+#include "ring.h"
+
+#include "externs.h"
+#include "types.h"
+
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
+
+Ring           ttyoring, ttyiring;
+unsigned char  ttyobuf[2*BUFSIZ], ttyibuf[BUFSIZ];
+char line[] = { '\0' };
+
+int termdata;                  /* Debugging flag */
+
+/*
+ * initialize the terminal data structures.
+ */
+
+void
+init_terminal(void)
+{
+    if (ring_init(&ttyoring, ttyobuf, sizeof ttyobuf) != 1) {
+       exit(1);
+    }
+    if (ring_init(&ttyiring, ttyibuf, sizeof ttyibuf) != 1) {
+       exit(1);
+    }
+    autoflush = TerminalAutoFlush();
+}
+
+
+/*
+ *             Send as much data as possible to the terminal, else exits if
+ *             it encounters a permanent failure when writing to the tty.
+ *
+ *             Return value:
+ *                     -1: No useful work done, data waiting to go out.
+ *                      0: No data was waiting, so nothing was done.
+ *                      1: All waiting data was written out.
+ *                      n: All data - n was written out.
+ */
+
+
+int
+ttyflush(int drop)
+{
+    int n, n0, n1;
+
+    n0 = ring_full_count(&ttyoring);
+    if ((n1 = n = ring_full_consecutive(&ttyoring)) > 0) {
+       if (drop) {
+           TerminalFlushOutput();
+           /* we leave 'n' alone! */
+       } else {
+           n = TerminalWrite(ttyoring.consume, n);
+       }
+    }
+    if (n > 0) {
+       if (termdata && n) {
+           Dump('>', ttyoring.consume, n);
+       }
+       /*
+        * If we wrote everything, and the full count is
+        * larger than what we wrote, then write the
+        * rest of the buffer.
+        */
+       if (n1 == n && n0 > n) {
+               n1 = n0 - n;
+               if (!drop)
+                       n1 = TerminalWrite(ttyoring.bottom, n1);
+               if (n1 > 0)
+                       n += n1;
+       }
+       ring_consumed(&ttyoring, n);
+    }
+    if (n < 0) {
+       if (errno == EAGAIN || errno == EINTR) {
+           return -1;
+       } else {
+           ring_consumed(&ttyoring, ring_full_count(&ttyoring));
+           setconnmode(0);
+           setcommandmode();
+           NetClose(net);
+           fprintf(stderr, "Connection closed by foreign host.\n");
+           exit(1);
+       }
+    }
+    if (n == n0) {
+       if (n0)
+           return -1;
+       return 0;
+    }
+    return n0 - n + 1;
+}
+
+\f
+/*
+ * These routines decides on what the mode should be (based on the values
+ * of various global variables).
+ */
+
+
+int
+getconnmode(void)
+{
+    extern int linemode;
+    int mode = 0;
+#ifdef KLUDGELINEMODE
+    extern int kludgelinemode;
+#endif
+
+    if (In3270)
+       return(MODE_FLOW);
+
+    if (my_want_state_is_dont(TELOPT_ECHO))
+       mode |= MODE_ECHO;
+
+    if (localflow)
+       mode |= MODE_FLOW;
+
+    if (my_want_state_is_will(TELOPT_BINARY))
+       mode |= MODE_INBIN;
+
+    if (his_want_state_is_will(TELOPT_BINARY))
+       mode |= MODE_OUTBIN;
+
+#ifdef KLUDGELINEMODE
+    if (kludgelinemode) {
+       if (my_want_state_is_dont(TELOPT_SGA)) {
+           mode |= (MODE_TRAPSIG|MODE_EDIT);
+           if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
+               mode &= ~MODE_ECHO;
+           }
+       }
+       return(mode);
+    }
+#endif
+    if (my_want_state_is_will(TELOPT_LINEMODE))
+       mode |= linemode;
+    return(mode);
+}
+
+void
+setconnmode(int force)
+{
+#ifdef ENCRYPTION
+    static int enc_passwd = 0;
+#endif
+    int newmode;
+
+    newmode = getconnmode()|(force?MODE_FORCE:0);
+
+    TerminalNewMode(newmode);
+
+#ifdef ENCRYPTION
+    if ((newmode & (MODE_ECHO|MODE_EDIT)) == MODE_EDIT) {
+       if (my_want_state_is_will(TELOPT_ENCRYPT)
+                               && (enc_passwd == 0) && !encrypt_output) {
+           encrypt_request_start(0, 0);
+           enc_passwd = 1;
+       }
+    } else {
+       if (enc_passwd) {
+           encrypt_request_end();
+           enc_passwd = 0;
+       }
+    }
+#endif /* ENCRYPTION */
+}
+
+
+void
+setcommandmode(void)
+{
+    TerminalNewMode(-1);
+}
diff --git a/usr.bin/telnet/tn3270.c b/usr.bin/telnet/tn3270.c
new file mode 100644 (file)
index 0000000..7e7ad66
--- /dev/null
@@ -0,0 +1,394 @@
+/*     $NetBSD: tn3270.c,v 1.22 2006/10/07 17:27:57 elad Exp $ */
+
+/*
+ * Copyright (c) 1988, 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[] = "@(#)tn3270.c   8.2 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: tn3270.c,v 1.22 2006/10/07 17:27:57 elad Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <arpa/telnet.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include "general.h"
+#include "defines.h"
+#include "ring.h"
+#include "externs.h"
+
+#ifdef TN3270
+
+#include "../ctlr/screen.h"
+#include "../ctlr/declare.h"
+
+#include "../ascii/state.h"
+
+#include "../general/globals.h"
+
+#include "../sys_curses/telextrn.h"
+
+int
+       HaveInput,              /* There is input available to scan */
+       cursesdata,             /* Do we dump curses data? */
+       sigiocount;             /* Number of times we got a SIGIO */
+
+char   tline[200];
+char   *transcom = 0;  /* transparent mode command (default: none) */
+
+char   Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp;
+
+static char    sb_terminal[] = { IAC, SB,
+                       TELOPT_TTYPE, TELQUAL_IS,
+                       'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
+                       IAC, SE };
+#define        SBTERMMODEL     13
+
+static int
+       Sent3270TerminalType;   /* Have we said we are a 3270? */
+
+#endif /* defined(TN3270) */
+
+
+#ifdef TN3270
+void
+init_3270(void)
+{
+    HaveInput = 0;
+    sigiocount = 0;
+    Sent3270TerminalType = 0;
+    Ifrontp = Ibackp = Ibuf;
+    init_ctlr();               /* Initialize some things */
+    init_keyboard();
+    init_screen();
+    init_system();
+}
+#endif /* defined(TN3270) */
+
+\f
+#ifdef TN3270
+
+/*
+ * DataToNetwork - queue up some data to go to network.  If "done" is set,
+ * then when last byte is queued, we add on an IAC EOR sequence (so,
+ * don't call us with "done" until you want that done...)
+ *
+ * We actually do send all the data to the network buffer, since our
+ * only client needs for us to do that.
+ */
+
+int
+DataToNetwork(char *buffer,    /* where the data is */
+    int  count,        /* how much to send */
+    int                  done)         /* is this the last of a logical block */
+{
+    int loop, c;
+    int origCount;
+
+    origCount = count;
+
+    while (count) {
+       /* If not enough room for EORs, IACs, etc., wait */
+       if (NETROOM() < 6) {
+           struct pollfd set[1];
+
+           set[0].fd = net;
+           set[0].events = POLLOUT;
+           netflush();
+           while (NETROOM() < 6) {
+               (void) poll(set, 1, INFTIM);
+               netflush();
+           }
+       }
+       c = ring_empty_count(&netoring);
+       if (c > count) {
+           c = count;
+       }
+       loop = c;
+       while (loop) {
+           if (((unsigned char)*buffer) == IAC) {
+               break;
+           }
+           buffer++;
+           loop--;
+       }
+       if ((c = c-loop)) {
+           ring_supply_data(&netoring, buffer-c, c);
+           count -= c;
+       }
+       if (loop) {
+           NET2ADD(IAC, IAC);
+           count--;
+           buffer++;
+       }
+    }
+
+    if (done) {
+       NET2ADD(IAC, EOR);
+       netflush();             /* try to move along as quickly as ... */
+    }
+    return(origCount - count);
+}
+
+
+void
+inputAvailable(int signo)
+{
+    HaveInput = 1;
+    sigiocount++;
+}
+
+void
+outputPurge(void)
+{
+    (void) ttyflush(1);
+}
+
+
+/*
+ * The following routines are places where the various tn3270
+ * routines make calls into telnet.c.
+ */
+
+/*
+ * DataToTerminal - queue up some data to go to terminal.
+ *
+ * Note: there are people who call us and depend on our processing
+ * *all* the data at one time (thus the poll).
+ */
+
+int
+DataToTerminal(
+    char       *buffer,                /* where the data is */
+    int        count)                  /* how much to send */
+{
+    int c;
+    int origCount;
+
+    origCount = count;
+
+    while (count) {
+       if (TTYROOM() == 0) {
+           struct pollfd set[1];
+
+           set[0].fd = tout;
+           set[0].events = POLLOUT;
+           (void) ttyflush(0);
+           while (TTYROOM() == 0) {
+               (void) poll(set, 1, INFTIM);
+               (void) ttyflush(0);
+           }
+       }
+       c = TTYROOM();
+       if (c > count) {
+           c = count;
+       }
+       ring_supply_data(&ttyoring, buffer, c);
+       count -= c;
+       buffer += c;
+    }
+    return(origCount);
+}
+\f
+
+/*
+ * Push3270 - Try to send data along the 3270 output (to screen) direction.
+ */
+
+int
+Push3270(void)
+{
+    int save = ring_full_count(&netiring);
+
+    if (save) {
+       if (Ifrontp+save > Ibuf+sizeof Ibuf) {
+           if (Ibackp != Ibuf) {
+               memmove(Ibuf, Ibackp, Ifrontp-Ibackp);
+               Ifrontp -= (Ibackp-Ibuf);
+               Ibackp = Ibuf;
+           }
+       }
+       if (Ifrontp+save < Ibuf+sizeof Ibuf) {
+           (void)telrcv();
+       }
+    }
+    return save != ring_full_count(&netiring);
+}
+
+
+/*
+ * Finish3270 - get the last dregs of 3270 data out to the terminal
+ *             before quitting.
+ */
+
+void
+Finish3270(void)
+{
+    while (Push3270() || !DoTerminalOutput()) {
+       HaveInput = 0;
+       ;
+    }
+}
+
+
+/* StringToTerminal - output a null terminated string to the terminal */
+
+void
+StringToTerminal(char *s)
+{
+    int count;
+
+    count = strlen(s);
+    if (count) {
+       (void) DataToTerminal(s, count);        /* we know it always goes... */
+    }
+}
+
+
+/* _putchar - output a single character to the terminal.  This name is so that
+ *     curses(3x) can call us to send out data.
+ */
+
+int
+_putchar(int cc)
+{
+    char c = (char)cc;
+    if (cursesdata) {
+       Dump('>', &c, 1);
+    }
+    if (!TTYROOM()) {
+       (void) DataToTerminal(&c, 1);
+    } else {
+       TTYADD(c);
+    }
+
+    return (0);
+}
+
+void
+SetIn3270(void)
+{
+    if (Sent3270TerminalType && my_want_state_is_will(TELOPT_BINARY)
+               && my_want_state_is_do(TELOPT_BINARY) && !donebinarytoggle) {
+       if (!In3270) {
+           In3270 = 1;
+           Init3270();         /* Initialize 3270 functions */
+           /* initialize terminal key mapping */
+           InitTerminal();     /* Start terminal going */
+           setconnmode(0);
+       }
+    } else {
+       if (In3270) {
+           StopScreen(1);
+           In3270 = 0;
+           Stop3270();         /* Tell 3270 we aren't here anymore */
+           setconnmode(0);
+       }
+    }
+}
+
+/*
+ * tn3270_ttype()
+ *
+ *     Send a response to a terminal type negotiation.
+ *
+ *     Return '0' if no more responses to send; '1' if a response sent.
+ */
+
+int
+tn3270_ttype(void)
+{
+    /*
+     * Try to send a 3270 type terminal name.  Decide which one based
+     * on the format of our screen, and (in the future) color
+     * capaiblities.
+     */
+    InitTerminal();            /* Sets MaxNumberColumns, MaxNumberLines */
+    if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
+       Sent3270TerminalType = 1;
+       if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
+           MaxNumberLines = 27;
+           MaxNumberColumns = 132;
+           sb_terminal[SBTERMMODEL] = '5';
+       } else if (MaxNumberLines >= 43) {
+           MaxNumberLines = 43;
+           MaxNumberColumns = 80;
+           sb_terminal[SBTERMMODEL] = '4';
+       } else if (MaxNumberLines >= 32) {
+           MaxNumberLines = 32;
+           MaxNumberColumns = 80;
+           sb_terminal[SBTERMMODEL] = '3';
+       } else {
+           MaxNumberLines = 24;
+           MaxNumberColumns = 80;
+           sb_terminal[SBTERMMODEL] = '2';
+       }
+       NumberLines = 24;               /* before we start out... */
+       NumberColumns = 80;
+       ScreenSize = NumberLines*NumberColumns;
+       if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
+           ExitString("Programming error:  MAXSCREENSIZE too small.\n",
+                                                               1);
+           /*NOTREACHED*/
+       }
+       printsub('>', sb_terminal+2, sizeof sb_terminal-2);
+       ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal);
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+int
+settranscom(int argc, char *argv[])
+{
+       int i;
+
+       if (argc == 1 && transcom) {
+          transcom = 0;
+       }
+       if (argc == 1) {
+          return 1;
+       }
+       transcom = tline;
+       (void) strlcpy(tline, argv[1], sizeof(tline));
+       for (i = 2; i < argc; ++i) {
+           (void) strlcat(tline, " ", sizeof(tline));
+           (void) strlcat(tline, argv[i], sizeof(tline));
+       }
+       return 1;
+}
+
+#endif /* defined(TN3270) */
diff --git a/usr.bin/telnet/types.h b/usr.bin/telnet/types.h
new file mode 100644 (file)
index 0000000..d85ae2e
--- /dev/null
@@ -0,0 +1,50 @@
+/*     $NetBSD: types.h,v 1.6 2003/08/07 11:16:12 agc Exp $    */
+
+/*
+ * Copyright (c) 1988, 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: @(#)types.h       8.1 (Berkeley) 6/6/93
+ */
+
+typedef struct {
+    char *modedescriptions;
+    char modetype;
+} Modelist;
+
+extern Modelist modelist[];
+
+typedef 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 */
+       gotDM;                  /* when did we last see a data mark */
+} Clocks;
+
+extern Clocks clocks;
diff --git a/usr.bin/telnet/utilities.c b/usr.bin/telnet/utilities.c
new file mode 100644 (file)
index 0000000..2bab18b
--- /dev/null
@@ -0,0 +1,925 @@
+/*     $NetBSD: utilities.c,v 1.23 2012/01/09 16:08:55 christos Exp $  */
+
+/*
+ * Copyright (c) 1988, 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[] = "@(#)utilities.c        8.3 (Berkeley) 5/30/95";
+#else
+__RCSID("$NetBSD: utilities.c,v 1.23 2012/01/09 16:08:55 christos Exp $");
+#endif
+#endif /* not lint */
+
+#define        TELOPTS
+#define        TELCMDS
+#define        SLC_NAMES
+#include <arpa/telnet.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <ctype.h>
+
+#include "general.h"
+#include "ring.h"
+#include "defines.h"
+#include "externs.h"
+
+#ifdef TN3270
+#include "../sys_curses/telextrn.h"
+#endif
+
+#ifdef AUTHENTICATION
+#include <libtelnet/auth.h>
+#endif
+#ifdef ENCRYPTION
+#include <libtelnet/encrypt.h>
+#endif
+
+FILE   *NetTrace = 0;          /* Not in bss, since needs to stay */
+int    prettydump;
+
+/*
+ * upcase()
+ *
+ *     Upcase (in place) the argument.
+ */
+
+void
+upcase(char *argument)
+{
+    int c;
+
+    while ((c = *argument) != 0) {
+       if (islower(c)) {
+           *argument = toupper(c);
+       }
+       argument++;
+    }
+}
+
+/*
+ * SetSockOpt()
+ *
+ * Compensate for differences in 4.2 and 4.3 systems.
+ */
+
+int
+SetSockOpt(int fd, int level, int option, int yesno)
+{
+    return setsockopt(fd, level, option, (char *)&yesno, sizeof yesno);
+}
+\f
+/*
+ * The following are routines used to print out debugging information.
+ */
+
+char NetTraceFile[256] = "(standard output)";
+
+void
+SetNetTrace(char *file)
+{
+    if (NetTrace && NetTrace != stdout)
+       fclose(NetTrace);
+    if (file  && (strcmp(file, "-") != 0)) {
+       NetTrace = fopen(file, "w");
+       if (NetTrace) {
+           strlcpy(NetTraceFile, file, sizeof(NetTraceFile));
+           return;
+       }
+       fprintf(stderr, "Cannot open %s.\n", file);
+    }
+    NetTrace = stdout;
+    strlcpy(NetTraceFile, "(standard output)", sizeof(NetTraceFile));
+}
+
+void
+Dump(int direction, unsigned char *buffer, int length)
+{
+#   define BYTES_PER_LINE      32
+#   define min(x,y)    ((x<y)? x:y)
+    unsigned char *pThis;
+    int offset;
+
+    offset = 0;
+
+    while (length) {
+       /* print one line */
+       fprintf(NetTrace, "%c 0x%x\t", direction, offset);
+       pThis = buffer;
+       if (prettydump) {
+           buffer = buffer + min(length, BYTES_PER_LINE/2);
+           while (pThis < buffer) {
+               fprintf(NetTrace, "%c%.2x",
+                   (((*pThis)&0xff) == 0xff) ? '*' : ' ',
+                   (*pThis)&0xff);
+               pThis++;
+           }
+           length -= BYTES_PER_LINE/2;
+           offset += BYTES_PER_LINE/2;
+       } else {
+           buffer = buffer + min(length, BYTES_PER_LINE);
+           while (pThis < buffer) {
+               fprintf(NetTrace, "%.2x", (*pThis)&0xff);
+               pThis++;
+           }
+           length -= BYTES_PER_LINE;
+           offset += BYTES_PER_LINE;
+       }
+       if (NetTrace == stdout) {
+           fprintf(NetTrace, "\r\n");
+       } else {
+           fprintf(NetTrace, "\n");
+       }
+       if (length < 0) {
+           fflush(NetTrace);
+           return;
+       }
+       /* find next unique line */
+    }
+    fflush(NetTrace);
+}
+
+
+void
+printoption(const char *direction, int cmd, int option)
+{
+       if (!showoptions)
+               return;
+       if (cmd == IAC) {
+               if (TELCMD_OK(option))
+                   fprintf(NetTrace, "%s IAC %s", direction, TELCMD(option));
+               else
+                   fprintf(NetTrace, "%s IAC %d", direction, option);
+       } else {
+               const char *fmt;
+               fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
+                       (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
+               if (fmt) {
+                   fprintf(NetTrace, "%s %s ", direction, fmt);
+                   if (TELOPT_OK(option))
+                       fprintf(NetTrace, "%s", TELOPT(option));
+                   else if (option == TELOPT_EXOPL)
+                       fprintf(NetTrace, "EXOPL");
+                   else
+                       fprintf(NetTrace, "%d", option);
+               } else
+                   fprintf(NetTrace, "%s %d %d", direction, cmd, option);
+       }
+       if (NetTrace == stdout) {
+           fprintf(NetTrace, "\r\n");
+           fflush(NetTrace);
+       } else {
+           fprintf(NetTrace, "\n");
+       }
+       return;
+}
+
+void
+optionstatus(void)
+{
+    int i;
+    extern char will_wont_resp[], do_dont_resp[];
+
+    for (i = 0; i < 256; i++) {
+       if (do_dont_resp[i]) {
+           if (TELOPT_OK(i))
+               printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]);
+           else if (TELCMD_OK(i))
+               printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]);
+           else
+               printf("resp DO_DONT %d: %d\n", i,
+                               do_dont_resp[i]);
+           if (my_want_state_is_do(i)) {
+               if (TELOPT_OK(i))
+                   printf("want DO   %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want DO   %s\n", TELCMD(i));
+               else
+                   printf("want DO   %d\n", i);
+           } else {
+               if (TELOPT_OK(i))
+                   printf("want DONT %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want DONT %s\n", TELCMD(i));
+               else
+                   printf("want DONT %d\n", i);
+           }
+       } else {
+           if (my_state_is_do(i)) {
+               if (TELOPT_OK(i))
+                   printf("     DO   %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("     DO   %s\n", TELCMD(i));
+               else
+                   printf("     DO   %d\n", i);
+           }
+       }
+       if (will_wont_resp[i]) {
+           if (TELOPT_OK(i))
+               printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]);
+           else if (TELCMD_OK(i))
+               printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]);
+           else
+               printf("resp WILL_WONT %d: %d\n",
+                               i, will_wont_resp[i]);
+           if (my_want_state_is_will(i)) {
+               if (TELOPT_OK(i))
+                   printf("want WILL %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want WILL %s\n", TELCMD(i));
+               else
+                   printf("want WILL %d\n", i);
+           } else {
+               if (TELOPT_OK(i))
+                   printf("want WONT %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want WONT %s\n", TELCMD(i));
+               else
+                   printf("want WONT %d\n", i);
+           }
+       } else {
+           if (my_state_is_will(i)) {
+               if (TELOPT_OK(i))
+                   printf("     WILL %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("     WILL %s\n", TELCMD(i));
+               else
+                   printf("     WILL %d\n", i);
+           }
+       }
+    }
+
+}
+
+void
+printsub(
+    int direction,     /* '<' or '>' */
+    unsigned char *pointer,    /* where suboption data sits */
+    int                  length)       /* length of suboption data */
+{
+    int i;
+#ifdef ENCRYPTION
+    char buf[512];
+#endif /* ENCRYPTION */
+    extern int want_status_response;
+
+    if (showoptions || direction == 0 ||
+       (want_status_response && (pointer[0] == TELOPT_STATUS))) {
+       if (direction) {
+           fprintf(NetTrace, "%s IAC SB ",
+                               (direction == '<')? "RCVD":"SENT");
+           if (length >= 3) {
+               int j;
+
+               i = pointer[length-2];
+               j = pointer[length-1];
+
+               if (i != IAC || j != SE) {
+                   fprintf(NetTrace, "(terminated by ");
+                   if (TELOPT_OK(i))
+                       fprintf(NetTrace, "%s ", TELOPT(i));
+                   else if (TELCMD_OK(i))
+                       fprintf(NetTrace, "%s ", TELCMD(i));
+                   else
+                       fprintf(NetTrace, "%d ", i);
+                   if (TELOPT_OK(j))
+                       fprintf(NetTrace, "%s", TELOPT(j));
+                   else if (TELCMD_OK(j))
+                       fprintf(NetTrace, "%s", TELCMD(j));
+                   else
+                       fprintf(NetTrace, "%d", j);
+                   fprintf(NetTrace, ", not IAC SE!) ");
+               }
+           }
+           length -= 2;
+       }
+       if (length < 1) {
+           fprintf(NetTrace, "(Empty suboption??\?)");
+           if (NetTrace == stdout)
+               fflush(NetTrace);
+           return;
+       }
+       switch (pointer[0]) {
+       case TELOPT_TTYPE:
+           fprintf(NetTrace, "TERMINAL-TYPE ");
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
+               break;
+           case TELQUAL_SEND:
+               fprintf(NetTrace, "SEND");
+               break;
+           default:
+               fprintf(NetTrace,
+                               "- unknown qualifier %d (0x%x).",
+                               pointer[1], pointer[1]);
+           }
+           break;
+       case TELOPT_TSPEED:
+           fprintf(NetTrace, "TERMINAL-SPEED");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               fprintf(NetTrace, " IS ");
+               fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2);
+               break;
+           default:
+               if (pointer[1] == 1)
+                   fprintf(NetTrace, " SEND");
+               else
+                   fprintf(NetTrace, " %d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++)
+                   fprintf(NetTrace, " ?%d?", pointer[i]);
+               break;
+           }
+           break;
+
+       case TELOPT_LFLOW:
+           fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case LFLOW_OFF:
+               fprintf(NetTrace, " OFF"); break;
+           case LFLOW_ON:
+               fprintf(NetTrace, " ON"); break;
+           case LFLOW_RESTART_ANY:
+               fprintf(NetTrace, " RESTART-ANY"); break;
+           case LFLOW_RESTART_XON:
+               fprintf(NetTrace, " RESTART-XON"); break;
+           default:
+               fprintf(NetTrace, " %d (unknown)", pointer[1]);
+           }
+           for (i = 2; i < length; i++)
+               fprintf(NetTrace, " ?%d?", pointer[i]);
+           break;
+
+       case TELOPT_NAWS:
+           fprintf(NetTrace, "NAWS");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption??\?)");
+               break;
+           }
+           if (length == 2) {
+               fprintf(NetTrace, " ?%d?", pointer[1]);
+               break;
+           }
+           fprintf(NetTrace, " %d %d (%d)",
+               pointer[1], pointer[2],
+               (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
+           if (length == 4) {
+               fprintf(NetTrace, " ?%d?", pointer[3]);
+               break;
+           }
+           fprintf(NetTrace, " %d %d (%d)",
+               pointer[3], pointer[4],
+               (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
+           for (i = 5; i < length; i++)
+               fprintf(NetTrace, " ?%d?", pointer[i]);
+           break;
+
+#ifdef AUTHENTICATION
+       case TELOPT_AUTHENTICATION:
+           fprintf(NetTrace, "AUTHENTICATION");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case TELQUAL_REPLY:
+           case TELQUAL_IS:
+               fprintf(NetTrace, " %s ", (pointer[1] == TELQUAL_IS) ?
+                                                       "IS" : "REPLY");
+               if (AUTHTYPE_NAME_OK(pointer[2]))
+                   fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[2]));
+               else
+                   fprintf(NetTrace, "%d ", pointer[2]);
+               if (length < 3) {
+                   fprintf(NetTrace, "(partial suboption??\?)");
+                   break;
+               }
+               fprintf(NetTrace, "%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));
+               fprintf(NetTrace, "%s", buf);
+               break;
+
+           case TELQUAL_SEND:
+               i = 2;
+               fprintf(NetTrace, " SEND ");
+               while (i < length) {
+                   if (AUTHTYPE_NAME_OK(pointer[i]))
+                       fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[i]));
+                   else
+                       fprintf(NetTrace, "%d ", pointer[i]);
+                   if (++i >= length) {
+                       fprintf(NetTrace, "(partial suboption??\?)");
+                       break;
+                   }
+                   fprintf(NetTrace, "%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;
+               fprintf(NetTrace, " NAME \"");
+               while (i < length)
+                   putc(pointer[i++], NetTrace);
+               putc('"', NetTrace);
+               break;
+
+           default:
+                   for (i = 2; i < length; i++)
+                       fprintf(NetTrace, " ?%d?", pointer[i]);
+                   break;
+           }
+           break;
+#endif
+
+#ifdef ENCRYPTION
+       case TELOPT_ENCRYPT:
+               fprintf(NetTrace, "ENCRYPT");
+               if (length < 2) {
+                       fprintf(NetTrace, " (empty suboption??\?)");
+                       break;
+               }
+               switch (pointer[1]) {
+               case ENCRYPT_START:
+                       fprintf(NetTrace, " START");
+                       break;
+
+               case ENCRYPT_END:
+                       fprintf(NetTrace, " END");
+                       break;
+
+               case ENCRYPT_REQSTART:
+                       fprintf(NetTrace, " REQUEST-START");
+                       break;
+
+               case ENCRYPT_REQEND:
+                       fprintf(NetTrace, " REQUEST-END");
+                       break;
+
+               case ENCRYPT_IS:
+               case ENCRYPT_REPLY:
+                       fprintf(NetTrace, " %s ", (pointer[1] == ENCRYPT_IS) ?
+                           "IS" : "REPLY");
+                       if (length < 3) {
+                               fprintf(NetTrace, " (partial suboption??\?)");
+                               break;
+                       }
+                       if (ENCTYPE_NAME_OK(pointer[2]))
+                               fprintf(NetTrace, "%s ",
+                                   ENCTYPE_NAME(pointer[2]));
+                       else
+                               fprintf(NetTrace, " %d (unknown)", pointer[2]);
+
+                       encrypt_printsub(&pointer[1], length - 1, buf,
+                           sizeof(buf));
+                       fprintf(NetTrace, "%s", buf);
+                       break;
+
+               case ENCRYPT_SUPPORT:
+                       i = 2;
+                       fprintf(NetTrace, " SUPPORT ");
+                       while (i < length) {
+                               if (ENCTYPE_NAME_OK(pointer[i]))
+                                       fprintf(NetTrace, "%s ",
+                                           ENCTYPE_NAME(pointer[i]));
+                               else
+                                       fprintf(NetTrace, "%d ", pointer[i]);
+                               i++;
+                       }
+                       break;
+
+               case ENCRYPT_ENC_KEYID:
+                       fprintf(NetTrace, " ENC_KEYID ");
+                       goto encommon;
+
+               case ENCRYPT_DEC_KEYID:
+                       fprintf(NetTrace, " DEC_KEYID ");
+                       goto encommon;
+
+               default:
+                       fprintf(NetTrace, " %d (unknown)", pointer[1]);
+               encommon:
+                       for (i = 2; i < length; i++)
+                               fprintf(NetTrace, " %d", pointer[i]);
+                       break;
+               }
+               break;
+#endif /* ENCRYPTION */
+
+       case TELOPT_LINEMODE:
+           fprintf(NetTrace, "LINEMODE ");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption??\?)");
+               break;
+           }
+           switch (pointer[1]) {
+           case WILL:
+               fprintf(NetTrace, "WILL ");
+               goto common;
+           case WONT:
+               fprintf(NetTrace, "WONT ");
+               goto common;
+           case DO:
+               fprintf(NetTrace, "DO ");
+               goto common;
+           case DONT:
+               fprintf(NetTrace, "DONT ");
+           common:
+               if (length < 3) {
+                   fprintf(NetTrace, "(no option??\?)");
+                   break;
+               }
+               switch (pointer[2]) {
+               case LM_FORWARDMASK:
+                   fprintf(NetTrace, "Forward Mask");
+                   for (i = 3; i < length; i++)
+                       fprintf(NetTrace, " %x", pointer[i]);
+                   break;
+               default:
+                   fprintf(NetTrace, "%d (unknown)", pointer[2]);
+                   for (i = 3; i < length; i++)
+                       fprintf(NetTrace, " %d", pointer[i]);
+                   break;
+               }
+               break;
+
+           case LM_SLC:
+               fprintf(NetTrace, "SLC");
+               for (i = 2; i < length - 2; i += 3) {
+                   if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
+                       fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
+                   else
+                       fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]);
+                   switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
+                   case SLC_NOSUPPORT:
+                       fprintf(NetTrace, " NOSUPPORT"); break;
+                   case SLC_CANTCHANGE:
+                       fprintf(NetTrace, " CANTCHANGE"); break;
+                   case SLC_VARIABLE:
+                       fprintf(NetTrace, " VARIABLE"); break;
+                   case SLC_DEFAULT:
+                       fprintf(NetTrace, " DEFAULT"); break;
+                   }
+                   fprintf(NetTrace, "%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))
+                       fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]);
+                   fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]);
+                   if ((pointer[i+SLC_VALUE] == IAC) &&
+                       (pointer[i+SLC_VALUE+1] == IAC))
+                               i++;
+               }
+               for (; i < length; i++)
+                   fprintf(NetTrace, " ?%d?", pointer[i]);
+               break;
+
+           case LM_MODE:
+               fprintf(NetTrace, "MODE ");
+               if (length < 3) {
+                   fprintf(NetTrace, "(no mode??\?)");
+                   break;
+               }
+               {
+                   char tbuf[64];
+                   sprintf(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" : "");
+                   fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0");
+               }
+               if (pointer[2]&~(MODE_MASK))
+                   fprintf(NetTrace, " (0x%x)", pointer[2]);
+               for (i = 3; i < length; i++)
+                   fprintf(NetTrace, " ?0x%x?", pointer[i]);
+               break;
+           default:
+               fprintf(NetTrace, "%d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++)
+                   fprintf(NetTrace, " %d", pointer[i]);
+           }
+           break;
+
+       case TELOPT_STATUS: {
+           const char *cp;
+           int j, k;
+
+           fprintf(NetTrace, "STATUS");
+
+           switch (pointer[1]) {
+           default:
+               if (pointer[1] == TELQUAL_SEND)
+                   fprintf(NetTrace, " SEND");
+               else
+                   fprintf(NetTrace, " %d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++)
+                   fprintf(NetTrace, " ?%d?", pointer[i]);
+               break;
+           case TELQUAL_IS:
+               if (--want_status_response < 0)
+                   want_status_response = 0;
+               if (NetTrace == stdout)
+                   fprintf(NetTrace, " IS\r\n");
+               else
+                   fprintf(NetTrace, " IS\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((int)pointer[i]))
+                           fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i]));
+                       else
+                           fprintf(NetTrace, " %s %d", cp, pointer[i]);
+
+                       if (NetTrace == stdout)
+                           fprintf(NetTrace, "\r\n");
+                       else
+                           fprintf(NetTrace, "\n");
+                       break;
+
+                   case SB:
+                       fprintf(NetTrace, " 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) {
+                           fprintf(NetTrace, " SE");
+                           i = j;
+                       } else
+                           i = j - 1;
+
+                       if (NetTrace == stdout)
+                           fprintf(NetTrace, "\r\n");
+                       else
+                           fprintf(NetTrace, "\n");
+
+                       break;
+
+                   default:
+                       fprintf(NetTrace, " %d", pointer[i]);
+                       break;
+                   }
+               }
+               break;
+           }
+           break;
+         }
+
+       case TELOPT_XDISPLOC:
+           fprintf(NetTrace, "X-DISPLAY-LOCATION ");
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
+               break;
+           case TELQUAL_SEND:
+               fprintf(NetTrace, "SEND");
+               break;
+           default:
+               fprintf(NetTrace, "- unknown qualifier %d (0x%x).",
+                               pointer[1], pointer[1]);
+           }
+           break;
+
+       case TELOPT_NEW_ENVIRON:
+           fprintf(NetTrace, "NEW-ENVIRON ");
+#ifdef OLD_ENVIRON
+           goto env_common1;
+       case TELOPT_OLD_ENVIRON:
+           fprintf(NetTrace, "OLD-ENVIRON");
+       env_common1:
+#endif
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               fprintf(NetTrace, "IS ");
+               goto env_common;
+           case TELQUAL_SEND:
+               fprintf(NetTrace, "SEND ");
+               goto env_common;
+           case TELQUAL_INFO:
+               fprintf(NetTrace, "INFO ");
+           env_common:
+               {
+                   static const char NQ[] = "\" ";
+                   const char *noquote = NQ;
+#if defined(ENV_HACK) && defined(OLD_ENVIRON)
+                   extern int old_env_var, old_env_value;
+#endif
+                   for (i = 2; i < length; i++ ) {
+                       switch (pointer[i]) {
+                       case NEW_ENV_VALUE:
+#ifdef OLD_ENVIRON
+                    /* case NEW_ENV_OVAR: */
+                           if (pointer[0] == TELOPT_OLD_ENVIRON) {
+# ifdef        ENV_HACK
+                               if (old_env_var == OLD_ENV_VALUE)
+                                   fprintf(NetTrace, "%s(VALUE) ", noquote);
+                               else
+# endif
+                                   fprintf(NetTrace, "%sVAR ", noquote);
+                           } else
+#endif /* OLD_ENVIRON */
+                               fprintf(NetTrace, "%sVALUE ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       case NEW_ENV_VAR:
+#ifdef OLD_ENVIRON
+                    /* case OLD_ENV_VALUE: */
+                           if (pointer[0] == TELOPT_OLD_ENVIRON) {
+# ifdef        ENV_HACK
+                               if (old_env_value == OLD_ENV_VAR)
+                                   fprintf(NetTrace, "%s(VAR) ", noquote);
+                               else
+# endif
+                                   fprintf(NetTrace, "%sVALUE ", noquote);
+                           } else
+#endif /* OLD_ENVIRON */
+                               fprintf(NetTrace, "%sVAR ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       case ENV_ESC:
+                           fprintf(NetTrace, "%sESC ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       case ENV_USERVAR:
+                           fprintf(NetTrace, "%sUSERVAR ", noquote);
+                           noquote = NQ;
+                           break;
+
+                       default:
+                           if (isprint(pointer[i]) && pointer[i] != '"') {
+                               if (*noquote) {
+                                   putc('"', NetTrace);
+                                   noquote = "";
+                               }
+                               putc(pointer[i], NetTrace);
+                           } else {
+                               fprintf(NetTrace, "%s%03o ", noquote,
+                                                       pointer[i]);
+                               noquote = NQ;
+                           }
+                           break;
+                       }
+                   }
+                   if (!noquote)
+                       putc('"', NetTrace);
+                   break;
+               }
+           }
+           break;
+
+       default:
+           if (TELOPT_OK(pointer[0]))
+               fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0]));
+           else
+               fprintf(NetTrace, "%d (unknown)", pointer[0]);
+           for (i = 1; i < length; i++)
+               fprintf(NetTrace, " %d", pointer[i]);
+           break;
+       }
+       if (direction) {
+           if (NetTrace == stdout)
+               fprintf(NetTrace, "\r\n");
+           else
+               fprintf(NetTrace, "\n");
+       }
+       if (NetTrace == stdout)
+           fflush(NetTrace);
+    }
+}
+
+/* EmptyTerminal - called to make sure that the terminal buffer is empty.
+ *                     Note that we consider the buffer to run all the
+ *                     way to the kernel (thus the poll).
+ */
+
+void
+EmptyTerminal(void)
+{
+    struct pollfd set[1];
+
+    set[0].fd = tout;
+    set[0].events = POLLOUT;
+
+    if (TTYBYTES() == 0) {
+       (void) poll(set, 1, INFTIM);
+    } else {
+       while (TTYBYTES()) {
+           (void) ttyflush(0);
+           (void) poll(set, 1, INFTIM);
+       }
+    }
+}
+
+void
+SetForExit(void)
+{
+    setconnmode(0);
+#ifdef TN3270
+    if (In3270) {
+       Finish3270();
+    }
+#else  /* defined(TN3270) */
+    do {
+       (void)telrcv();                 /* Process any incoming data */
+       EmptyTerminal();
+    } while (ring_full_count(&netiring));      /* While there is any */
+#endif /* defined(TN3270) */
+    setcommandmode();
+    fflush(stdout);
+    fflush(stderr);
+#ifdef TN3270
+    if (In3270) {
+       StopScreen(1);
+    }
+#endif /* defined(TN3270) */
+    setconnmode(0);
+    EmptyTerminal();                   /* Flush the path to the tty */
+    setcommandmode();
+}
+
+void
+Exit(int returnCode)
+{
+    SetForExit();
+    exit(returnCode);
+}
+
+void
+ExitString(const char *string, int returnCode)
+{
+    SetForExit();
+    fwrite(string, 1, strlen(string), stderr);
+    exit(returnCode);
+}