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