New telnetd, with LINEMODE support
authorPaul Borman <borman@ucbvax.Berkeley.EDU>
Sat, 2 Sep 1989 06:17:55 +0000 (22:17 -0800)
committerPaul Borman <borman@ucbvax.Berkeley.EDU>
Sat, 2 Sep 1989 06:17:55 +0000 (22:17 -0800)
SCCS-vsn: libexec/telnetd/Makefile 5.5
SCCS-vsn: libexec/telnetd/pathnames.h 5.3
SCCS-vsn: libexec/telnetd/telnetd.c 5.38
SCCS-vsn: libexec/telnetd/defs.h 5.1
SCCS-vsn: libexec/telnetd/ext.h 5.1
SCCS-vsn: libexec/telnetd/global.c 5.1
SCCS-vsn: libexec/telnetd/slc.c 5.1
SCCS-vsn: libexec/telnetd/state.c 5.1
SCCS-vsn: libexec/telnetd/sys_term.c 5.1
SCCS-vsn: libexec/telnetd/telnetd.h 5.1
SCCS-vsn: libexec/telnetd/termstat.c 5.1
SCCS-vsn: libexec/telnetd/utility.c 5.1

12 files changed:
usr/src/libexec/telnetd/Makefile
usr/src/libexec/telnetd/defs.h [new file with mode: 0644]
usr/src/libexec/telnetd/ext.h [new file with mode: 0644]
usr/src/libexec/telnetd/global.c [new file with mode: 0644]
usr/src/libexec/telnetd/pathnames.h
usr/src/libexec/telnetd/slc.c [new file with mode: 0644]
usr/src/libexec/telnetd/state.c [new file with mode: 0644]
usr/src/libexec/telnetd/sys_term.c [new file with mode: 0644]
usr/src/libexec/telnetd/telnetd.c
usr/src/libexec/telnetd/telnetd.h [new file with mode: 0644]
usr/src/libexec/telnetd/termstat.c [new file with mode: 0644]
usr/src/libexec/telnetd/utility.c [new file with mode: 0644]

index 1f43888..4e24b65 100644 (file)
 # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 # WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 #
 # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 # WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 #
-#      @(#)Makefile    5.4 (Berkeley) %G%
+#      @(#)Makefile    5.5 (Berkeley) %G%
 #
 #
-CFLAGS=        -O
+
+DEFINES=
 LIBC=  /lib/libc.a
 LIBC=  /lib/libc.a
-SRCS=  telnetd.c get_date.c gettytab.c
-OBJS=  telnetd.o get_date.o gettytab.o
+# 4.4BSD
+#              LIBS= -lutil -ltermcap
+#              GETTYOBJ= get_date.o gettytab.o
+#              GETTYSRC= get_date.c gettytab.c
+#              LIBEXEC=${DESTDIR}/usr/libexec
+#
+# 4.3BSD       LIBS= -ltermcap
+#              GETTYOBJ= get_date.o gettytab.o
+#              GETTYSRC= get_date.c gettytab.c
+#              LIBEXEC=${DESTDIR}/etc
+#
+# Cray UNICOS
+#              LIBS= -lnet -lcurses
+#              GETTYOBJ=
+#              GETTYSRC=
+#              LIBEXEC=${DESTDIR}/etc
+
+LIBS=  -lutil -ltermcap
+GETTYOBJ= get_date.o gettytab.o
+GETTYSRC= get_date.c gettytab.c
+LIBEXEC= ${DESTDIR}/usr/libexec
+
+SRC1=  telnetd.c state.c termstat.c slc.c sys_term.c utility.c global.c
+OBJ1=  telnetd.o state.o termstat.o slc.o sys_term.o utility.o global.o
+
+OBJS=  ${OBJ1} ${GETTYOBJ}
+SRCS=  ${SRC1} ${GETTYSRC}
+
 VPATH= ../getty
 MAN=   telnetd.0
 VPATH= ../getty
 MAN=   telnetd.0
+CFLAGS=        -O ${DEFINES}
 
 all: telnetd
 
 telnetd: ${OBJS} ${LIBC}
 
 all: telnetd
 
 telnetd: ${OBJS} ${LIBC}
-       ${CC} -o $@ ${CFLAGS} ${OBJS} -lutil
+       ${CC} -o $@ ${CFLAGS} ${OBJS} ${LIBS}
 
 clean:
        rm -f ${OBJS} core telnetd
 
 clean:
        rm -f ${OBJS} core telnetd
@@ -38,7 +66,7 @@ depend: ${SRCS}
        mkdep ${CFLAGS} ${SRCS}
 
 install: ${MAN}
        mkdep ${CFLAGS} ${SRCS}
 
 install: ${MAN}
-       install -s -o bin -g bin -m 755 telnetd ${DESTDIR}/usr/libexec
+       install -s -o bin -g bin -m 755 telnetd ${LIBEXEC}
        install -c -o bin -g bin -m 444 ${MAN} ${DESTDIR}/usr/man/cat8
 
 lint: ${SRCS}
        install -c -o bin -g bin -m 444 ${MAN} ${DESTDIR}/usr/man/cat8
 
 lint: ${SRCS}
diff --git a/usr/src/libexec/telnetd/defs.h b/usr/src/libexec/telnetd/defs.h
new file mode 100644 (file)
index 0000000..fc58002
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)defs.h      5.1 (Berkeley) %G%
+ */
+
+/*
+ * Telnet server defines
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifndef        BSD
+# define       BSD 43
+#endif
+
+#if BSD > 43
+#define        USE_TERMIO
+#endif
+
+#ifdef CRAY
+# define       NEWINIT
+# define       SYSV_TERMIO
+# define       NO_GETTYTAB
+# define       signal sigset
+#endif /* CRAY */
+
+#ifdef SYSV_TERMIO
+# define       USE_TERMIO
+#endif
+
+#include <sys/socket.h>
+#ifndef        CRAY
+#include <sys/wait.h>
+#endif /* CRAY */
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+
+#include <arpa/telnet.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <strings.h>
+
+#ifndef        USE_TERMIO
+#include <sgtty.h>
+#else
+# ifdef        SYSV_TERMIO
+# include <termio.h>
+# else
+# include <termios.h>
+# endif
+#endif
+
+#ifdef CRAY
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+# ifdef        CRAY1
+# include <sys/pty.h>
+#  ifndef FD_ZERO
+# include <sys/select.h>
+#  endif /* FD_ZERO */
+# endif        /* CRAY1 */
+
+#include <memory.h>
+#endif /* CRAY */
+
+#if    defined(TCSIG) || defined(TIOCPKT_IOCTL)
+# define       LINEMODE
+# define       KLUDGELINEMODE
+#endif
+
+#ifndef        FD_SET
+typedef struct fd_set { int fds_bits[1]; } fd_set;
+
+#define        FD_SET(n, p)    ((p)->fds_bits[0] |= (1<<(n)))
+#define        FD_CLR(n, p)    ((p)->fds_bits[0] &= ~(1<<(n)))
+#define        FD_ISSET(n, p)  ((p)->fds_bits[0] & (1<<(n)))
+#define FD_ZERO(p)     ((p)->fds_bits[0] = 0)
+#endif /* FD_SET */
+
+#define        OPT_NO                  0               /* won't do this option */
+#define        OPT_YES                 1               /* will do this option */
+#define        OPT_YES_BUT_ALWAYS_LOOK 2
+#define        OPT_NO_BUT_ALWAYS_LOOK  3
+
+/*
+ * I/O data buffers defines
+ */
+#define        NETSLOP 64
+#ifdef CRAY
+#undef BUFSIZ
+#define BUFSIZ  2048
+#endif
+
+#define        NIACCUM(c)      {   *netip++ = c; \
+                           ncc++; \
+                       }
+
+/* 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  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 */
+       unsigned char   val;            /* the value of the special character */
+} slcent, *Slcent;
+
+typedef struct {
+       slcent          defset;         /* the default settings */
+       slcent          current;        /* the current settings */
+       unsigned char   *sptr;          /* a pointer to the char in */
+                                       /* system data structures */
+} slcfun, *Slcfun;
diff --git a/usr/src/libexec/telnetd/ext.h b/usr/src/libexec/telnetd/ext.h
new file mode 100644 (file)
index 0000000..2d3dbbb
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)ext.h       5.1 (Berkeley) %G%
+ */
+
+/*
+ * Telnet server variable declarations
+ */
+extern char    hisopts[256];
+extern char    myopts[256];
+extern char    hiswants[256];
+extern char    mywants[256];
+extern char    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 slcfun  slctab[NSLC + 1];       /* slc mapping table */
+
+char   *terminaltype;
+
+/*
+ * 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 */
+
+/*
+ * 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 */
+       baseline,               /* time started to do timed action */
+       gotDM;                  /* when did we last see a data mark */
+} clocks;
+
+
+#ifdef CRAY2
+extern int     needtermstat;
+#endif
+
+#ifndef        CRAY
+#define DEFAULT_IM     "\r\n\r\n4.3 BSD UNIX (%h) (%t)\r\n\r\r\n\r"
+#else
+#define DEFAULT_IM     "\r\n\r\nCray UNICOS (%h) (%t)\r\n\r\r\n\r"
+#endif
diff --git a/usr/src/libexec/telnetd/global.c b/usr/src/libexec/telnetd/global.c
new file mode 100644 (file)
index 0000000..6f5e9fc
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)global.c   5.1 (Berkeley) %G%";
+#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"
index ebad627..b8a1f08 100644 (file)
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- *     @(#)pathnames.h 5.2 (Berkeley) %G%
+ *     @(#)pathnames.h 5.3 (Berkeley) %G%
  */
 
  */
 
-#include <paths.h>
+#if BSD > 43
 
 
-#define        _PATH_LOGIN     "/usr/bin/login"
+# include <paths.h>
+
+# define       _PATH_LOGIN     "/usr/bin/login"
+
+#else
+# define       _PATH_TTY       "/dev/tty"
+# define       _PATH_LOGIN     "/bin/login"
+
+#endif
diff --git a/usr/src/libexec/telnetd/slc.c b/usr/src/libexec/telnetd/slc.c
new file mode 100644 (file)
index 0000000..ed82727
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)slc.c      5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+#include "telnetd.h"
+
+#ifdef LINEMODE
+/*
+ * local varibles
+ */
+static char    *def_slcbuf = (char *)0;;
+static int     def_slclen = 0;
+static int     slcchange;      /* change to slc is requested */
+static char    *slcptr;        /* pointer into slc buffer */
+static char    slcbuf[NSLC*6]; /* buffer for slc negotiation */
+
+/*
+ * send_slc
+ *
+ * Write out the current special characters to the client.
+ */
+send_slc()
+{
+       register 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].current.flag & SLC_LEVELBITS) != SLC_NOSUPPORT) {
+                       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.
+ */
+default_slc()
+{
+       register int i;
+
+       for (i = 1; i <= NSLC; i++) {
+               slctab[i].current.flag = slctab[i].defset.flag;
+               slctab[i].current.val = slctab[i].defset.val;
+               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.
+ */
+get_slc_defaults()
+{
+       register 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.
+ */
+add_slc(func, flag, val)
+       register unsigned char func, flag, val;
+{
+
+       if (func == 0xff)
+               *slcptr++ = 0xff;
+       *slcptr++ = func;
+
+       if (flag == 0xff)
+               *slcptr++ = 0xff;
+       *slcptr++ = flag;
+
+       if (val == 0xff)
+               *slcptr++ = 0xff;
+       *slcptr++ = val;
+
+}  /* 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.
+ */
+start_slc(getit)
+       register int getit;
+{
+
+       slcchange = 0;
+       if (getit)
+               init_termbuf();
+       (void) sprintf(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.
+ */
+end_slc(bufp)
+register char **bufp;
+{
+       register int len;
+       void netflush();
+
+       /*
+        * If a change has occured, 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;
+       }
+
+       if (slcptr > (slcbuf + 4)) {
+               if (bufp) {
+                       *bufp = &slcbuf[4];
+                       return(slcptr - slcbuf - 4);
+               } else {
+                       (void) sprintf(slcptr, "%c%c", IAC, SE);
+                       slcptr += 2;
+                       len = slcptr - slcbuf;
+                       writenet(slcbuf, len);
+                       netflush();  /* force it out immediately */
+               }
+       }
+
+}  /* end of end_slc */
+
+/*
+ * process_slc
+ *
+ * Figure out what to do about the client's slc
+ */
+process_slc(func, flag, val)
+       register unsigned char func, flag, val;
+{
+       register 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();
+               }
+               if (flag == SLC_DEFAULT || 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 {
+               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.
+ */
+change_slc(func, flag, val)
+       register unsigned char func, flag, val;
+{
+       register 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
+        * accomodate the request.
+        */
+       if (hislevel == SLC_NOSUPPORT) {
+               slctab[func].current.flag = flag;
+               slctab[func].current.val = val;
+               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 */
+
+/*
+ * 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.
+ */
+check_slc()
+{
+       register int i;
+
+       for (i = 1; i <= NSLC; i++) {
+#if    defined(USE_TERMIO) && defined(SYSV_TERMIO)
+               /*
+                * 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 (!tty_isediting() && i == SLC_EOF)
+                       continue;
+#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
+               if (slctab[i].sptr &&
+                               (*(slctab[i].sptr) != slctab[i].current.val)) {
+                       slctab[i].current.val = *(slctab[i].sptr);
+                       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.
+ */
+do_opt_slc(ptr, len)
+register char *ptr;
+register int len;
+{
+       register unsigned char func, flag, val;
+       register char *end = (char *)(ptr + len);
+       char *malloc();
+
+       if (terminit()) {  /* go ahead */
+               while (ptr < end) {
+                       func = *ptr++;
+                       if (ptr >= end) break;
+                       flag = *ptr++;
+                       if (ptr >= end) break;
+                       val = *ptr++;
+
+                       process_slc(func, flag, val);
+
+               }
+       } else {
+               /*
+                * save this slc buffer if it is the first, otherwise dump
+                * it.
+                */
+               if (def_slcbuf == (char *)0) {
+                       def_slclen = len;
+                       def_slcbuf = malloc((unsigned)len);
+                       if (def_slcbuf == (char *)0)
+                               return;  /* too bad */
+                       bcopy(ptr, def_slcbuf, len);
+               }
+       }
+
+}  /* end of do_opt_slc */
+
+/*
+ * deferslc
+ *
+ * Do slc stuff that was deferred.
+ */
+deferslc()
+{
+       if (def_slcbuf) {
+               start_slc(1);
+               do_opt_slc(def_slcbuf, def_slclen);
+               end_slc(0);
+               free(def_slcbuf);
+               def_slcbuf = (char *)0;
+               def_slclen = 0;
+       }
+
+}  /* end of deferslc */
+
+#endif /* LINEMODE */
diff --git a/usr/src/libexec/telnetd/state.c b/usr/src/libexec/telnetd/state.c
new file mode 100644 (file)
index 0000000..986e383
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)state.c    5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+#include "telnetd.h"
+
+char   doopt[] = { IAC, DO, '%', 'c', 0 };
+char   dont[] = { IAC, DONT, '%', 'c', 0 };
+char   will[] = { IAC, WILL, '%', 'c', 0 };
+char   wont[] = { IAC, WONT, '%', 'c', 0 };
+int    not42 = 1;
+
+/*
+ * Buffer for sub-options, and macros
+ * for suboptions buffer manipulations
+ */
+char   subbuffer[100], *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)
+
+
+
+/*
+ * 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 " */
+
+telrcv()
+{
+       register int c;
+       static int state = TS_DATA;
+#ifdef CRAY2
+       char *opfrontp = pfrontp;
+#endif
+
+       while (ncc > 0) {
+               if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
+                       break;
+               c = *netip++ & 0377, ncc--;
+               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') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
+                               /*
+                                * If we are operating in linemode,
+                                * convert to local end-of-line.
+                                */
+                               if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
+                                       netip++; ncc--;
+                                       c = '\n';
+                               } else {
+                                       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:
+                               interrupt();
+                               break;
+
+                       case BREAK:
+                               sendbrk();
+                               break;
+
+                       /*
+                        * Are You There?
+                        */
+                       case AYT:
+                               (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
+                               nfrontp += 9;
+                               break;
+
+                       /*
+                        * Abort Output
+                        */
+                       case AO:
+                           {
+                               ptyflush();     /* half-hearted */
+                               init_termbuf();
+
+                               if (slctab[SLC_AO].sptr &&
+                                   *slctab[SLC_AO].sptr != '\377') {
+                                       *pfrontp++ = *slctab[SLC_AO].sptr;
+                               }
+
+                               netclear();     /* clear buffer back */
+                               *nfrontp++ = IAC;
+                               *nfrontp++ = DM;
+                               neturg = nfrontp-1; /* off by one XXX */
+                               break;
+                           }
+
+                       /*
+                        * Erase Character and
+                        * Erase Line
+                        */
+                       case EC:
+                       case EL:
+                           {
+                               unsigned char ch;
+
+                               ptyflush();     /* half-hearted */
+                               init_termbuf();
+                               ch = (c == EC) ? *slctab[SLC_EC].sptr :
+                                                *slctab[SLC_EL].sptr;
+                               if (ch != '\377')
+                                       *pfrontp++ = ch;
+                               break;
+                           }
+
+                       /*
+                        * Check for urgent data...
+                        */
+                       case DM:
+                               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 (hisopts[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.
+                                        */
+                                       SB_TERM();
+                                       suboption();
+                                       state = TS_IAC;
+                                       goto gotiac;
+                               }
+                               SB_ACCUM(c);
+                               state = TS_SB;
+                       } else {
+                               SB_TERM();
+                               suboption();    /* handle sub-option */
+                               state = TS_DATA;
+                       }
+                       break;
+
+               case TS_WILL:
+                       willoption(c, 0);
+                       state = TS_DATA;
+                       continue;
+
+               case TS_WONT:
+                       wontoption(c, 0);
+                       state = TS_DATA;
+                       continue;
+
+               case TS_DO:
+                       dooption(c, 0);
+                       state = TS_DATA;
+                       continue;
+
+               case TS_DONT:
+                       dontoption(c, 0);
+                       state = TS_DATA;
+                       continue;
+
+               default:
+                       syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
+                       printf("telnetd: panic state=%d\n", state);
+                       exit(1);
+               }
+       }
+#if    CRAY2
+       if (!linemode) {
+               char    xptyobuf[BUFSIZ+NETSLOP];
+               char    xbuf2[BUFSIZ];
+               register char *cp;
+               int n = pfrontp - opfrontp, oc;
+               bcopy(opfrontp, xptyobuf, n);
+               pfrontp = opfrontp;
+               pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
+                                       xbuf2, &oc, BUFSIZ);
+               for (cp = xbuf2; oc > 0; --oc)
+                       if ((*nfrontp++ = *cp++) == IAC)
+                               *nfrontp++ = IAC;
+       }
+#endif /* CRAY2 */
+}  /* end of telrcv */
+
+/*
+ * The will/wont/do/dont state machines are based on Dave Borman's
+ * Telnet option processing state machine.  We keep track of the full
+ * state of the option negotiation with the following state variables
+ *     myopts, hisopts - The last fully negotiated state for each
+ *                     side of the connection.
+ *     mywants, hiswants - The state we wish to be in after a completed
+ *                     negotiation.  (hiswants is slightly misleading,
+ *                     this is more precisely the state I want him to
+ *                     be in.
+ *     resp - We count the number of requests we have sent out.
+ *
+ * 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.
+ *
+ * In processing options, request signifies whether this is a request
+ * to send or a response.  request is true if this is a request to 
+ * send generated locally.
+ */
+willoption(option, request)
+       int option, request;
+{
+       int changeok = 0;
+       char *fmt = (char *)0;
+
+    /*
+     * process input from peer.
+     */
+    if (request == 0) {
+               switch (option) {
+
+               case TELOPT_BINARY:
+                       init_termbuf();
+                       tty_binaryin(1);
+                       set_termbuf();
+                       changeok++;
+                       break;
+
+               case TELOPT_ECHO:
+                       not42 = 0;              /* looks like a 4.2 system */
+                       /*
+                        * Now, in a 4.2 system, to break them out of ECHOing
+                        * (to the terminal) mode, we need to send a
+                        * "WILL ECHO".  Kludge upon kludge!
+                        */
+                       if (myopts[TELOPT_ECHO] == OPT_YES) {
+                               dooption(TELOPT_ECHO, 1);
+                       }
+                       /*
+                        * Fool the state machine into sending a don't.
+                        * This also allows the initial echo sending code to
+                        * break out of the loop that it is
+                        * in.  (Look in telnet())
+                        */
+                       hiswants[TELOPT_ECHO] = OPT_NO;
+                       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);
+                               dontoption(TELOPT_SGA, 0);
+                       }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       /*
+                        * cheat the state machine so that it
+                        * looks like we never sent the TM at
+                        * all.  The bad part of this is that
+                        * if the client sends a will TM on his
+                        * own to turn on linemode, then he
+                        * won't get a response.
+                        */
+                       hiswants[TELOPT_TM] = OPT_NO;
+                       resp[TELOPT_TM]--;
+                       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:
+#ifdef LINEMODE
+               case TELOPT_LINEMODE:
+#endif LINEMODE
+                       changeok++;
+                       break;
+
+               default:
+                       break;
+               }
+
+       }
+
+       if (request) {
+               if (!((resp[option] == 0 && hisopts[option] == OPT_YES) ||
+                   hiswants[option] == OPT_YES)) {
+                       hiswants[option] = OPT_YES;
+                       fmt = doopt;
+                       resp[option]++;
+               }
+       } else {
+               if (resp[option]) {
+                       resp[option]--;
+                       if (resp[option] && hisopts[option] == OPT_YES)
+                               resp[option]--;
+               }
+               if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) {
+                       if (changeok)
+                               hiswants[option] = OPT_YES;
+                       else
+                               resp[option]++;
+                       fmt = (hiswants[option] ? doopt : dont);
+               }
+               hisopts[option] = OPT_YES;
+       }
+
+       if (fmt) {
+               (void) sprintf(nfrontp, fmt, option);
+               nfrontp += sizeof (dont) - 2;
+       }
+
+       /*
+        * Handle other processing that should occur after we have
+        * responded to client input.
+        */
+       if (!request) {
+               switch (option) {
+#ifdef LINEMODE
+               case TELOPT_LINEMODE:
+# ifdef        KLUDGELINEMODE
+                       /*
+                        * Note client's desire to use linemode.
+                        */
+                       lmodetype = REAL_LINEMODE;
+# endif        /* KLUDGELINEMODE */
+                       clientstat(TELOPT_LINEMODE, WILL, 0);
+                       break;
+#endif LINEMODE
+               
+               default:
+                       break;
+               }
+       }
+
+}  /* end of willoption */
+
+wontoption(option, request)
+       int option, request;
+{
+       char *fmt = (char *)0;
+
+       /*
+        * Process client input.
+        */
+       if (!request) {
+               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)
+# endif        /* KLUDGELINEMODE */
+                               clientstat(TELOPT_LINEMODE, WONT, 0);
+                       break;
+#endif LINEMODE
+
+               case TELOPT_TM:
+#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
+                       if (lmodetype < REAL_LINEMODE) {
+                               lmodetype = NO_LINEMODE;
+                               clientstat(TELOPT_LINEMODE, WONT, 0);
+                               dooption(TELOPT_SGA, 0);
+                       }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       /*
+                        * If we get a WONT TM, and had sent a DO TM, don't
+                        * respond with a DONT TM, just leave it as is.
+                        * Short circut the state machine to achive this.
+                        * The bad part of this is that if the client sends
+                        * a WONT TM on his own to turn off linemode, then he
+                        * won't get a response.
+                        */
+                       hiswants[TELOPT_TM] = OPT_NO;
+                       resp[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;
+
+               default:
+                       break;
+               }
+       }
+
+
+       if (request) {
+               if (!((resp[option] == 0 && hisopts[option] == OPT_NO) ||
+                       hiswants[option] == OPT_NO)) {
+                       hiswants[option] = OPT_NO;
+                       fmt = dont;
+                       resp[option]++;
+               }
+       } else {
+               if (resp[option]) {
+                       resp[option]--;
+                       if (resp[option] && hisopts[option] == OPT_NO)
+                               resp[option]--;
+               }
+               if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) {
+                       /* it is always ok to change to negative state */
+                       hiswants[option] = OPT_NO;
+                       fmt = dont;
+               }
+               hisopts[option] = OPT_NO;
+       }
+
+       if (fmt) {
+               (void) sprintf(nfrontp, fmt, option);
+               nfrontp += sizeof (doopt) - 2;
+       }
+
+}  /* end of wontoption */
+
+dooption(option, request)
+       int option, request;
+{
+       int changeok = 0;
+       char *fmt = (char *)0;
+
+       /*
+        * Process client input.
+        */
+       if (!request) {
+               switch (option) {
+               case TELOPT_ECHO:
+#ifdef LINEMODE
+                       if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
+#endif
+                               init_termbuf();
+                               tty_setecho(1);
+                               set_termbuf();
+#ifdef LINEMODE
+                       }
+#endif
+                       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;
+                       }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+                       changeok++;
+                       break;
+
+               case TELOPT_STATUS:
+                       changeok++;
+                       break;
+
+               case TELOPT_TM:
+               case TELOPT_LINEMODE:
+               case TELOPT_TTYPE:
+               case TELOPT_NAWS:
+               case TELOPT_TSPEED:
+               case TELOPT_LFLOW:
+               default:
+                       break;
+               }
+       }
+
+       if (request) {
+               if (!((resp[option] == 0 && myopts[option] == OPT_YES) ||
+                   mywants[option] == OPT_YES)) {
+                       mywants[option] = OPT_YES;
+                       fmt = will;
+                       resp[option]++;
+               }
+       } else {
+               if (resp[option]) {
+                       resp[option]--;
+                       if (resp[option] && myopts[option] == OPT_YES)
+                               resp[option]--;
+               }
+               if ((resp[option] == 0) && (mywants[option] != OPT_YES)) {
+                       if (changeok)
+                               mywants[option] = OPT_YES;
+                       else
+                               resp[option]++;
+                       fmt = (mywants[option] ? will : wont);
+               }
+               myopts[option] = OPT_YES;
+       }
+
+       if (fmt) {
+               (void) sprintf(nfrontp, fmt, option);
+               nfrontp += sizeof (doopt) - 2;
+       }
+
+}  /* end of dooption */
+
+
+dontoption(option, request)
+       int option, request;
+{
+       char *fmt = (char *)0;
+
+       /*
+        * Process client input.
+        */
+       if (!request) {
+               switch (option) {
+               case TELOPT_BINARY:
+                       init_termbuf();
+                       tty_binaryout(0);
+                       set_termbuf();
+                       break;
+
+               case TELOPT_ECHO:               /* we should stop echoing */
+#ifdef LINEMODE
+                       if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
+#endif
+                               init_termbuf();
+                               tty_setecho(0);
+                               set_termbuf();
+#ifdef LINEMODE
+                       }
+#endif
+                       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) {
+                               /*
+                                * The client is asking us to turn linemode
+                                * on.
+                                */
+                               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.
+                                */
+                       }
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+
+               default:
+                       break;
+               }
+       }
+
+       if (request) {
+               if (!((resp[option] == 0 && myopts[option] == OPT_NO) ||
+                   mywants[option] == OPT_NO)) {
+                       mywants[option] = OPT_NO;
+                       fmt = wont;
+                       resp[option]++;
+               }
+       } else {
+               if (resp[option]) {
+                       resp[option]--;
+                       if (resp[option] && myopts[option] == OPT_NO)
+                               resp[option]--;
+               }
+               if ((resp[option] == 0) && (mywants[option] != OPT_NO)) {
+                       mywants[option] = OPT_NO;
+                       fmt = wont;
+               }
+               myopts[option] = OPT_NO;
+       }
+
+       if (fmt) {
+           (void) sprintf(nfrontp, fmt, option);
+           nfrontp += sizeof (wont) - 2;
+       }
+
+}  /* end of dontoption */
+
+/*
+ * 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
+ */
+suboption()
+{
+    register int subchar;
+
+    subchar = SB_GET();
+    switch (subchar) {
+    case TELOPT_TSPEED: {
+       register int xspeed, rspeed;
+
+       if (hisopts[TELOPT_TSPEED] == OPT_NO)   /* Ignore if option disabled */
+               break;
+
+       settimer(tspeedsubopt);
+
+       if (SB_EOF() || SB_GET() != TELQUAL_IS)
+               return;
+
+       xspeed = atoi(subpointer);
+
+       while (SB_GET() != ',' && !SB_EOF());
+       if (SB_EOF())
+               return;
+
+       rspeed = atoi(subpointer);
+       clientstat(TELOPT_TSPEED, xspeed, rspeed);
+
+       break;
+
+    }  /* end of case TELOPT_TSPEED */
+
+    case TELOPT_TTYPE: {               /* Yaaaay! */
+       static char terminalname[5+41] = "TERM=";
+
+       if (hisopts[TELOPT_TSPEED] == OPT_NO)   /* Ignore if option disabled */
+               break;
+       settimer(ttypesubopt);
+
+       if (SB_GET() != TELQUAL_IS) {
+           return;             /* ??? XXX but, this is the most robust */
+       }
+
+       terminaltype = terminalname+sizeof("TERM=")-1;
+
+       while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
+                                                                   !SB_EOF()) {
+           register int c;
+
+           c = SB_GET();
+           if (isupper(c)) {
+               c = tolower(c);
+           }
+           *terminaltype++ = c;    /* accumulate name */
+       }
+       *terminaltype = 0;
+       terminaltype = terminalname;
+       break;
+    }  /* end of case TELOPT_TTYPE */
+
+    case TELOPT_NAWS: {
+       register int xwinsize, ywinsize;
+
+       if (hisopts[TELOPT_NAWS] == OPT_NO)     /* 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: {
+       register int request;
+
+       if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* 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 preceeded by WILL or WONT */
+               /*
+                * Process suboption buffer of slc's
+                */
+               start_slc(1);
+               do_opt_slc(subpointer, subend - subpointer);
+               end_slc(0);
+
+       } else if (request == LM_MODE) {
+               useeditmode = SB_GET();  /* get mode flag */
+               clientstat(LM_MODE, 0, 0);
+       }
+
+       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;
+       }
+
+    }  /* end of case TELOPT_LINEMODE */
+#endif
+    case TELOPT_STATUS: {
+       int mode;
+
+       mode = SB_GET();
+       switch (mode) {
+       case TELQUAL_SEND:
+           if (myopts[TELOPT_STATUS] == OPT_YES)
+               send_status();
+           break;
+
+       case TELQUAL_IS:
+           break;
+
+       default:
+           break;
+       }
+    }
+
+    default:
+       break;
+    }  /* end of switch */
+
+}  /* end of suboption */
+
+#define        ADD(c)   *ncp++ = c;
+#define        ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
+send_status()
+{
+       char statusbuf[256];
+       register char *ncp;
+       register int i;
+
+       ncp = statusbuf;
+
+       netflush();     /* get rid of anything waiting to go out */
+
+       ADD(IAC);
+       ADD(SB);
+       ADD(TELOPT_STATUS);
+       ADD(TELQUAL_IS);
+
+       for (i = 0; i < NTELOPTS; i++) {
+               if (myopts[i] == OPT_YES) {
+                       ADD(WILL);
+                       ADD_DATA(i);
+                       if (i == IAC)
+                               ADD(IAC);
+               }
+               if (hisopts[i] == OPT_YES) {
+                       ADD(DO);
+                       ADD_DATA(i);
+                       if (i == IAC)
+                               ADD(IAC);
+               }
+       }
+
+#ifdef LINEMODE
+       if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
+               char *cp, *cpe;
+               int len;
+
+               ADD(SB);
+               ADD(TELOPT_LINEMODE);
+               ADD(LM_MODE);
+               ADD_DATA(editmode);
+               if (editmode == IAC)
+                       ADD(IAC);
+               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 */
+}
diff --git a/usr/src/libexec/telnetd/sys_term.c b/usr/src/libexec/telnetd/sys_term.c
new file mode 100644 (file)
index 0000000..8152239
--- /dev/null
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sys_term.c 5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+#include "telnetd.h"
+#include "pathnames.h"
+
+#ifdef NEWINIT
+#include <initreq.h>
+#else  /* NEWINIT*/
+#include <utmp.h>
+struct utmp wtmp;
+
+# ifndef CRAY
+char   wtmpf[] = "/usr/adm/wtmp";
+char   utmpf[] = "/etc/utmp";
+# else /* CRAY */
+char   wtmpf[] = "/etc/wtmp";
+# endif        /* CRAY */
+#endif /* NEWINIT */
+
+#define SCPYN(a, b)    (void) strncpy(a, b, sizeof(a))
+#define SCMPN(a, b)    strncmp(a, b, sizeof(a))
+
+#include <sys/tty.h>
+#ifdef t_erase
+#undef t_erase
+#undef t_kill
+#undef t_intrc
+#undef t_quitc
+#undef t_startc
+#undef t_stopc
+#undef t_eofc
+#undef t_brkc
+#undef t_suspc
+#undef t_dsuspc
+#undef t_rprntc
+#undef t_flushc
+#undef t_werasc
+#undef t_lnextc
+#endif
+
+#ifndef        USE_TERMIO
+struct termbuf {
+       struct sgttyb sg;
+       struct tchars tc;
+       struct ltchars ltc;
+       int state;
+       int lflags;
+} termbuf, termbuf2;
+#else  /* USE_TERMIO */
+# ifndef EXTPROC
+# define EXTPROC 0400
+# endif
+# ifdef        SYSV_TERMIO
+#      define termios termio
+# endif
+struct termios termbuf, termbuf2;      /* pty control structure */
+#endif /* USE_TERMIO */
+
+/*
+ * 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.
+ */
+
+init_termbuf()
+{
+#ifndef        USE_TERMIO
+       (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg);
+       (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc);
+       (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc);
+# ifdef        TIOCGSTATE
+       (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state);
+# endif
+#else
+       (void) ioctl(pty, TCGETA, (char *)&termbuf);
+#endif
+       termbuf2 = termbuf;
+}
+
+#if    defined(LINEMODE) && defined(TIOCPKT_IOCTL)
+copy_termbuf(cp, len)
+char *cp;
+int len;
+{
+       if (len > sizeof(termbuf))
+               len = sizeof(termbuf);
+       bcopy(cp, (char *)&termbuf, len);
+       termbuf2 = termbuf;
+}
+#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
+
+set_termbuf()
+{
+       /*
+        * Only make the necessary changes.
+        */
+#ifndef        USE_TERMIO
+       if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg)))
+               (void) ioctl(pty, TIOCSETP, (char *)&termbuf.sg);
+       if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc)))
+               (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc);
+       if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc,
+                                                       sizeof(termbuf.ltc)))
+               (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc);
+       if (termbuf.lflags != termbuf2.lflags)
+               (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags);
+#else  /* USE_TERMIO */
+       if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
+               (void) ioctl(pty, TCSETA, (char *)&termbuf);
+# ifdef        CRAY2
+       needtermstat = 1;
+# endif
+#endif /* USE_TERMIO */
+}
+
+
+/*
+ * 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.
+ */
+
+#ifndef        USE_TERMIO
+spcset(func, valp, valpp)
+int func;
+unsigned char *valp;
+unsigned char **valpp;
+{
+       switch(func) {
+       case SLC_EOF:
+               *valp = termbuf.tc.t_eofc;
+               *valpp = (unsigned char *)&termbuf.tc.t_eofc;
+               return(SLC_VARIABLE);
+       case SLC_EC:
+               *valp = termbuf.sg.sg_erase;
+               *valpp = (unsigned char *)&termbuf.sg.sg_erase;
+               return(SLC_VARIABLE);
+       case SLC_EL:
+               *valp = termbuf.sg.sg_kill;
+               *valpp = (unsigned char *)&termbuf.sg.sg_kill;
+               return(SLC_VARIABLE);
+       case SLC_IP:
+               *valp = termbuf.tc.t_intrc;
+               *valpp = (unsigned char *)&termbuf.tc.t_intrc;
+               return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+       case SLC_ABORT:
+               *valp = termbuf.tc.t_quitc;
+               *valpp = (unsigned char *)&termbuf.tc.t_quitc;
+               return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+       case SLC_XON:
+               *valp = termbuf.tc.t_startc;
+               *valpp = (unsigned char *)&termbuf.tc.t_startc;
+               return(SLC_VARIABLE);
+       case SLC_XOFF:
+               *valp = termbuf.tc.t_stopc;
+               *valpp = (unsigned char *)&termbuf.tc.t_stopc;
+               return(SLC_VARIABLE);
+       case SLC_AO:
+               *valp = termbuf.ltc.t_flushc;
+               *valpp = (unsigned char *)&termbuf.ltc.t_flushc;
+               return(SLC_VARIABLE);
+       case SLC_SUSP:
+               *valp = termbuf.ltc.t_suspc;
+               *valpp = (unsigned char *)&termbuf.ltc.t_suspc;
+               return(SLC_VARIABLE);
+       case SLC_EW:
+               *valp = termbuf.ltc.t_werasc;
+               *valpp = (unsigned char *)&termbuf.ltc.t_werasc;
+               return(SLC_VARIABLE);
+       case SLC_RP:
+               *valp = termbuf.ltc.t_rprntc;
+               *valpp = (unsigned char *)&termbuf.ltc.t_rprntc;
+               return(SLC_VARIABLE);
+       case SLC_LNEXT:
+               *valp = termbuf.ltc.t_lnextc;
+               *valpp = (unsigned char *)&termbuf.ltc.t_lnextc;
+               return(SLC_VARIABLE);
+       case SLC_BRK:
+       case SLC_SYNCH:
+       case SLC_AYT:
+       case SLC_EOR:
+               *valp = 0;
+               *valpp = 0;
+               return(SLC_DEFAULT);
+       default:
+               *valp = 0;
+               *valpp = 0;
+               return(SLC_NOSUPPORT);
+       }
+}
+
+#else  /* USE_TERMIO */
+
+spcset(func, valp, valpp)
+int func;
+unsigned char *valp;
+unsigned char **valpp;
+{
+       switch(func) {
+       case SLC_EOF:
+               *valp = termbuf.c_cc[VEOF];
+               *valpp = &termbuf.c_cc[VEOF];
+               return(SLC_VARIABLE);
+       case SLC_EC:
+               *valp = termbuf.c_cc[VERASE];
+               *valpp = &termbuf.c_cc[VERASE];
+               return(SLC_VARIABLE);
+       case SLC_EL:
+               *valp = termbuf.c_cc[VKILL];
+               *valpp = &termbuf.c_cc[VKILL];
+               return(SLC_VARIABLE);
+       case SLC_IP:
+               *valp = termbuf.c_cc[VINTR];
+               *valpp = &termbuf.c_cc[VINTR];
+               return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+       case SLC_ABORT:
+               *valp = termbuf.c_cc[VQUIT];
+               *valpp = &termbuf.c_cc[VQUIT];
+               return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
+       case SLC_XON:
+               *valp = 0x13;
+               *valpp = 0;
+               return(SLC_DEFAULT);
+       case SLC_XOFF:
+               *valp = 0x11;
+               *valpp = 0;
+               return(SLC_DEFAULT);
+       case SLC_EW:
+       case SLC_RP:
+       case SLC_LNEXT:
+       case SLC_BRK:
+       case SLC_SYNCH:
+       case SLC_AYT:
+       case SLC_EOR:
+               *valp = 0;
+               *valpp = 0;
+               return(SLC_DEFAULT);
+       case SLC_AO:
+       case SLC_SUSP:
+       default:
+               *valp = 0;
+               *valpp = 0;
+               return(SLC_NOSUPPORT);
+       }
+}
+#endif /* USE_TERMIO */
+
+/*
+ * 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.
+ */
+char *line = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+getpty()
+{
+       register int p;
+#ifndef CRAY
+       register char c, *p1, *p2;
+       register int i;
+
+       (void) sprintf(line, "/dev/ptyXX");
+       p1 = &line[8];
+       p2 = &line[9];
+
+       for (c = 'p'; c <= 's'; c++) {
+               struct stat stb;
+
+               *p1 = c;
+               *p2 = '0';
+               if (stat(line, &stb) < 0)
+                       break;
+               for (i = 0; i < 16; i++) {
+                       *p2 = "0123456789abcdef"[i];
+                       p = open(line, 2);
+                       if (p > 0) {
+                               line[5] = 't';
+                               return(p);
+                       }
+               }
+       }
+#else  /* CRAY */
+       register int npty;
+       extern lowpty, highpty;
+
+       for (npty = lowpty; npty <= highpty; npty++) {
+               (void) sprintf(line, "/dev/pty/%03d", npty);
+               p = open(line, 2);
+               if (p < 0)
+                       continue;
+               (void) sprintf(line, "/dev/ttyp%03d", npty);
+               if (access(line, 6) == 0)
+                       return(p);
+               else {
+                       /* no tty side to pty so skip it */
+                       (void) close(p);
+               }
+       }
+#endif /* CRAY */
+       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_tspeed(val)     Set transmit speed to val.
+ * tty_rspeed(val)     Set receive speed to val.
+ */
+
+tty_flowmode()
+{
+#ifndef USE_TERMIO
+       return((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0);
+#else
+       return(termbuf.c_iflag & IXON ? 1 : 0);
+#endif
+}
+
+tty_linemode()
+{
+#ifndef        USE_TERMIO
+       return(termbuf.state & TS_EXTPROC);
+#else
+       return(termbuf.c_lflag & EXTPROC);
+#endif
+}
+
+tty_setlinemode(on)
+int on;
+{
+#ifdef TIOCEXT
+       (void) ioctl(pty, TIOCEXT, (char *)&on);
+#else  /* !TIOCEXT */
+#ifdef EXTPROC
+       if (on)
+               termbuf.c_lflag |= EXTPROC;
+       else
+               termbuf.c_lflag &= ~EXTPROC;
+#endif
+       set_termbuf();
+#endif /* TIOCEXT */
+}
+
+tty_isecho()
+{
+#ifndef USE_TERMIO
+       return (termbuf.sg.sg_flags & ECHO);
+#else
+       return (termbuf.c_lflag & ECHO);
+#endif
+}
+#endif /* LINEMODE */
+
+tty_setecho(on)
+{
+#ifndef        USE_TERMIO
+       if (on)
+               termbuf.sg.sg_flags |= ECHO|CRMOD;
+       else
+               termbuf.sg.sg_flags &= ~(ECHO|CRMOD);
+#else
+       if (on)
+               termbuf.c_lflag |= ECHO;
+       else
+               termbuf.c_lflag &= ~ECHO;
+#endif
+}
+
+#if    defined(LINEMODE) && defined(KLUDGELINEMODE)
+tty_israw()
+{
+#ifndef USE_TERMIO
+       return(termbuf.sg.sg_flags & RAW);
+#else
+       return(!(termbuf.c_lflag & ICANON));
+#endif
+}
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+
+tty_binaryin(on)
+{
+#ifndef        USE_TERMIO
+       if (on)
+               termbuf.lflags |= LPASS8;
+       else
+               termbuf.lflags &= ~LPASS8;
+#else
+       if (on) {
+               termbuf.c_lflag &= ~ISTRIP;
+       } else {
+               termbuf.c_lflag |= ISTRIP;
+       }
+#endif
+}
+
+tty_binaryout(on)
+{
+#ifndef        USE_TERMIO
+       if (on)
+               termbuf.lflags |= LLITOUT;
+       else
+               termbuf.lflags &= ~LLITOUT;
+#else
+       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;
+       }
+#endif
+}
+
+tty_isbinaryin()
+{
+#ifndef        USE_TERMIO
+       return(termbuf.lflags & LPASS8);
+#else
+       return(!(termbuf.c_lflag & ISTRIP));
+#endif
+}
+
+tty_isbinaryout()
+{
+#ifndef        USE_TERMIO
+       return(termbuf.lflags & LLITOUT);
+#else
+       return(mywants[TELOPT_BINARY] == OPT_YES);
+#endif
+}
+
+#ifdef LINEMODE
+tty_isediting()
+{
+#ifndef USE_TERMIO
+       return(!(termbuf.sg.sg_flags & (CBREAK|RAW)));
+#else
+       return(termbuf.c_lflag & ICANON);
+#endif
+}
+
+tty_istrapsig()
+{
+#ifndef USE_TERMIO
+       return(!(termbuf.sg.sg_flags&RAW));
+#else
+       return(termbuf.c_lflag & ISIG);
+#endif
+}
+
+tty_setedit(on)
+int on;
+{
+#ifndef USE_TERMIO
+       if (on)
+               termbuf.sg.sg_flags &= ~CBREAK;
+       else
+               termbuf.sg.sg_flags |= CBREAK;
+#else
+       if (on)
+               termbuf.c_lflag |= ICANON;
+       else
+               termbuf.c_lflag &= ~ICANON;
+#endif
+}
+
+tty_setsig(on)
+int on;
+{
+#ifndef        USE_TERMIO
+       if (on)
+               ;
+#else
+       if (on)
+               termbuf.c_lflag |= ISIG;
+       else
+               termbuf.c_lflag &= ~ISIG;
+#endif
+}
+#endif /* LINEMODE */
+
+/*
+ * A table of available terminal speeds
+ */
+struct termspeeds {
+       int     speed;
+       int     value;
+} termspeeds[] = {
+       { 0,     B0 },    { 50,    B50 },   { 75,    B75 },
+       { 110,   B110 },  { 134,   B134 },  { 150,   B150 },
+       { 200,   B200 },  { 300,   B300 },  { 600,   B600 },
+       { 1200,  B1200 }, { 1800,  B1800 }, { 2400,  B2400 },
+       { 4800,  B4800 }, { 9600,  B9600 }, { 19200, B9600 },
+       { 38400, B9600 }, { -1,    B9600 }
+};
+
+tty_tspeed(val)
+{
+       register struct termspeeds *tp;
+
+       for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
+               ;
+#ifndef        USE_TERMIO
+       termbuf.sg.sg_ospeed = tp->value;
+#else
+# ifdef        SYSV_TERMIO
+       termbuf.c_cflag &= ~CBAUD;
+       termbuf.c_cflag |= tp->value;
+# else
+       termbuf.c_ospeed = tp->value;
+# endif
+#endif
+}
+
+tty_rspeed(val)
+{
+       register struct termspeeds *tp;
+
+       for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
+               ;
+#ifndef        USE_TERMIO
+       termbuf.sg.sg_ispeed = tp->value;
+#else
+# ifdef        SYSV_TERMIO
+       termbuf.c_cflag &= ~CBAUD;
+       termbuf.c_cflag |= tp->value;
+# else
+       termbuf.c_ispeed = tp->value;
+# endif
+#endif
+}
+
+#ifdef CRAY2
+tty_isnewmap()
+{
+       return((termbuf.c_oflag & OPOST) && (termbuf.c_oflag & ONLCR) &&
+                       !(termbuf.c_oflag & ONLRET));
+}
+#endif
+
+#ifdef CRAY
+# ifndef NEWINIT
+extern struct utmp wtmp;
+extern char wtmpf[];
+# else /* NEWINIT */
+int    gotalarm;
+nologinproc()
+{
+       gotalarm++;
+}
+# endif        /* NEWINIT */
+#endif /* CRAY */
+
+/*
+ * 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.
+ */
+getptyslave()
+{
+       register int t = -1;
+
+#ifndef        CRAY
+       /*
+        * Disassociate self from control terminal and open ttyp side.
+        * Set important flags on ttyp and ptyp.
+        */
+       t = open(_PATH_TTY, O_RDWR);
+       if (t >= 0) {
+               (void) ioctl(t, TIOCNOTTY, (char *)0);
+               (void) close(t);
+       }
+
+       t = open(line, O_RDWR);
+       if (t < 0)
+               fatalperror(net, line);
+       if (fchmod(t, 0))
+               fatalperror(net, line);
+       (void) signal(SIGHUP, SIG_IGN);
+       vhangup();
+       (void) signal(SIGHUP, SIG_DFL);
+       t = open(line, O_RDWR);
+       if (t < 0)
+               fatalperror(net, line);
+
+       init_termbuf();
+#ifndef        USE_TERMIO
+       termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO;
+       termbuf.sg.sg_ospeed = termbuf.sg.sg_ispeed = B9600;
+#else
+       termbuf.c_lflag |= ECHO;
+       termbuf.c_oflag |= ONLCR|OXTABS;
+       termbuf.c_iflag |= ICRNL;
+       termbuf.c_iflag &= ~IXOFF;
+# ifdef        SYSV_TERMIO
+       termbuf.sg.sg_ospeed = termbuf.sg.sg_ispeed = B9600;
+# else SYSV_TERMIO
+       termbuf.c_ospeed = termbuf.c_ispeed = B9600;
+# endif
+#endif
+       set_termbuf();
+#else  /* CRAY */
+       (void) chown(line, 0, 0);
+       (void) chmod(line, 0600);
+#endif /* CRAY */
+       return(t);
+}
+
+#ifdef NEWINIT
+char *gen_id = "fe";
+#endif
+
+/*
+ * startslave(t, host)
+ *
+ * Given a file descriptor (t) for a tty, and a hostname, do whatever
+ * is necessary to startup the login process on the slave side of the pty.
+ */
+
+/* ARGSUSED */
+startslave(t, host)
+int t;
+char *host;
+{
+       register int i;
+       long time();
+
+#ifndef        NEWINIT
+# ifdef        CRAY
+       utmp_sig_init();
+# endif        /* CRAY */
+
+       if ((i = fork()) < 0)
+               fatalperror(net, "fork");
+       if (i) {
+# ifdef        CRAY
+               /*
+                * Cray parent will create utmp entry for child and send
+                * signal to child to tell when done.  Child waits for signal
+                * before doing anything important.
+                */
+               register int pid = i;
+
+               setpgrp();
+               (void) signal(SIGUSR1, func);   /* reset handler to default */
+               /*
+                * Create utmp entry for child
+                */
+               (void) time(&wtmp.ut_time);
+               wtmp.ut_type = LOGIN_PROCESS;
+               wtmp.ut_pid = pid;
+               SCPYN(wtmp.ut_user, "LOGIN");
+               SCPYN(wtmp.ut_host, host);
+               SCPYN(wtmp.ut_line, line + sizeof("/dev/") - 1);
+               SCPYN(wtmp.ut_id, wtmp.ut_line+3);
+               pututline(&wtmp);
+               endutent();
+               if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) {
+                       (void) write(i, (char *)&wtmp, sizeof(struct utmp));
+                       (void) close(i);
+               }
+               utmp_sig_notify(pid);
+# endif        /* CRAY */
+               (void) close(t);
+       } else {
+               start_login(t, host);
+               /*NOTREACHED*/
+       }
+#else  /* NEWINIT */
+
+       extern char *ptyip;
+       struct init_request request;
+       int nologinproc();
+       register int n;
+
+       /*
+        * Init will start up login process if we ask nicely.  We only wait
+        * for it to start up and begin normal telnet operation.
+        */
+       if ((i = open(INIT_FIFO, O_WRONLY)) < 0) {
+               char tbuf[128];
+               (void) sprintf(tbuf, "Can't open %s\n", INIT_FIFO);
+               fatalperror(net, tbuf);
+       }
+       memset((char *)&request, 0, sizeof(request));
+       request.magic = INIT_MAGIC;
+       SCPYN(request.gen_id, gen_id);
+       SCPYN(request.tty_id, &line[8]);
+       SCPYN(request.host, host);
+       SCPYN(request.term_type, &terminaltype[5]);
+       if (write(i, (char *)&request, sizeof(request)) < 0) {
+               char tbuf[128];
+               (void) sprintf(tbuf, "Can't write to %s\n", INIT_FIFO);
+               fatalperror(net, tbuf);
+       }
+       (void) close(i);
+       (void) signal(SIGALRM, nologinproc);
+       for (i = 0; ; i++) {
+               alarm(15);
+               n = read(pty, ptyip, BUFSIZ);
+               if (i == 3 || n >= 0 || !gotalarm)
+                       break;
+               gotalarm = 0;
+               (void) write(net, "telnetd: waiting for /etc/init to start login process.\r\n", 56);
+       }
+       if (n < 0 && gotalarm)
+               fatal(net, "/etc/init didn't start login process");
+       pcc += n;
+       alarm(0);
+       (void) signal(SIGALRM, SIG_DFL);
+
+       /*
+        * Set tab expansion the way we like, in case init did something
+        * different.
+        */
+       init_termbuf();
+       termbuf.c_oflag &= ~TABDLY;
+       termbuf.c_oflag |= TAB0;
+       set_termbuf();
+       return;
+#endif /* NEWINIT */
+}
+
+#ifndef        NEWINIT
+char   *envinit[3];
+
+/*
+ * start_login(t, host)
+ *
+ * Assuming that we are now running as a child processes, this
+ * function will turn us into the login process.
+ */
+
+start_login(t, host)
+int t;
+char *host;
+{
+       extern char *getenv();
+       char **envp;
+
+#ifdef CRAY
+       utmp_sig_wait();
+# ifndef TCVHUP
+       setpgrp();
+# endif
+       t = open(line, 2);      /* open ttyp */
+       if (t < 0)
+               fatalperror(net, line);
+# ifdef        TCVHUP
+       /*
+        * Hangup anybody else using this ttyp, then reopen it for
+        * ourselves.
+        */
+       (void) chown(line, 0, 0);
+       (void) chmod(line, 0600);
+       (void) signal(SIGHUP, SIG_IGN);
+       (void) ioctl(t, TCVHUP, (char *)0);
+       (void) signal(SIGHUP, SIG_DFL);
+       setpgrp();
+       i = open(line, 2);
+       if (i < 0)
+               fatalperror(net, line);
+       (void) close(t);
+       t = i;
+# endif        /* TCVHUP */
+       /*
+        * set ttyp modes as we like them to be
+        */
+       init_termbuf();
+       termbuf.c_oflag = OPOST|ONLCR;
+       termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
+       termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
+       termbuf.c_cflag = EXTB|HUPCL|CS8;
+       set_termbuf();
+#endif /* CRAY */
+
+       /*
+        * set up standard paths before forking to login
+        */
+#ifdef BSD >43
+       if (setsid() < 0)
+               fatalperror(net, "setsid");
+       if (ioctl(t, TIOCSCTTY, (char *)0) < 0)
+               fatalperror(net, "ioctl(sctty)");
+#endif
+       (void) close(net);
+       (void) close(pty);
+       (void) dup2(t, 0);
+       (void) dup2(t, 1);
+       (void) dup2(t, 2);
+       (void) close(t);
+       envp = envinit;
+       *envp++ = terminaltype;
+       if (*envp = getenv("TZ"))
+               *envp++ -= 3;
+#ifdef CRAY
+       else
+               *envp++ = "TZ=GMT0";
+#endif
+       *envp = 0;
+       environ = envinit;
+       /*
+        * -h : pass on name of host.
+        *              WARNING:  -h is accepted by login if and only if
+        *                      getuid() == 0.
+        * -p : don't clobber the environment (so terminal type stays set).
+        */
+       execl(_PATH_LOGIN, "login", "-h", host,
+#ifndef CRAY
+                                       terminaltype ? "-p" : 0,
+#endif
+                                                               0);
+       syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN);
+       fatalperror(net, _PATH_LOGIN);
+       /*NOTREACHED*/
+}
+#endif NEWINIT
+
+/*
+ * cleanup()
+ *
+ * This is the routine to call when we are all through, to
+ * clean up anything that needs to be cleaned up.
+ */
+cleanup()
+{
+
+#ifndef        CRAY
+# if BSD > 43
+       char *p;
+
+       p = line + sizeof("/dev/") - 1;
+       if (logout(p))
+               logwtmp(p, "", "");
+       (void)chmod(line, 0666);
+       (void)chown(line, 0, 0);
+       *p = 'p';
+       (void)chmod(line, 0666);
+       (void)chown(line, 0, 0);
+# else
+       rmut();
+       vhangup();      /* XXX */
+# endif
+       (void) shutdown(net, 2);
+#else  /* CRAY */
+# ifndef NEWINIT
+       rmut(line);
+       (void) shutdown(net, 2);
+       kill(0, SIGHUP);
+# else /* NEWINIT */
+       (void) shutdown(net, 2);
+       sleep(5);
+# endif        /* NEWINT */
+#endif /* CRAY */
+       exit(1);
+}
+
+#if    defined(CRAY) && !defined(NEWINIT)
+/*
+ * _utmp_sig_rcv
+ * utmp_sig_init
+ * utmp_sig_wait
+ *     These three functions are used to coordinate the handling of
+ *     the utmp file between the server and the soon-to-be-login shell.
+ *     The server actually creates the utmp structure, the child calls
+ *     utmp_sig_wait(), until the server calls utmp_sig_notify() and
+ *     signals the future-login shell to proceed.
+ */
+static int caught=0;           /* NZ when signal intercepted */
+static void (*func)();         /* address of previous handler */
+
+void
+_utmp_sig_rcv(sig)
+int sig;
+{
+       caught = 1;
+       (void) signal(SIGUSR1, func);
+}
+
+utmp_sig_init()
+{
+       /*
+        * register signal handler for UTMP creation
+        */
+       if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1)
+               fatalperror(net, "telnetd/signal");
+}
+
+utmp_sig_wait()
+{
+       /*
+        * Wait for parent to write our utmp entry.
+        */
+       sigoff();
+       while (caught == 0) {
+               pause();        /* wait until we get a signal (sigon) */
+               sigoff();       /* turn off signals while we check caught */
+       }
+       sigon();                /* turn on signals again */
+}
+
+utmp_sig_notify(pid)
+{
+       kill(pid, SIGUSR1);
+}
+#endif /* defined(CRAY) && !defined(NEWINIT) */
+
+/*
+ * rmut()
+ *
+ * This is the function called by cleanup() to
+ * remove the utmp entry for this person.
+ */
+
+#if    !defined(CRAY) && BSD <= 43
+rmut()
+{
+       register f;
+       int found = 0;
+       struct utmp *u, *utmp;
+       int nutmp;
+       struct stat statbf;
+       char *malloc();
+       long time();
+       off_t lseek();
+
+       f = open(utmpf, O_RDWR);
+       if (f >= 0) {
+               (void) fstat(f, &statbf);
+               utmp = (struct utmp *)malloc((unsigned)statbf.st_size);
+               if (!utmp)
+                       syslog(LOG_ERR, "utmp malloc failed");
+               if (statbf.st_size && utmp) {
+                       nutmp = read(f, (char *)utmp, (int)statbf.st_size);
+                       nutmp /= sizeof(struct utmp);
+               
+                       for (u = utmp ; u < &utmp[nutmp] ; u++) {
+                               if (SCMPN(u->ut_line, line+5) ||
+                                   u->ut_name[0]==0)
+                                       continue;
+                               (void) lseek(f, ((long)u)-((long)utmp), L_SET);
+                               SCPYN(u->ut_name, "");
+                               SCPYN(u->ut_host, "");
+                               (void) time(&u->ut_time);
+                               (void) write(f, (char *)u, sizeof(wtmp));
+                               found++;
+                       }
+               }
+               (void) close(f);
+       }
+       if (found) {
+               f = open(wtmpf, O_WRONLY|O_APPEND);
+               if (f >= 0) {
+                       SCPYN(wtmp.ut_line, line+5);
+                       SCPYN(wtmp.ut_name, "");
+                       SCPYN(wtmp.ut_host, "");
+                       (void) time(&wtmp.ut_time);
+                       (void) write(f, (char *)&wtmp, sizeof(wtmp));
+                       (void) close(f);
+               }
+       }
+       (void) chmod(line, 0666);
+       (void) chown(line, 0, 0);
+       line[strlen("/dev/")] = 'p';
+       (void) chmod(line, 0666);
+       (void) chown(line, 0, 0);
+}  /* end of rmut */
+#endif /* CRAY */
index b08a95d..2f07a87 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1983, 1986 Regents of the University of California.
+ * Copyright (c) 1989 Regents of the University of California.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted
 
 #ifndef lint
 char copyright[] =
 
 #ifndef lint
 char copyright[] =
-"@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\
+"@(#) Copyright (c) 1989 Regents of the University of California.\n\
  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)telnetd.c  5.37 (Berkeley) %G%";
+static char sccsid[] = "@(#)telnetd.c  5.38 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-/*
- * Telnet server.
- */
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <netinet/in.h>
-
-#include <arpa/telnet.h>
-
-#include <stdio.h>
-#include <signal.h>
-#include <errno.h>
-#include <sgtty.h>
-#include <netdb.h>
-#include <syslog.h>
-#include <ctype.h>
-#include "pathnames.h"
-
-#define        OPT_NO                  0               /* won't do this option */
-#define        OPT_YES                 1               /* will do this option */
-#define        OPT_YES_BUT_ALWAYS_LOOK 2
-#define        OPT_NO_BUT_ALWAYS_LOOK  3
-char   hisopts[256];
-char   myopts[256];
-
-char   doopt[] = { IAC, DO, '%', 'c', 0 };
-char   dont[] = { IAC, DONT, '%', 'c', 0 };
-char   will[] = { IAC, WILL, '%', 'c', 0 };
-char   wont[] = { IAC, WONT, '%', 'c', 0 };
+#include "telnetd.h"
 
 /*
 
 /*
- * I/O data buffers, pointers, and counters.
+ * I/O data buffers,
+ * pointers, and counters.
  */
 char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
  */
 char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
+char   ptyibuf2[BUFSIZ];
 
 
-char   ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
-
-char   netibuf[BUFSIZ], *netip = netibuf;
-#define        NIACCUM(c)      {   *netip++ = c; \
-                           ncc++; \
-                       }
+#ifdef CRAY
+int    hostinfo = 1;                   /* do we print login banner? */
+#endif
 
 
-char   netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
-char   *neturg = 0;            /* one past last bye of urgent data */
-       /* the remote system seems to NOT be an old 4.2 */
-int    not42 = 1;
-
-#define        BANNER  "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
-
-               /* buffer for sub-options */
-char   subbuffer[100], *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)
+#ifdef CRAY
+extern int      newmap; /* nonzero if \n maps to ^M^J */
+int    lowpty = 0, highpty = 128;      /* low, high pty numbers */
+#endif /* CRAY */
 
 
-int    pcc, ncc;
+int debug = 0;
+char *progname;
 
 
-int    pty, net;
-int    inter;
-extern char **environ;
-extern int errno;
-char   *line;
-int    SYNCHing = 0;           /* we are in TELNET SYNCH mode */
-/*
- * The following are some clocks used to decide how to interpret
- * the relationship between various variables.
- */
-
-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 */
-       ttypeopt,               /* ttype will/won't received */
-       ttypesubopt,            /* ttype subopt is received */
-       getterminal,            /* time started to get terminal information */
-       gotDM;                  /* when did we last see a data mark */
-} clocks;
-
-#define        settimer(x)     (clocks.x = ++clocks.system)
-#define        sequenceIs(x,y) (clocks.x < clocks.y)
-\f
 main(argc, argv)
        char *argv[];
 {
        struct sockaddr_in from;
        int on = 1, fromlen;
 
 main(argc, argv)
        char *argv[];
 {
        struct sockaddr_in from;
        int on = 1, fromlen;
 
-       if ((argc > 1) && (strcmp(argv[1], "-debug") == 0)) {
+       pfrontp = pbackp = ptyobuf;
+       netip = netibuf;
+       nfrontp = nbackp = netobuf;
+
+       progname = *argv;
+top:
+       argc--, argv++;
+
+       if (argc > 0 && strcmp(*argv, "-debug") == 0) {
+               debug++;
+               goto top;
+       }
+
+#ifdef LINEMODE
+       if (argc > 0 && !strcmp(*argv, "-l")) {
+               alwayslinemode = 1;
+               goto top;
+       }
+#endif /* LINEMODE */
+
+#ifdef CRAY
+       if (argc > 0 && !strcmp(*argv, "-h")) {
+               hostinfo = 0;
+               goto top;
+       }
+
+       if (argc > 0 && !strncmp(*argv, "-r", 2)) {
+               char *strchr();
+               char *c;
+
+               *argv += 2;
+               if (**argv == '\0' && argc)
+                       argv++, argc--;
+               c = strchr(*argv, '-');
+               if (c) {
+                       *c++ = '\0';
+                       highpty = atoi(c);
+               } else
+                       highpty = -1;
+               lowpty = atoi(*argv);
+               if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) {
+       usage:
+                       fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
+# ifdef        NEWINIT
+                       fprintf(stderr, "[-Iinitid] ");
+# endif        /* NEWINIT */
+                       fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n");
+                       exit(1);
+               }
+               goto top;
+       }
+# ifdef        NEWINIT
+       if (argc > 0 && !strncmp(*argv, "-I", 2)) {
+               extern char *gen_id;
+
+               *argv += 2;
+               if (**argv == '\0') {
+                       if (argc < 2)
+                               goto usage;
+                       argv++, argc--;
+                       if (**argv == '\0')
+                               goto usage;
+               }
+               gen_id = *argv;
+               goto top;
+       }
+# endif        /* NEWINIT */
+#endif /* CRAY */
+
+       if (debug) {
            int s, ns, foo;
            struct servent *sp;
            static struct sockaddr_in sin = { AF_INET };
 
            int s, ns, foo;
            struct servent *sp;
            static struct sockaddr_in sin = { AF_INET };
 
-           argc--, argv++;
-           if (argc > 1) {
-                   sin.sin_port = atoi(argv[1]);
-                   sin.sin_port = htons((u_short)sin.sin_port);
+           if (argc > 0) {
+                   if (sp = getservbyname(*argv, "tcp")) {
+                       sin.sin_port = sp->s_port;
+                   } else {
+                       sin.sin_port = atoi(*argv);
+                       if ((int)sin.sin_port <= 0) {
+                           fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
+                           exit(1);
+                       }
+                       sin.sin_port = htons((u_short)sin.sin_port);
+                  }
            } else {
                sp = getservbyname("telnet", "tcp");
                if (sp == 0) {
                        fprintf(stderr,
                                "telnetd: tcp/telnet: unknown service\n");
            } else {
                sp = getservbyname("telnet", "tcp");
                if (sp == 0) {
                        fprintf(stderr,
                                "telnetd: tcp/telnet: unknown service\n");
-                       exit(1);
+                   exit(1);
                }
                sin.sin_port = sp->s_port;
            }
                }
                sin.sin_port = sp->s_port;
            }
@@ -147,7 +152,8 @@ main(argc, argv)
                    perror("telnetd: socket");;
                    exit(1);
            }
                    perror("telnetd: socket");;
                    exit(1);
            }
-           if (bind(s, &sin, sizeof sin) < 0) {
+           (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+           if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
                perror("bind");
                exit(1);
            }
                perror("bind");
                exit(1);
            }
@@ -156,270 +162,193 @@ main(argc, argv)
                exit(1);
            }
            foo = sizeof sin;
                exit(1);
            }
            foo = sizeof sin;
-           ns = accept(s, &sin, &foo);
+           ns = accept(s, (struct sockaddr *)&sin, &foo);
            if (ns < 0) {
                perror("accept");
                exit(1);
            }
            if (ns < 0) {
                perror("accept");
                exit(1);
            }
-           dup2(ns, 0);
-           close(s);
+           (void) dup2(ns, 0);
+           (void) close(ns);
+           (void) close(s);
        }
        }
+
        openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
        fromlen = sizeof (from);
        openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
        fromlen = sizeof (from);
-       if (getpeername(0, &from, &fromlen) < 0) {
-               fprintf(stderr, "%s: ", argv[0]);
+       if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+               fprintf(stderr, "%s: ", progname);
                perror("getpeername");
                _exit(1);
        }
        if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
                syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
        }
                perror("getpeername");
                _exit(1);
        }
        if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
                syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
        }
-       doit(0, &from);
-}
+       net = 0;
+       doit(&from);
+       /* NOTREACHED */
+}  /* end of main */
 
 
-char   *terminaltype = 0;
-char   *envinit[2];
 int    cleanup();
 
 int    cleanup();
 
-/*
- * 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()
-{
-    if (nfrontp-nbackp) {
-       netflush();
-    }
-    ncc = read(net, netibuf, sizeof netibuf);
-    if (ncc < 0) {
-       syslog(LOG_INFO, "ttloop:  read: %m\n");
-       exit(1);
-    } else if (ncc == 0) {
-       syslog(LOG_INFO, "ttloop:  peer died: %m\n");
-       exit(1);
-    }
-    netip = netibuf;
-    telrcv();                  /* state machine */
-    if (ncc > 0) {
-       pfrontp = pbackp = ptyobuf;
-       telrcv();
-    }
-}
-
-/*
- * getterminalspeed
- *
- *     Ask the other end to send along its terminal speed.
- * subopt does the rest.
- */
-
-void
-getterminalspeed()
-{
-  static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
-
-  bcopy(sbuf, nfrontp, sizeof sbuf);
-  nfrontp += sizeof sbuf;
-}
-
 /*
  * getterminaltype
  *
 /*
  * getterminaltype
  *
- *     Ask the other end to send along its terminal type.
+ *     Ask the other end to send along its terminal type and speed.
  * Output is the variable terminaltype filled in.
  */
  * Output is the variable terminaltype filled in.
  */
-
+static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
 void
 getterminaltype()
 {
 void
 getterminaltype()
 {
-    static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
+    void ttloop();
 
 
-    settimer(getterminal);
-    bcopy(sbuf, nfrontp, sizeof sbuf);
-    nfrontp += sizeof sbuf;
-    hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
-    while (sequenceIs(ttypeopt, getterminal)) {
+    settimer(baseline);
+    willoption(TELOPT_TTYPE, 1);
+    willoption(TELOPT_TSPEED, 1);
+    while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
+          (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
        ttloop();
     }
        ttloop();
     }
-    if (hisopts[TELOPT_TTYPE] == OPT_YES) {
-       static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
+    if (hisopts[TELOPT_TSPEED] == OPT_YES) {
+       static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
 
        bcopy(sbbuf, nfrontp, sizeof sbbuf);
        nfrontp += sizeof sbbuf;
 
        bcopy(sbbuf, nfrontp, sizeof sbbuf);
        nfrontp += sizeof sbbuf;
-       while (sequenceIs(ttypesubopt, getterminal)) {
+    }
+    if (hisopts[TELOPT_TTYPE] == OPT_YES) {
+
+       bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+       nfrontp += sizeof ttytype_sbbuf;
+    }
+    if (hisopts[TELOPT_TSPEED] == OPT_YES) {
+       while (sequenceIs(tspeedsubopt, baseline))
+           ttloop();
+    }
+    if (hisopts[TELOPT_TTYPE] == OPT_YES) {
+       char first[256], last[256];
+
+       while (sequenceIs(ttypesubopt, baseline))
            ttloop();
            ttloop();
+
+       if (!terminaltypeok(&terminaltype[5])) {
+           (void) strncpy(first, terminaltype, sizeof(first));
+           for(;;) {
+               /*
+                * Save the unknown name, and request the next name.
+                */
+               (void) strncpy(last, terminaltype, sizeof(last));
+               _gettermname();
+               if (terminaltypeok(&terminaltype[5]))
+                   break;
+               if (strncmp(last, terminaltype, sizeof(last)) == 0) {
+                   /*
+                    * 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 type, so that
+                    * RFC1091 compliant telnets will cycle back to
+                    * the start of the list.
+                    */
+                   _gettermname();
+                   if (strncmp(first, terminaltype, sizeof(first) != 0))
+                       (void) strncpy(terminaltype, first, sizeof(first));
+                   break;
+               }
+           }
        }
     }
        }
     }
+}  /* end of getterminaltype */
+
+_gettermname()
+{
+    settimer(baseline);
+    bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+    nfrontp += sizeof ttytype_sbbuf;
+    while (sequenceIs(ttypesubopt, baseline))
+       ttloop();
+}
+
+terminaltypeok(s)
+char *s;
+{
+    char buf[1024];
+
+    if (terminaltype == NULL)
+       return(1);
+
+    /*
+     * 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);
 }
 
 /*
  * Get a pty, scan input lines.
  */
 }
 
 /*
  * Get a pty, scan input lines.
  */
-doit(f, who)
-       int f;
+doit(who)
        struct sockaddr_in *who;
 {
        char *host, *inet_ntoa();
        struct sockaddr_in *who;
 {
        char *host, *inet_ntoa();
-       int i, p, t;
-       struct sgttyb b;
+       int t;
        struct hostent *hp;
        struct hostent *hp;
-       int c;
 
 
-       for (c = 'p'; c <= 's'; c++) {
-               struct stat stb;
+       /*
+        * Find an available pty to use.
+        */
+       pty = getpty();
+       if (pty < 0)
+               fatal(net, "All network ports in use");
 
 
-               line = "/dev/ptyXX";
-               line[strlen("/dev/pty")] = c;
-               line[strlen("/dev/ptyp")] = '0';
-               if (stat(line, &stb) < 0)
-                       break;
-               for (i = 0; i < 16; i++) {
-                       line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
-                       p = open(line, O_RDWR);
-                       if (p > 0)
-                               goto gotpty;
-               }
-       }
-       fatal(f, "All network ports in use");
-       /*NOTREACHED*/
-gotpty:
-       dup2(f, 0);
-       line[strlen("/dev/")] = 't';
-       t = open(_PATH_TTY, O_RDWR);
-       if (t >= 0) {
-               ioctl(t, TIOCNOTTY, 0);
-               close(t);
-       }
-       t = open(line, O_RDWR);
-       if (t < 0)
-               fatalperror(f, line);
-       if (fchmod(t, 0))
-               fatalperror(f, line);
-       (void)signal(SIGHUP, SIG_IGN);
-       vhangup();
-       (void)signal(SIGHUP, SIG_DFL);
-       t = open(line, O_RDWR);
-       if (t < 0)
-               fatalperror(f, line);
-       ioctl(t, TIOCGETP, &b);
-       b.sg_flags = CRMOD|XTABS|ANYP;
-       ioctl(t, TIOCSETP, &b);
-       ioctl(p, TIOCGETP, &b);
-       b.sg_flags &= ~ECHO;
-       b.sg_ospeed = b.sg_ispeed = B9600;
-       ioctl(p, TIOCSETP, &b);
-       hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
+       t = getptyslave();
+
+       /* get name of connected client */
+       hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
                who->sin_family);
        if (hp)
                host = hp->h_name;
        else
                host = inet_ntoa(who->sin_addr);
 
                who->sin_family);
        if (hp)
                host = hp->h_name;
        else
                host = inet_ntoa(who->sin_addr);
 
-       net = f;
-       pty = p;
-
        /*
        /*
-        * get terminal type and size.
+        * get terminal type.
         */
        getterminaltype();
         */
        getterminaltype();
+       if (terminaltype == NULL)
+               terminaltype = "TERM=network";
 
 
-       if ((i = fork()) < 0)
-               fatalperror(f, "fork");
-       if (i) {
-               close(t);
-               telnet(f, p);
-       }
-       if (setsid() < 0)
-               fatalperror(f, "setsid");
-       if (ioctl(t, TIOCSCTTY, 0) < 0)
-               fatalperror(f, "ioctl(sctty)");
-       close(f);
-       close(p);
-       dup2(t, 0);
-       dup2(t, 1);
-       dup2(t, 2);
-       close(t);
-       envinit[0] = terminaltype;
-       envinit[1] = 0;
-       environ = envinit;
        /*
        /*
-        * -h : pass on name of host.
-        *              WARNING:  -h is accepted by login if and only if
-        *                      getuid() == 0.
-        * -p : don't clobber the environment (so terminal type stays set).
+        * Start up the login process on the slave side of the terminal
         */
         */
-       execl(_PATH_LOGIN, "login", "-h", host,
-                                       terminaltype ? "-p" : 0, 0);
-       syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN);
-       fatalperror(2, _PATH_LOGIN);
-       /*NOTREACHED*/
-}
-
-fatal(f, msg)
-       int f;
-       char *msg;
-{
-       char buf[BUFSIZ];
-
-       (void) sprintf(buf, "telnetd: %s.\r\n", msg);
-       (void) write(f, buf, strlen(buf));
-       exit(1);
-}
-
-fatalperror(f, msg)
-       int f;
-       char *msg;
-{
-       char buf[BUFSIZ];
-       extern char *sys_errlist[];
-
-       (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
-       fatal(f, buf);
-}
-
-
-/*
- * Check a descriptor to see if out of band data exists on it.
- */
+       startslave(t, host);
 
 
+       telnet(net, pty);  /* begin server processing */
+       /*NOTREACHED*/
+}  /* end of doit */
 
 
-stilloob(s)
-int    s;              /* socket number */
-{
-    static struct timeval timeout = { 0 };
-    fd_set     excepts;
-    int value;
-
-    do {
-       FD_ZERO(&excepts);
-       FD_SET(s, &excepts);
-       value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
-    } while ((value == -1) && (errno == EINTR));
-
-    if (value < 0) {
-       fatalperror(pty, "select");
-    }
-    if (FD_ISSET(s, &excepts)) {
-       return 1;
-    } else {
-       return 0;
-    }
-}
-\f
+#ifndef        MAXHOSTNAMELEN
+#define        MAXHOSTNAMELEN 64
+#endif MAXHOSTNAMELEN
 /*
  * Main loop.  Select from pty and network, and
  * hand data to telnet receiver finite state machine.
  */
 telnet(f, p)
 /*
  * Main loop.  Select from pty and network, and
  * hand data to telnet receiver finite state machine.
  */
 telnet(f, p)
+int f, p;
 {
        int on = 1;
        char hostname[MAXHOSTNAMELEN];
 {
        int on = 1;
        char hostname[MAXHOSTNAMELEN];
+#ifdef CRAY2
+       int termstat();
+       int interrupt(), sendbrk();
+#endif
 #define        TABBUFSIZ       512
        char    defent[TABBUFSIZ];
        char    defstrs[TABBUFSIZ];
 #define        TABBUFSIZ       512
        char    defent[TABBUFSIZ];
        char    defstrs[TABBUFSIZ];
@@ -427,44 +356,22 @@ telnet(f, p)
        char *HE;
        char *HN;
        char *IM;
        char *HE;
        char *HN;
        char *IM;
-
-       ioctl(f, FIONBIO, &on);
-       ioctl(p, FIONBIO, &on);
-       ioctl(p, TIOCPKT, &on);
-#if    defined(SO_OOBINLINE)
-       setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
-#endif /* defined(SO_OOBINLINE) */
-       signal(SIGTSTP, SIG_IGN);
+       void netflush();
+       
        /*
        /*
-        * Ignoring SIGTTOU keeps the kernel from blocking us
-        * in ttioctl() in /sys/tty.c.
+        * Initialize the slc mapping table.
         */
         */
-       signal(SIGTTOU, SIG_IGN);
-       signal(SIGCHLD, cleanup);
-       setpgrp(0, 0);
+       get_slc_defaults();
 
        /*
 
        /*
-        * Request to do remote echo and to suppress go ahead.
+        * 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 (!myopts[TELOPT_ECHO]) {
-           dooption(TELOPT_ECHO);
-       }
-       if (!myopts[TELOPT_SGA]) {
-           dooption(TELOPT_SGA);
-       }
-       if (!hisopts[TELOPT_NAWS]) {
-          willoption(TELOPT_NAWS);
-          hisopts[TELOPT_NAWS] = OPT_NO;
-       }
-       if (!hisopts[TELOPT_TSPEED]) {
-          willoption(TELOPT_TSPEED);
-          hisopts[TELOPT_TSPEED] = OPT_NO;
-       }
-       if (!hisopts[TELOPT_LFLOW]) {
-          willoption(TELOPT_LFLOW);
-          hisopts[TELOPT_LFLOW] = OPT_NO;
-       }
-
+       if (!myopts[TELOPT_ECHO])
+               dooption(TELOPT_ECHO);
+       if (!myopts[TELOPT_SGA])
+               dooption(TELOPT_SGA);
        /*
         * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
         * because 4.2 clients are unable to deal with TCP urgent data.
        /*
         * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
         * because 4.2 clients are unable to deal with TCP urgent data.
@@ -475,9 +382,103 @@ telnet(f, p)
         * WE, the server, sends it; it does NOT mean that the client will
         * echo the terminal input).
         */
         * WE, the server, sends it; it does NOT mean that the client will
         * echo the terminal input).
         */
-       (void) sprintf(nfrontp, doopt, TELOPT_ECHO);
-       nfrontp += sizeof doopt-2;
-       hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
+       willoption(TELOPT_ECHO, 1);
+
+#ifdef LINEMODE
+       if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
+               /* Query the peer for linemode support by trying to negotiate
+                * the linemode option.
+                */
+               linemode = 1;
+               editmode = 0;
+               willoption(TELOPT_LINEMODE, 1);  /* send do linemode */
+       }
+#endif /* LINEMODE */
+
+       /*
+        * Send along a couple of other options that we wish to negotiate.
+        */
+       willoption(TELOPT_NAWS, 1);
+       dooption(TELOPT_STATUS, 1);
+       flowmode = 1;  /* default flow control state */
+       willoption(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 (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
+               ttloop();
+
+       /*
+        * Turn on packet mode, and default to line at at time mode.
+        */
+       (void) ioctl(p, TIOCPKT, (char *)&on);
+#ifdef LINEMODE
+       tty_setlinemode(1);
+
+# ifdef        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)
+               willoption(TELOPT_TM, 1);
+# endif        /* KLUDGELINEMODE */
+#endif /* LINEMODE */
+
+       /*
+        * 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);
+#ifdef CRAY2
+       init_termdriver(f, p, interrupt, sendbrk);
+#endif
+
+#if    defined(SO_OOBINLINE)
+       (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
+#endif /* defined(SO_OOBINLINE) */
+
+#ifdef SIGTSTP
+       (void) signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGTTOU
+       /*
+        * Ignoring SIGTTOU keeps the kernel from blocking us
+        * in ttioct() in /sys/tty.c.
+        */
+       (void) signal(SIGTTOU, SIG_IGN);
+#endif
+
+       (void) signal(SIGCHLD, cleanup);
+
+#if    defined(CRAY2)
+       /*
+        * Cray-2 will send a signal when pty modes are changed by slave
+        * side.  Set up signal handler now.
+        */
+       if ((int)signal(SIGUSR1, termstat) < 0)
+               perror("signal");
+       else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
+               perror("ioctl:TCSIGME");
+       /*
+        * Make processing loop check terminal characteristics early on.
+        */
+       termstat();
+#endif
+
+       (void) setpgrp(0, 0);
 
        /*
         * Show banner that getty never gave.
 
        /*
         * Show banner that getty never gave.
@@ -487,34 +488,36 @@ telnet(f, p)
         * other pty --> client data.
         */
 
         * other pty --> client data.
         */
 
-       gethostname(hostname, sizeof (hostname));
+       (void) gethostname(hostname, sizeof (hostname));
+
        if (getent(defent, "default") == 1) {
                char *getstr();
        if (getent(defent, "default") == 1) {
                char *getstr();
-               char *p=defstrs;
+               char *cp=defstrs;
 
 
-               HE = getstr("he", &p);
-               HN = getstr("hn", &p);
-               IM = getstr("im", &p);
+               HE = getstr("he", &cp);
+               HN = getstr("hn", &cp);
+               IM = getstr("im", &cp);
                if (HN && *HN)
                if (HN && *HN)
-                       strcpy(hostname, HN);
-               edithost(HE, hostname);
-               if (IM && *IM)
-                       putf(IM, ptyibuf+1);
+                       (void) strcpy(hostname, HN);
+               if (IM == 0)
+                       IM = "";
        } else {
        } else {
-               sprintf(ptyibuf+1, BANNER, hostname);
-       }
-
-       ptyip = ptyibuf+1;              /* Prime the pump */
-       pcc = strlen(ptyip);            /* ditto */
-
-       /* Clear ptybuf[0] - where the packet information is received */
-       ptyibuf[0] = 0;
-
-       /*
-        * Call telrcv() once to pick up anything received during
-        * terminal type negotiation.
-        */
-       telrcv();
+#ifdef CRAY
+               if (hostinfo == 0)
+                       IM = 0;
+               else
+#endif
+                       IM = DEFAULT_IM;
+               HE = 0;
+       }
+       edithost(HE, hostname);
+       if (IM && *IM)
+               putf(IM, ptyibuf2);
+
+       if (pcc)
+               (void) strncat(ptyibuf2, ptyip, pcc+1);
+       ptyip = ptyibuf2;
+       pcc = strlen(ptyip);
 
        for (;;) {
                fd_set ibits, obits, xbits;
 
        for (;;) {
                fd_set ibits, obits, xbits;
@@ -523,6 +526,10 @@ telnet(f, p)
                if (ncc < 0 && pcc < 0)
                        break;
 
                if (ncc < 0 && pcc < 0)
                        break;
 
+#ifdef CRAY2
+               if (needtermstat)
+                       _termstat();
+#endif /* CRAY2 */
                FD_ZERO(&ibits);
                FD_ZERO(&obits);
                FD_ZERO(&xbits);
                FD_ZERO(&ibits);
                FD_ZERO(&obits);
                FD_ZERO(&xbits);
@@ -543,7 +550,6 @@ telnet(f, p)
                if (!SYNCHing) {
                        FD_SET(f, &xbits);
                }
                if (!SYNCHing) {
                        FD_SET(f, &xbits);
                }
-               FD_SET(p, &xbits);
                if ((c = select(16, &ibits, &obits, &xbits,
                                                (struct timeval *)0)) < 1) {
                        if (c == -1) {
                if ((c = select(16, &ibits, &obits, &xbits,
                                                (struct timeval *)0)) < 1) {
                        if (c == -1) {
@@ -604,7 +610,7 @@ telnet(f, p)
                    if (SYNCHing) {
                        int atmark;
 
                    if (SYNCHing) {
                        int atmark;
 
-                       ioctl(net, SIOCATMARK, (char *)&atmark);
+                       (void) ioctl(net, SIOCATMARK, (char *)&atmark);
                        if (atmark) {
                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
                            if ((ncc == -1) && (errno == EINVAL)) {
                        if (atmark) {
                            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
                            if ((ncc == -1) && (errno == EINVAL)) {
@@ -636,23 +642,34 @@ telnet(f, p)
                /*
                 * Something to read from the pty...
                 */
                /*
                 * Something to read from the pty...
                 */
-               if (FD_ISSET(p, &ibits) || FD_ISSET(p, &xbits)) {
+               if (FD_ISSET(p, &ibits)) {
                        pcc = read(p, ptyibuf, BUFSIZ);
                        if (pcc < 0 && errno == EWOULDBLOCK)
                                pcc = 0;
                        else {
                                if (pcc <= 0)
                                        break;
                        pcc = read(p, ptyibuf, BUFSIZ);
                        if (pcc < 0 && errno == EWOULDBLOCK)
                                pcc = 0;
                        else {
                                if (pcc <= 0)
                                        break;
+#ifndef        CRAY2
+#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) {
                                if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
-                                       netclear();     /* clear buffer back */
+                                       netclear();     /* clear buffer back */
                                        *nfrontp++ = IAC;
                                        *nfrontp++ = DM;
                                        neturg = nfrontp-1; /* off by one XXX */
                                }
                                if (hisopts[TELOPT_LFLOW] &&
                                    (ptyibuf[0] &
                                        *nfrontp++ = IAC;
                                        *nfrontp++ = DM;
                                        neturg = nfrontp-1; /* off by one XXX */
                                }
                                if (hisopts[TELOPT_LFLOW] &&
                                    (ptyibuf[0] &
-                                     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
-                                       sprintf(nfrontp,"%c%c%c%c%c%c",
+                                    (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
+                                       (void) sprintf(nfrontp, "%c%c%c%c%c%c",
                                            IAC, SB, TELOPT_LFLOW,
                                            ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
                                            IAC, SE);
                                            IAC, SB, TELOPT_LFLOW,
                                            ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
                                            IAC, SE);
@@ -660,7 +677,15 @@ telnet(f, p)
                                }
                                pcc--;
                                ptyip = ptyibuf+1;
                                }
                                pcc--;
                                ptyip = ptyibuf+1;
-                       }
+#else  /* CRAY2 */
+                               if (!uselinemode) {
+                                       pcc = term_output(ptyibuf, ptyibuf2,
+                                                               pcc, BUFSIZ);
+                                       ptyip = ptyibuf2;
+                               } else
+                                       ptyip = ptyibuf;
+#endif /* CRAY2 */
+                       }
                }
 
                while (pcc > 0) {
                }
 
                while (pcc > 0) {
@@ -669,8 +694,12 @@ telnet(f, p)
                        c = *ptyip++ & 0377, pcc--;
                        if (c == IAC)
                                *nfrontp++ = c;
                        c = *ptyip++ & 0377, pcc--;
                        if (c == IAC)
                                *nfrontp++ = c;
+#ifdef CRAY2
+                       else if (c == '\n' &&
+                                    myopts[TELOPT_BINARY] == OPT_NO && newmap)
+                               *nfrontp++ = '\r';
+#endif /* CRAY2 */
                        *nfrontp++ = c;
                        *nfrontp++ = c;
-                       /* Don't do CR-NUL if we are in binary mode */
                        if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
                                        *nfrontp++ = *ptyip++ & 0377;
                        if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
                                if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
                                        *nfrontp++ = *ptyip++ & 0377;
@@ -687,519 +716,13 @@ telnet(f, p)
                        ptyflush();
        }
        cleanup();
                        ptyflush();
        }
        cleanup();
-}
+}  /* end of telnet */
        
        
-/*
- * 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 " */
-
-telrcv()
-{
-       register int c;
-       static int state = TS_DATA;
-
-       while (ncc > 0) {
-               if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
-                       return;
-               c = *netip++ & 0377, ncc--;
-               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;
-                       }
-                       if (inter > 0)
-                               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') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
-                               state = TS_CR;
-                       }
-                       *pfrontp++ = c;
-                       break;
-
-               case TS_IAC:
-                       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:
-                               interrupt();
-                               break;
-
-                       case BREAK:
-                               sendbrk();
-                               break;
-
-                       /*
-                        * Are You There?
-                        */
-                       case AYT:
-                               strcpy(nfrontp, "\r\n[Yes]\r\n");
-                               nfrontp += 9;
-                               break;
-
-                       /*
-                        * Abort Output
-                        */
-                       case AO: {
-                                       struct ltchars tmpltc;
-
-                                       ptyflush();     /* half-hearted */
-                                       ioctl(pty, TIOCGLTC, &tmpltc);
-                                       if (tmpltc.t_flushc != '\377') {
-                                               *pfrontp++ = tmpltc.t_flushc;
-                                       }
-                                       netclear();     /* clear buffer back */
-                                       *nfrontp++ = IAC;
-                                       *nfrontp++ = DM;
-                                       neturg = nfrontp-1; /* off by one XXX */
-                                       break;
-                               }
-
-                       /*
-                        * Erase Character and
-                        * Erase Line
-                        */
-                       case EC:
-                       case EL: {
-                                       struct sgttyb b;
-                                       char ch;
-
-                                       ptyflush();     /* half-hearted */
-                                       ioctl(pty, TIOCGETP, &b);
-                                       ch = (c == EC) ?
-                                               b.sg_erase : b.sg_kill;
-                                       if (ch != '\377') {
-                                               *pfrontp++ = ch;
-                                       }
-                                       break;
-                               }
-
-                       /*
-                        * Check for urgent data...
-                        */
-                       case DM:
-                               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 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) {
-                                       SB_ACCUM(IAC);
-                               }
-                               SB_ACCUM(c);
-                               state = TS_SB;
-                       } else {
-                               SB_TERM();
-                               suboption();    /* handle sub-option */
-                               state = TS_DATA;
-                       }
-                       break;
-
-               case TS_WILL:
-                       if (hisopts[c] != OPT_YES)
-                               willoption(c);
-                       state = TS_DATA;
-                       if (c == TELOPT_TSPEED)
-                               getterminalspeed();
-                       continue;
-
-               case TS_WONT:
-                       if (hisopts[c] != OPT_NO)
-                               wontoption(c);
-                       state = TS_DATA;
-                       continue;
-
-               case TS_DO:
-                       if (myopts[c] != OPT_YES)
-                               dooption(c);
-                       state = TS_DATA;
-                       continue;
-
-               case TS_DONT:
-                       if (myopts[c] != OPT_NO) {
-                               dontoption(c);
-                       }
-                       state = TS_DATA;
-                       continue;
-
-               default:
-                       syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
-                       printf("telnetd: panic state=%d\n", state);
-                       exit(1);
-               }
-       }
-}
-
-willoption(option)
-       int option;
-{
-       char *fmt;
-
-       switch (option) {
-
-       case TELOPT_BINARY:
-               mode(RAW, 0);
-               fmt = doopt;
-               break;
-
-       case TELOPT_ECHO:
-               not42 = 0;              /* looks like a 4.2 system */
-               /*
-                * Now, in a 4.2 system, to break them out of ECHOing
-                * (to the terminal) mode, we need to send a "WILL ECHO".
-                * Kludge upon kludge!
-                */
-               if (myopts[TELOPT_ECHO] == OPT_YES) {
-                   dooption(TELOPT_ECHO);
-               }
-               fmt = dont;
-               break;
-
-       case TELOPT_TTYPE:
-               settimer(ttypeopt);
-               if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
-                   hisopts[TELOPT_TTYPE] = OPT_YES;
-                   return;
-               }
-               fmt = doopt;
-               break;
-
-       case TELOPT_NAWS:
-       case TELOPT_TSPEED:
-       case TELOPT_LFLOW:
-       case TELOPT_SGA:
-               fmt = doopt;
-               break;
-
-       case TELOPT_TM:
-               fmt = dont;
-               break;
-
-       default:
-               fmt = dont;
-               break;
-       }
-       if (fmt == doopt) {
-               hisopts[option] = OPT_YES;
-       } else {
-               hisopts[option] = OPT_NO;
-       }
-       (void) sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof (dont) - 2;
-}
-
-wontoption(option)
-       int option;
-{
-       char *fmt;
-
-       switch (option) {
-       case TELOPT_ECHO:
-               not42 = 1;              /* doesn't seem to be a 4.2 system */
-               break;
-
-       case TELOPT_BINARY:
-               mode(0, RAW);
-               break;
-
-       case TELOPT_TTYPE:
-           settimer(ttypeopt);
-           break;
-       }
-
-       fmt = dont;
-       hisopts[option] = OPT_NO;
-       (void) sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof (doopt) - 2;
-}
-
-dooption(option)
-       int option;
-{
-       char *fmt;
-
-       switch (option) {
-
-       case TELOPT_TM:
-               fmt = wont;
-               break;
-
-       case TELOPT_ECHO:
-               mode(ECHO|CRMOD, 0);
-               fmt = will;
-               break;
-
-       case TELOPT_BINARY:
-               mode(RAW, 0);
-               fmt = will;
-               break;
-
-       case TELOPT_SGA:
-               fmt = will;
-               break;
-
-       default:
-               fmt = wont;
-               break;
-       }
-       if (fmt == will) {
-           myopts[option] = OPT_YES;
-       } else {
-           myopts[option] = OPT_NO;
-       }
-       (void) sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof (doopt) - 2;
-}
-
-
-dontoption(option)
-int option;
-{
-    char *fmt;
-
-    switch (option) {
-    case TELOPT_ECHO:          /* we should stop echoing */
-       mode(0, ECHO);
-       fmt = wont;
-       break;
-
-    default:
-       fmt = wont;
-       break;
-    }
-
-    if (fmt = wont) {
-       myopts[option] = OPT_NO;
-    } else {
-       myopts[option] = OPT_YES;
-    }
-    (void) sprintf(nfrontp, fmt, option);
-    nfrontp += sizeof (wont) - 2;
-}
-
-/*
- * Given a string, assign the "best" speed which we support.
- *
- * "Best" is defined as rounding up, unless what is presented is
- * higher than the highest.
- */
-
-string2speed(s)
-  char *s;
-{
-       /*
-        * The order here is important.  The index of each speed needs to
-        * correspond with the sgtty structure value for that speed.
-        *
-        * Additionally, the search algorithm assumes the table is in
-        * ascending sequence.
-        */
-       static int ttyspeeds[] = {
-               0, 50, 75, 110, 134, 150, 200, 300,
-               600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 };
-#define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0]
-       int i;
-       int theirspeed = atoi(s);
-
-       for (i = 0; i < NUMSPEEDS; i++) {
-               if (ttyspeeds[i] == theirspeed) {       /* Exact match */
-                       return(i);
-               } else if (ttyspeeds[i] > theirspeed) {
-                       if (i > 0) {
-                               return i-1;
-                       }
-               }
-       }
-       /* Their number is greater than any of our numbers */
-       return(NUMSPEEDS-1);
-}
-
-/*
- * suboption()
- *
- *     Look at the sub-option buffer, and try to be helpful to the other
- * side.
- *
- *     Currently we recognize:
- *
- *     Terminal type is
- *     Terminal size
- *     Terminal speed is
- */
-
-suboption()
-{
-    switch (SB_GET()) {
-    case TELOPT_TTYPE: {               /* Yaaaay! */
-       static char terminalname[5+41] = "TERM=";
-
-       settimer(ttypesubopt);
-
-       if (SB_GET() != TELQUAL_IS) {
-           return;             /* ??? XXX but, this is the most robust */
-       }
-
-       terminaltype = terminalname+strlen(terminalname);
-
-       while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
-                                                                   !SB_EOF()) {
-           register int c;
-
-           c = SB_GET();
-           if (isupper(c)) {
-               c = tolower(c);
-           }
-           *terminaltype++ = c;    /* accumulate name */
-       }
-       *terminaltype = 0;
-       terminaltype = terminalname;
-       break;
-    }
-    case TELOPT_NAWS: {
-       struct winsize win;
-
-       ioctl(pty, TIOCGWINSZ, &win);
-       settimer(ttypesubopt);
-
-       syslog(LOG_INFO, "%x %x %x %x",
-       subpointer[0],subpointer[1],subpointer[2],subpointer[3]);
-       win.ws_col = SB_GET() << 8;
-       win.ws_col |= SB_GET();
-       win.ws_row = SB_GET() << 8;
-       win.ws_row |= SB_GET();
-       syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row);
-       ioctl(pty, TIOCSWINSZ, &win);
-       break;
-    }
-    case TELOPT_TSPEED: {
-       char speeds[41],*cp=speeds;
-       struct sgttyb b;
-       int ispeed,ospeed;
-       char *ispeeds,*ospeeds;
-
-       if (SB_GET() != TELQUAL_IS) {
-           return;             /* ??? XXX but, this is the most robust */
-       }
-
-       ispeeds = NULL;
-       ospeeds = speeds;
-       ispeed = 0;
-       ospeed = 0;
-       while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) {
-           register int c;
-
-           c = SB_GET();
-           if (c == ',') {
-               c = 0;
-               ispeeds = cp+1;
-           }
-           *cp++ = c;    /* accumulate name */
-       }
-       *cp = 0;
-
-       if (ispeeds)
-           ispeed = string2speed(ispeeds);
-       if (ospeeds)
-           ospeed = string2speed(ospeeds);
-
-       if (ispeed && ospeed) {
-           ioctl(pty, TIOCGETP, &b);
-           b.sg_ospeed = ospeed;
-           b.sg_ispeed = ispeed;
-           ioctl(pty, TIOCSETP, &b);
-       }
-
-       break;
-    }
-    default:
-       ;
-    }
-}
-
-mode(on, off)
-       int on, off;
-{
-       struct sgttyb b;
-
-       ptyflush();
-       ioctl(pty, TIOCGETP, &b);
-       b.sg_flags |= on;
-       b.sg_flags &= ~off;
-       ioctl(pty, TIOCSETP, &b);
-}
+#ifndef        TCSIG
+# ifdef        TIOCSIG
+#  define TCSIG TIOCSIG
+# endif
+#endif
 
 /*
  * Send interrupt to process on other side of pty.
 
 /*
  * Send interrupt to process on other side of pty.
@@ -1208,17 +731,14 @@ mode(on, off)
  */
 interrupt()
 {
  */
 interrupt()
 {
-       struct sgttyb b;
-       struct tchars tchars;
-
        ptyflush();     /* half-hearted */
        ptyflush();     /* half-hearted */
-       ioctl(pty, TIOCGETP, &b);
-       if (b.sg_flags & RAW) {
-               *pfrontp++ = '\0';
-               return;
-       }
-       *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
-               '\177' : tchars.t_intrc;
+
+#ifdef TCSIG
+       (void) ioctl(pty, TCSIG, (char *)SIGINT);
+#else  /* TCSIG */
+       init_termbuf();
+       *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177';
+#endif /* TCSIG */
 }
 
 /*
 }
 
 /*
@@ -1228,291 +748,30 @@ interrupt()
  */
 sendbrk()
 {
  */
 sendbrk()
 {
-       struct sgttyb b;
-       struct tchars tchars;
-
        ptyflush();     /* half-hearted */
        ptyflush();     /* half-hearted */
-       ioctl(pty, TIOCGETP, &b);
-       if (b.sg_flags & RAW) {
-               *pfrontp++ = '\0';
-               return;
-       }
-       *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
-               '\034' : tchars.t_quitc;
-}
-
-ptyflush()
-{
-       int n;
-
-       if ((n = pfrontp - pbackp) > 0)
-               n = write(pty, pbackp, n);
-       if (n < 0)
-               return;
-       pbackp += n;
-       if (pbackp == pfrontp)
-               pbackp = pfrontp = ptyobuf;
-}
-\f
-/*
- * 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(current)
-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 */
-       {
-           register char *look = current+2;
-
-           for (;;) {
-               if ((*look++&0xff) == IAC) {
-                   if ((*look++&0xff) == SE) {
-                       return look;
-                   }
-               }
-           }
-       }
-    default:
-       return current+2;
-    }
-}
-
-
-/*
- * 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.
- */
-
-netclear()
-{
-    register char *thisitem, *next;
-    char *good;
-#define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
-                               ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
-
-    thisitem = netobuf;
-
-    while ((next = nextitem(thisitem)) <= nbackp) {
-       thisitem = next;
-    }
-
-    /* Now, thisitem is first before/at boundary. */
-
-    good = netobuf;    /* where the good bytes go */
-
-    while (nfrontp > thisitem) {
-       if (wewant(thisitem)) {
-           int length;
-
-           next = thisitem;
-           do {
-               next = nextitem(next);
-           } while (wewant(next) && (nfrontp > next));
-           length = next-thisitem;
-           bcopy(thisitem, good, length);
-           good += length;
-           thisitem = next;
-       } else {
-           thisitem = nextitem(thisitem);
-       }
-    }
-
-    nbackp = netobuf;
-    nfrontp = good;            /* next byte to be sent */
-    neturg = 0;
+#ifdef TCSIG
+       (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
+#else  /* TCSIG */
+       init_termbuf();
+       *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034';
+#endif /* TCSIG */
 }
 }
-\f
-/*
- *  netflush
- *             Send as much data as possible to the network,
- *     handling requests for urgent data.
- */
-
 
 
-netflush()
+sendsusp()
 {
 {
-    int n;
-
-    if ((n = nfrontp - nbackp) > 0) {
-       /*
-        * 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)
-           return;
-       /* should blow this guy away... */
-       return;
-    }
-    nbackp += n;
-    if (nbackp >= neturg) {
-       neturg = 0;
-    }
-    if (nbackp == nfrontp) {
-       nbackp = nfrontp = netobuf;
-    }
-}
-
-cleanup()
-{
-       char *p;
-
-       p = line + sizeof("/dev/") - 1;
-       if (logout(p))
-               logwtmp(p, "", "");
-       (void)chmod(line, 0666);
-       (void)chown(line, 0, 0);
-       *p = 'p';
-       (void)chmod(line, 0666);
-       (void)chown(line, 0, 0);
-       shutdown(net, 2);
-       exit(1);
-}
-
-char   editedhost[32];
-
-edithost(pat, host)
-       register char *pat;
-       register char *host;
-{
-       register char *res = editedhost;
-
-       if (!pat)
-               pat = "";
-       while (*pat) {
-               switch (*pat) {
-
-               case '#':
-                       if (*host)
-                               host++;
-                       break;
-
-               case '@':
-                       if (*host)
-                               *res++ = *host++;
-                       break;
-
-               default:
-                       *res++ = *pat;
-                       break;
-
-               }
-               if (res == &editedhost[sizeof editedhost - 1]) {
-                       *res = '\0';
-                       return;
-               }
-               pat++;
-       }
-       if (*host)
-               strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
-       else
-               *res = '\0';
-       editedhost[sizeof editedhost - 1] = '\0';
-}
-
-static char *putlocation;
-
-puts(s)
-register char *s;
-{
-
-       while (*s)
-               putchr(*s++);
-}
-
-putchr(cc)
-{
-       *putlocation++ = cc;
+#ifdef SIGTSTP
+       ptyflush();     /* half-hearted */
+# ifdef        TCSIG
+       (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
+# else /* TCSIG */
+       *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032';
+# endif        /* TCSIG */
+#endif /* SIGTSTP */
 }
 
 }
 
-putf(cp, where)
-register char *cp;
-char *where;
+doeof()
 {
 {
-       char *slash;
-       char datebuffer[60];
-       extern char *rindex();
+       init_termbuf();
 
 
-       putlocation = where;
-
-       while (*cp) {
-               if (*cp != '%') {
-                       putchr(*cp++);
-                       continue;
-               }
-               switch (*++cp) {
-
-               case 't':
-                       slash = rindex(line, '/');
-                       if (slash == (char *) 0)
-                               puts(line);
-                       else
-                               puts(&slash[1]);
-                       break;
-
-               case 'h':
-                       puts(editedhost);
-                       break;
-
-               case 'd':
-                       get_date(datebuffer);
-                       puts(datebuffer);
-                       break;
-
-               case '%':
-                       putchr('%');
-                       break;
-               }
-               cp++;
-       }
+       *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004';
 }
 }
diff --git a/usr/src/libexec/telnetd/telnetd.h b/usr/src/libexec/telnetd/telnetd.h
new file mode 100644 (file)
index 0000000..b495540
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)telnetd.h   5.1 (Berkeley) %G%
+ */
+
+
+#include "defs.h"
+#include "ext.h"
+
+/* other external variables */
+extern char **environ;
+extern int errno;
+
diff --git a/usr/src/libexec/telnetd/termstat.c b/usr/src/libexec/telnetd/termstat.c
new file mode 100644 (file)
index 0000000..bd9484c
--- /dev/null
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)termstat.c 5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+#include "telnetd.h"
+
+/*
+ * local variables
+ */
+#ifdef LINEMODE
+static int _terminit = 0;
+static int def_tspeed = -1, def_rspeed = -1;
+#ifdef TIOCSWINSZ
+static int def_row = 0, def_col = 0;
+#endif
+#endif LINEMODE
+
+#ifdef CRAY2
+int    newmap = 1;     /* nonzero if \n maps to ^M^J */
+#endif
+
+#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
+ *             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,
+ *        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.
+ */
+localstat()
+{
+       void netflush();
+
+#ifdef CRAY2
+       /*
+        * Keep track of that ol' CR/NL mapping while we're in the
+        * neighborhood.
+        */
+       newmap = tty_isnewmap();
+#endif /* CRAY2 */
+
+       /*
+        * Check for state of BINARY options.
+        */
+       if (tty_isbinaryin()) {
+               if (hiswants[TELOPT_BINARY] == OPT_NO)
+                       willoption(TELOPT_BINARY, 1);
+       } else {
+               if (hiswants[TELOPT_BINARY] == OPT_YES)
+                       wontoption(TELOPT_BINARY, 1);
+       }
+
+       if (tty_isbinaryout()) {
+               if (mywants[TELOPT_BINARY] == OPT_NO)
+                       dooption(TELOPT_BINARY, 1);
+       } else {
+               if (mywants[TELOPT_BINARY] == OPT_YES)
+                       dontoption(TELOPT_BINARY, 1);
+       }
+
+       /*
+        * Check for changes to flow control if client supports it.
+        */
+       if (hisopts[TELOPT_LFLOW] == OPT_YES) {
+               if (tty_flowmode() != flowmode) {
+                       flowmode = tty_flowmode();
+                       (void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB,
+                               TELOPT_LFLOW, flowmode, IAC, SE);
+                       nfrontp += 6;
+               }
+       }
+
+       /*
+        * 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        KLUDGELINEMODE
+       /*
+        * If using kludge linemode and linemode is desired, it can't
+        * be done if normal line editing is not available on the
+        * pty.  This becomes the test for linemode on/off when
+        * using kludge linemode.
+        */
+       if (lmodetype == KLUDGE_LINEMODE && uselinemode && tty_israw())
+               uselinemode = 0;
+# endif        /* KLUDGELINEMODE */
+
+       /*
+        * 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 (tty_isecho() && uselinemode)
+               dontoption(TELOPT_ECHO, 1);
+       else
+               dooption(TELOPT_ECHO, 1);
+
+       /*
+        * 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 */
+                       wontoption(TELOPT_LINEMODE, 1);
+# ifdef        KLUDGELINEMODE
+               else if (lmodetype == KLUDGE_LINEMODE)
+                       dooption(TELOPT_SGA, 1);
+# endif        /* KLUDGELINEMODE */
+               linemode = uselinemode;
+               goto done;
+       }
+
+# ifdef        KLUDGELINEMODE
+       /*
+        * If using real linemode check edit modes for possible later use.
+        */
+       if (lmodetype == REAL_LINEMODE) {
+# endif        /* KLUDGELINEMODE */
+               useeditmode = 0;
+               if (tty_isediting())
+                       useeditmode |= MODE_EDIT;
+               if (tty_istrapsig())
+                       useeditmode |= MODE_TRAPSIG;
+# ifdef        KLUDGELINEMODE
+       }
+# 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) {
+                       dontoption(TELOPT_SGA, 1);
+               } else if (lmodetype == REAL_LINEMODE) {
+# endif        /* KLUDGELINEMODE */
+                       willoption(TELOPT_LINEMODE, 1);
+                       /* send along edit modes */
+                       (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
+                               TELOPT_LINEMODE, LM_MODE, useeditmode,
+                               IAC, SE);
+                       nfrontp += 7;
+                       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) {
+               /*
+                * If edit mode changed, send edit mode.
+                */
+                if (useeditmode != editmode) {
+                       /*
+                        * Send along appropriate edit mode mask.
+                        */
+                       (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB,
+                               TELOPT_LINEMODE, LM_MODE, useeditmode,
+                               IAC, SE);
+                       nfrontp += 7;
+                       editmode = useeditmode;
+               }
+                                                       
+
+               /*
+                * Check for changes to special characters in use.
+                */
+               start_slc(0);
+               check_slc();
+               end_slc(0);
+       }
+
+done:
+       /*
+        * 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 */
+
+
+/*
+ * 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.
+ */
+clientstat(code, parm1, parm2)
+register int code, parm1, parm2;
+{
+       void netflush();
+
+       /*
+        * 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
+                        * or if 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;
+                               (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
+                                       SB, TELOPT_LINEMODE, LM_MODE,
+                                                       useeditmode, IAC, SE);
+                               nfrontp += 7;
+                               editmode = useeditmode;
+                       }
+
+
+                       tty_setlinemode(uselinemode);
+
+                       linemode = uselinemode;
+
+               }
+               break;
+       
+       case LM_MODE:
+           {
+               register int mode, sig, ack;
+
+               /*
+                * 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 (useeditmode != editmode) {
+                       mode = (useeditmode & MODE_EDIT);
+                       sig = (useeditmode & MODE_TRAPSIG);
+
+                       if (mode != (editmode & LM_MODE)) {
+                               tty_setedit(mode);
+                       }
+                       if (sig != (editmode & MODE_TRAPSIG)) {
+                               tty_setsig(sig);
+                       }
+
+                       set_termbuf();
+
+                       if (!ack) {
+                               (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC,
+                                       SB, TELOPT_LINEMODE, LM_MODE,
+                                       useeditmode|MODE_ACK,
+                                       IAC, SE);
+                               nfrontp += 7;
+                       }
+               
+                       editmode = useeditmode;
+               }
+
+               break;
+
+           }  /* end of case LM_MODE */
+#endif /* LINEMODE */
+
+       case TELOPT_NAWS:
+#ifdef TIOCSWINSZ
+           {
+               struct winsize ws;
+
+#ifdef LINEMODE
+               /*
+                * Defer changing window size until after terminal is
+                * initialized.
+                */
+               if (terminit() == 0) {
+                       def_col = parm1;
+                       def_row = parm1;
+                       return;
+               }
+#endif /* LINEMODE */
+
+               /*
+                * Change window size as requested by client.
+                */
+
+               ws.ws_col = parm1;
+               ws.ws_row = parm2;
+               (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
+           }
+#endif /* TIOCSWINSZ */
+               
+               break;
+       
+       case TELOPT_TSPEED:
+           {
+#ifdef LINEMODE
+               /*
+                * Defer changing the terminal speed.
+                */
+               if (terminit() == 0) {
+                       def_tspeed = parm1;
+                       def_rspeed = parm2;
+                       return;
+               }
+#endif /* LINEMODE */
+               /*
+                * Change terminal speed as requested by client.
+                */
+               tty_tspeed(parm1);
+               tty_rspeed(parm2);
+               set_termbuf();
+
+               break;
+
+           }  /* end of case TELOPT_TSPEED */
+
+       default:
+               /* What? */
+               break;
+       }  /* end of switch */
+
+#ifdef CRAY2
+       /*
+        * Just in case of the likely event that we changed the pty state.
+        */
+       rcv_ioctl();
+#endif /* CRAY2 */
+
+       netflush();
+
+}  /* end of clientstat */
+
+#ifdef CRAY2
+termstat()
+{
+       needtermstat = 1;
+}
+
+_termstat()
+{
+       needtermstat = 0;
+       init_termbuf();
+       localstat();
+       rcv_ioctl();
+}
+#endif /* CRAY2 */
+
+#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.
+ */
+defer_terminit()
+{
+
+       /*
+        * local stuff that got deferred.
+        */
+       if (def_tspeed != -1) {
+               clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed);
+               def_tspeed = def_rspeed = 0;
+       }
+
+#ifdef TIOCSWINSZ
+       if (def_col || def_row) {
+               struct winsize ws;
+
+               ws.ws_col = def_col;
+               ws.ws_row = def_row;
+               (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
+       }
+#endif
+
+       /*
+        * 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()
+{
+       return _terminit;
+
+}  /* end of terminit */
+#endif /* LINEMODE */
diff --git a/usr/src/libexec/telnetd/utility.c b/usr/src/libexec/telnetd/utility.c
new file mode 100644 (file)
index 0000000..0aa1d19
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utility.c  5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+
+#include "telnetd.h"
+
+/*
+ * 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 netflush();
+
+    if (nfrontp-nbackp) {
+       netflush();
+    }
+    ncc = read(net, netibuf, sizeof netibuf);
+    if (ncc < 0) {
+       syslog(LOG_INFO, "ttloop:  read: %m\n");
+       exit(1);
+    } else if (ncc == 0) {
+       syslog(LOG_INFO, "ttloop:  peer died: %m\n");
+       exit(1);
+    }
+    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.
+ */
+stilloob(s)
+int    s;              /* socket number */
+{
+    static struct timeval timeout = { 0 };
+    fd_set     excepts;
+    int value;
+
+    do {
+       FD_ZERO(&excepts);
+       FD_SET(s, &excepts);
+       value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
+    } while ((value == -1) && (errno == EINTR));
+
+    if (value < 0) {
+       fatalperror(pty, "select");
+    }
+    if (FD_ISSET(s, &excepts)) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+ptyflush()
+{
+       int n;
+
+       if ((n = pfrontp - pbackp) > 0)
+               n = write(pty, pbackp, n);
+       if (n < 0)
+               return;
+       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(current)
+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 */
+       {
+           register 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.
+ */
+netclear()
+{
+    register char *thisitem, *next;
+    char *good;
+#define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
+                               ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+    thisitem = netobuf;
+
+    while ((next = nextitem(thisitem)) <= nbackp) {
+       thisitem = next;
+    }
+
+    /* Now, thisitem is first before/at boundary. */
+
+    good = netobuf;    /* where the good bytes go */
+
+    while (nfrontp > thisitem) {
+       if (wewant(thisitem)) {
+           int length;
+
+           next = thisitem;
+           do {
+               next = nextitem(next);
+           } while (wewant(next) && (nfrontp > next));
+           length = next-thisitem;
+           bcopy(thisitem, good, 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()
+{
+    int n;
+    extern int not42;
+
+    if ((n = nfrontp - nbackp) > 0) {
+       /*
+        * 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();
+    }
+    nbackp += n;
+    if (nbackp >= neturg) {
+       neturg = 0;
+    }
+    if (nbackp == nfrontp) {
+       nbackp = nfrontp = netobuf;
+    }
+    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
+ */
+writenet(ptr, len)
+register char *ptr;
+register 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();
+       }
+
+       bcopy(ptr, nfrontp, len);
+       nfrontp += len;
+
+}  /* end of writenet */
+
+
+/*
+ * miscellaneous functions doing a variety of little jobs follow ...
+ */
+
+
+fatal(f, msg)
+       int f;
+       char *msg;
+{
+       char buf[BUFSIZ];
+
+       (void) sprintf(buf, "telnetd: %s.\r\n", msg);
+       (void) write(f, buf, (int)strlen(buf));
+       sleep(1);       /*XXX*/
+       exit(1);
+}
+
+fatalperror(f, msg)
+       int f;
+       char *msg;
+{
+       char buf[BUFSIZ];
+       extern char *sys_errlist[];
+
+       (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
+       fatal(f, buf);
+}
+
+char editedhost[32];
+
+edithost(pat, host)
+       register char *pat;
+       register char *host;
+{
+       register char *res = editedhost;
+       char *strncpy();
+
+       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;
+
+putstr(s)
+register char *s;
+{
+
+       while (*s)
+               putchr(*s++);
+}
+
+putchr(cc)
+{
+       *putlocation++ = cc;
+}
+
+putf(cp, where)
+register char *cp;
+char *where;
+{
+       char *slash;
+#ifndef        NO_GETTYTAB
+       char datebuffer[60];
+#endif /* NO_GETTYTAB */
+       extern char *rindex();
+
+       putlocation = where;
+
+       while (*cp) {
+               if (*cp != '%') {
+                       putchr(*cp++);
+                       continue;
+               }
+               switch (*++cp) {
+
+               case 't':
+                       slash = rindex(line, '/');
+                       if (slash == (char *) 0)
+                               putstr(line);
+                       else
+                               putstr(&slash[1]);
+                       break;
+
+               case 'h':
+                       putstr(editedhost);
+                       break;
+
+#ifndef        NO_GETTYTAB
+               case 'd':
+                       get_date(datebuffer);
+                       putstr(datebuffer);
+                       break;
+#endif /* NO_GETTYTAB */
+
+               case '%':
+                       putchr('%');
+                       break;
+               }
+               cp++;
+       }
+}
+
+/*ARGSUSED*/
+#ifdef NO_GETTYTAB
+getent(cp, name)
+char *cp, *name;
+{
+       return(0);
+}
+
+/*ARGSUSED*/
+char *
+getstr(cp, cpp)
+char *cp, **cpp;
+{
+       return(0);
+}
+#endif /* NO_GETTYTAB */