/*
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
#ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1983 Regents of the University of California.\n\
- All rights reserved.\n";
-#endif not lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)telnetd.c 5.9 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)telnetd.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+#include "telnetd.h"
+#include "pathnames.h"
+
+#if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)
/*
- * Stripped-down telnet server.
+ * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can
+ * use it to tell us to turn off all the socket security code,
+ * since that is only used in UNICOS 7.0 and later.
*/
-#include <sys/types.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>
+# undef _SC_CRAY_SECURE_SYS
+#endif
+
+#if defined(_SC_CRAY_SECURE_SYS)
+#include <sys/sysv.h>
+#include <sys/secdev.h>
+# ifdef SO_SEC_MULTI /* 8.0 code */
+#include <sys/secparm.h>
+#include <sys/usrv.h>
+# endif /* SO_SEC_MULTI */
+int secflag;
+char tty_dev[16];
+struct secdev dv;
+struct sysv sysv;
+# ifdef SO_SEC_MULTI /* 8.0 code */
+struct socksec ss;
+# else /* SO_SEC_MULTI */ /* 7.0 code */
+struct socket_security ss;
+# endif /* SO_SEC_MULTI */
+#endif /* _SC_CRAY_SECURE_SYS */
+
+#if defined(AUTHENTICATION)
+#include <libtelnet/auth.h>
+int auth_level = 0;
+#endif
+#if defined(SecurID)
+int require_SecurID = 0;
+#endif
+
+extern int utmp_len;
+int registerd_host_only = 0;
+
+#ifdef STREAMSPTY
+# include <stropts.h>
+# include <termio.h>
+/* make sure we don't get the bsd version */
+# include "/usr/include/sys/tty.h"
+# include <sys/ptyvar.h>
-#include <stdio.h>
-#include <signal.h>
-#include <errno.h>
-#include <sgtty.h>
-#include <netdb.h>
-#include <syslog.h>
-
-#define BELL '\07'
-#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
+/*
+ * Because of the way ptyibuf is used with streams messages, we need
+ * ptyibuf+1 to be on a full-word boundary. The following wierdness
+ * is simply to make that happen.
+ */
+char ptyibufbuf[BUFSIZ+4];
+char *ptyibuf = ptyibufbuf+3;
+char *ptyip = ptyibufbuf+3;
+char ptyibuf2[BUFSIZ];
+unsigned char ctlbuf[BUFSIZ];
+struct strbuf strbufc, strbufd;
-char hisopts[256];
-char myopts[256];
+int readstream();
-char doopt[] = { IAC, DO, '%', 'c', 0 };
-char dont[] = { IAC, DONT, '%', 'c', 0 };
-char will[] = { IAC, WILL, '%', 'c', 0 };
-char wont[] = { IAC, WONT, '%', 'c', 0 };
+#else /* ! STREAMPTY */
/*
- * I/O data buffers, pointers, and counters.
+ * I/O data buffers,
+ * pointers, and counters.
*/
char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
-char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
-char netibuf[BUFSIZ], *netip = netibuf;
-char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
-char *neturg = 0; /* one past last bye of urgent data */
-int pcc, ncc;
-
-int pty, net;
-int inter;
-extern char **environ;
-extern int errno;
-char *line;
-int SYNCHing = 0; /* we are in TELNET SYNCH mode */
+char ptyibuf2[BUFSIZ];
+
+#endif /* ! STREAMPTY */
+
+int hostinfo = 1; /* do we print login banner? */
+
+#ifdef CRAY
+extern int newmap; /* nonzero if \n maps to ^M^J */
+int lowpty = 0, highpty; /* low, high pty numbers */
+#endif /* CRAY */
+
+int debug = 0;
+int keepalive = 1;
+char *progname;
+
+extern void usage P((void));
+
/*
- * The following are some clocks used to decide how to interpret
- * the relationship between various variables.
+ * The string to pass to getopt(). We do it this way so
+ * that only the actual options that we support will be
+ * passed off to getopt().
*/
+char valid_opts[] = {
+ 'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',
+#ifdef AUTHENTICATION
+ 'a', ':', 'X', ':',
+#endif
+#ifdef BFTPDAEMON
+ 'B',
+#endif
+#ifdef DIAGNOSTICS
+ 'D', ':',
+#endif
+#ifdef ENCRYPTION
+ 'e', ':',
+#endif
+#if defined(CRAY) && defined(NEWINIT)
+ 'I', ':',
+#endif
+#ifdef LINEMODE
+ 'l',
+#endif
+#ifdef CRAY
+ 'r', ':',
+#endif
+#ifdef SecurID
+ 's',
+#endif
+ '\0'
+};
-struct {
- int
- system, /* what the current time is */
- echotoggle, /* last time user entered echo character */
- modenegotiated, /* last time operating mode negotiated */
- didnetreceive, /* last time we read data from network */
- gotDM; /* when did we last see a data mark */
-} clocks;
-
-#define settimer(x) clocks.x = clocks.system++
-\f
main(argc, argv)
char *argv[];
{
struct sockaddr_in from;
int on = 1, fromlen;
+ register int ch;
+ extern char *optarg;
+ extern int optind;
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ int tos = -1;
+#endif
+
+ pfrontp = pbackp = ptyobuf;
+ netip = netibuf;
+ nfrontp = nbackp = netobuf;
+#ifdef ENCRYPTION
+ nclearto = 0;
+#endif /* ENCRYPTION */
+
+ progname = *argv;
+
+#ifdef CRAY
+ /*
+ * Get number of pty's before trying to process options,
+ * which may include changing pty range.
+ */
+ highpty = getnpty();
+#endif /* CRAY */
-#if defined(DEBUG)
- {
+ while ((ch = getopt(argc, argv, valid_opts)) != EOF) {
+ switch(ch) {
+
+#ifdef AUTHENTICATION
+ case 'a':
+ /*
+ * Check for required authentication level
+ */
+ if (strcmp(optarg, "debug") == 0) {
+ extern int auth_debug_mode;
+ auth_debug_mode = 1;
+ } else if (strcasecmp(optarg, "none") == 0) {
+ auth_level = 0;
+ } else if (strcasecmp(optarg, "other") == 0) {
+ auth_level = AUTH_OTHER;
+ } else if (strcasecmp(optarg, "user") == 0) {
+ auth_level = AUTH_USER;
+ } else if (strcasecmp(optarg, "valid") == 0) {
+ auth_level = AUTH_VALID;
+ } else if (strcasecmp(optarg, "off") == 0) {
+ /*
+ * This hack turns off authentication
+ */
+ auth_level = -1;
+ } else {
+ fprintf(stderr,
+ "telnetd: unknown authorization level for -a\n");
+ }
+ break;
+#endif /* AUTHENTICATION */
+
+#ifdef BFTPDAEMON
+ case 'B':
+ bftpd++;
+ break;
+#endif /* BFTPDAEMON */
+
+ case 'd':
+ if (strcmp(optarg, "ebug") == 0) {
+ debug++;
+ break;
+ }
+ usage();
+ /* NOTREACHED */
+ break;
+
+#ifdef DIAGNOSTICS
+ case 'D':
+ /*
+ * Check for desired diagnostics capabilities.
+ */
+ if (!strcmp(optarg, "report")) {
+ diagnostic |= TD_REPORT|TD_OPTIONS;
+ } else if (!strcmp(optarg, "exercise")) {
+ diagnostic |= TD_EXERCISE;
+ } else if (!strcmp(optarg, "netdata")) {
+ diagnostic |= TD_NETDATA;
+ } else if (!strcmp(optarg, "ptydata")) {
+ diagnostic |= TD_PTYDATA;
+ } else if (!strcmp(optarg, "options")) {
+ diagnostic |= TD_OPTIONS;
+ } else {
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+#endif /* DIAGNOSTICS */
+
+#ifdef ENCRYPTION
+ case 'e':
+ if (strcmp(optarg, "debug") == 0) {
+ extern int encrypt_debug_mode;
+ encrypt_debug_mode = 1;
+ break;
+ }
+ usage();
+ /* NOTREACHED */
+ break;
+#endif /* ENCRYPTION */
+
+ case 'h':
+ hostinfo = 0;
+ break;
+
+#if defined(CRAY) && defined(NEWINIT)
+ case 'I':
+ {
+ extern char *gen_id;
+ gen_id = optarg;
+ break;
+ }
+#endif /* defined(CRAY) && defined(NEWINIT) */
+
+#ifdef LINEMODE
+ case 'l':
+ alwayslinemode = 1;
+ break;
+#endif /* LINEMODE */
+
+ case 'k':
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ lmodetype = NO_AUTOKLUDGE;
+#else
+ /* ignore -k option if built without kludge linemode */
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+ break;
+
+ case 'n':
+ keepalive = 0;
+ break;
+
+#ifdef CRAY
+ case 'r':
+ {
+ char *strchr();
+ char *c;
+
+ /*
+ * Allow the specification of alterations
+ * to the pty search range. It is legal to
+ * specify only one, and not change the
+ * other from its default.
+ */
+ c = strchr(optarg, '-');
+ if (c) {
+ *c++ = '\0';
+ highpty = atoi(c);
+ }
+ if (*optarg != '\0')
+ lowpty = atoi(optarg);
+ if ((lowpty > highpty) || (lowpty < 0) ||
+ (highpty > 32767)) {
+ usage();
+ /* NOT REACHED */
+ }
+ break;
+ }
+#endif /* CRAY */
+
+#ifdef SecurID
+ case 's':
+ /* SecurID required */
+ require_SecurID = 1;
+ break;
+#endif /* SecurID */
+ case 'S':
+#ifdef HAS_GETTOS
+ if ((tos = parsetos(optarg, "tcp")) < 0)
+ fprintf(stderr, "%s%s%s\n",
+ "telnetd: Bad TOS argument '", optarg,
+ "'; will try to use default TOS");
+#else
+ fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
+ "-S flag not supported\n");
+#endif
+ break;
+
+ case 'u':
+ utmp_len = atoi(optarg);
+ break;
+
+ case 'U':
+ registerd_host_only = 1;
+ break;
+
+#ifdef AUTHENTICATION
+ case 'X':
+ /*
+ * Check for invalid authentication types
+ */
+ auth_disable_name(optarg);
+ break;
+#endif /* AUTHENTICATION */
+
+ default:
+ fprintf(stderr, "telnetd: %c: unknown option\n", ch);
+ /* FALLTHROUGH */
+ case '?':
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (debug) {
int s, ns, foo;
struct servent *sp;
static struct sockaddr_in sin = { AF_INET };
- sp = getservbyname("telnet", "tcp");
- if (sp == 0) {
+ if (argc > 1) {
+ usage();
+ /* NOT REACHED */
+ } else if (argc == 1) {
+ 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);
+ usage();
+ /* NOT REACHED */
+ }
+ 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");
exit(1);
- }
- sin.sin_port = sp->s_port;
- argc--, argv++;
- if (argc > 0) {
- sin.sin_port = atoi(*argv);
- sin.sin_port = htons((u_short)sin.sin_port);
+ }
+ sin.sin_port = sp->s_port;
}
s = socket(AF_INET, SOCK_STREAM, 0);
perror("telnetd: socket");;
exit(1);
}
- if (bind(s, &sin, sizeof sin) < 0) {
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on));
+ if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
perror("bind");
exit(1);
}
exit(1);
}
foo = sizeof sin;
- ns = accept(s, &sin, &foo);
+ ns = accept(s, (struct sockaddr *)&sin, &foo);
if (ns < 0) {
perror("accept");
exit(1);
}
- dup2(ns, 0);
- close(s);
+ (void) dup2(ns, 0);
+ (void) close(ns);
+ (void) close(s);
+#ifdef convex
+ } else if (argc == 1) {
+ ; /* VOID*/ /* Just ignore the host/port name */
+#endif
+ } else if (argc > 0) {
+ usage();
+ /* NOT REACHED */
+ }
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ secflag = sysconf(_SC_CRAY_SECURE_SYS);
+
+ /*
+ * Get socket's security label
+ */
+ if (secflag) {
+ int szss = sizeof(ss);
+#ifdef SO_SEC_MULTI /* 8.0 code */
+ int sock_multi;
+ int szi = sizeof(int);
+#endif /* SO_SEC_MULTI */
+
+ bzero((char *)&dv, sizeof(dv));
+
+ if (getsysv(&sysv, sizeof(struct sysv)) != 0) {
+ perror("getsysv");
+ exit(1);
+ }
+
+ /*
+ * Get socket security label and set device values
+ * {security label to be set on ttyp device}
+ */
+#ifdef SO_SEC_MULTI /* 8.0 code */
+ if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,
+ (char *)&ss, &szss) < 0) ||
+ (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,
+ (char *)&sock_multi, &szi) < 0)) {
+ perror("getsockopt");
+ exit(1);
+ } else {
+ dv.dv_actlvl = ss.ss_actlabel.lt_level;
+ dv.dv_actcmp = ss.ss_actlabel.lt_compart;
+ if (!sock_multi) {
+ dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;
+ dv.dv_valcmp = dv.dv_actcmp;
+ } else {
+ dv.dv_minlvl = ss.ss_minlabel.lt_level;
+ dv.dv_maxlvl = ss.ss_maxlabel.lt_level;
+ dv.dv_valcmp = ss.ss_maxlabel.lt_compart;
+ }
+ dv.dv_devflg = 0;
+ }
+#else /* SO_SEC_MULTI */ /* 7.0 code */
+ if (getsockopt(0, SOL_SOCKET, SO_SECURITY,
+ (char *)&ss, &szss) >= 0) {
+ dv.dv_actlvl = ss.ss_slevel;
+ dv.dv_actcmp = ss.ss_compart;
+ dv.dv_minlvl = ss.ss_minlvl;
+ dv.dv_maxlvl = ss.ss_maxlvl;
+ dv.dv_valcmp = ss.ss_maxcmp;
+ }
+#endif /* SO_SEC_MULTI */
}
-#endif /* defined(DEBUG) */
+#endif /* _SC_CRAY_SECURE_SYS */
+
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) {
+ if (keepalive &&
+ setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&on, sizeof (on)) < 0) {
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
}
- doit(0, &from);
+
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ {
+# if defined(HAS_GETTOS)
+ struct tosent *tp;
+ if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
+ tos = tp->t_tos;
+# endif
+ if (tos < 0)
+ tos = 020; /* Low Delay bit */
+ if (tos
+ && (setsockopt(0, IPPROTO_IP, IP_TOS,
+ (char *)&tos, sizeof(tos)) < 0)
+ && (errno != ENOPROTOOPT) )
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
+ net = 0;
+ doit(&from);
+ /* NOTREACHED */
+} /* end of main */
+
+ void
+usage()
+{
+ fprintf(stderr, "Usage: telnetd");
+#ifdef AUTHENTICATION
+ fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t");
+#endif
+#ifdef BFTPDAEMON
+ fprintf(stderr, " [-B]");
+#endif
+ fprintf(stderr, " [-debug]");
+#ifdef DIAGNOSTICS
+ fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
+#endif
+#ifdef AUTHENTICATION
+ fprintf(stderr, " [-edebug]");
+#endif
+ fprintf(stderr, " [-h]");
+#if defined(CRAY) && defined(NEWINIT)
+ fprintf(stderr, " [-Iinitid]");
+#endif
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ fprintf(stderr, " [-k]");
+#endif
+#ifdef LINEMODE
+ fprintf(stderr, " [-l]");
+#endif
+ fprintf(stderr, " [-n]");
+#ifdef CRAY
+ fprintf(stderr, " [-r[lowpty]-[highpty]]");
+#endif
+ fprintf(stderr, "\n\t");
+#ifdef SecurID
+ fprintf(stderr, " [-s]");
+#endif
+#ifdef HAS_GETTOS
+ fprintf(stderr, " [-S tos]");
+#endif
+#ifdef AUTHENTICATION
+ fprintf(stderr, " [-X auth-type]");
+#endif
+ fprintf(stderr, " [-u utmp_hostname_length] [-U]");
+ fprintf(stderr, " [port]\n");
+ exit(1);
+}
+
+/*
+ * getterminaltype
+ *
+ * Ask the other end to send along its terminal type and speed.
+ * Output is the variable terminaltype filled in.
+ */
+static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
+
+ int
+getterminaltype(name)
+ char *name;
+{
+ int retval = -1;
+ void _gettermname();
+
+ settimer(baseline);
+#if defined(AUTHENTICATION)
+ /*
+ * Handle the Authentication option before we do anything else.
+ */
+ send_do(TELOPT_AUTHENTICATION, 1);
+ while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
+ ttloop();
+ if (his_state_is_will(TELOPT_AUTHENTICATION)) {
+ retval = auth_wait(name);
+ }
+#endif
+
+#ifdef ENCRYPTION
+ send_will(TELOPT_ENCRYPT, 1);
+#endif /* ENCRYPTION */
+ send_do(TELOPT_TTYPE, 1);
+ send_do(TELOPT_TSPEED, 1);
+ send_do(TELOPT_XDISPLOC, 1);
+ send_do(TELOPT_ENVIRON, 1);
+ while (
+#ifdef ENCRYPTION
+ his_do_dont_is_changing(TELOPT_ENCRYPT) ||
+#endif /* ENCRYPTION */
+ his_will_wont_is_changing(TELOPT_TTYPE) ||
+ his_will_wont_is_changing(TELOPT_TSPEED) ||
+ his_will_wont_is_changing(TELOPT_XDISPLOC) ||
+ his_will_wont_is_changing(TELOPT_ENVIRON)) {
+ ttloop();
+ }
+#ifdef ENCRYPTION
+ /*
+ * Wait for the negotiation of what type of encryption we can
+ * send with. If autoencrypt is not set, this will just return.
+ */
+ if (his_state_is_will(TELOPT_ENCRYPT)) {
+ encrypt_wait();
+ }
+#endif /* ENCRYPTION */
+ if (his_state_is_will(TELOPT_TSPEED)) {
+ static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sbbuf, nfrontp, sizeof sbbuf);
+ nfrontp += sizeof sbbuf;
+ }
+ if (his_state_is_will(TELOPT_XDISPLOC)) {
+ static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sbbuf, nfrontp, sizeof sbbuf);
+ nfrontp += sizeof sbbuf;
+ }
+ if (his_state_is_will(TELOPT_ENVIRON)) {
+ static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE };
+
+ bcopy(sbbuf, nfrontp, sizeof sbbuf);
+ nfrontp += sizeof sbbuf;
+ }
+ if (his_state_is_will(TELOPT_TTYPE)) {
+
+ bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+ nfrontp += sizeof ttytype_sbbuf;
+ }
+ if (his_state_is_will(TELOPT_TSPEED)) {
+ while (sequenceIs(tspeedsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_XDISPLOC)) {
+ while (sequenceIs(xdisplocsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_ENVIRON)) {
+ while (sequenceIs(environsubopt, baseline))
+ ttloop();
+ }
+ if (his_state_is_will(TELOPT_TTYPE)) {
+ char first[256], last[256];
+
+ while (sequenceIs(ttypesubopt, baseline))
+ ttloop();
+
+ /*
+ * If the other side has already disabled the option, then
+ * we have to just go with what we (might) have already gotten.
+ */
+ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
+ (void) 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))
+ break;
+ if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
+ his_state_is_wont(TELOPT_TTYPE)) {
+ /*
+ * We've hit the end. If this is the same as
+ * the first name, just go with it.
+ */
+ if (strncmp(first, terminaltype, sizeof(first)) == 0)
+ break;
+ /*
+ * Get the terminal name one more time, so that
+ * RFC1091 compliant telnets will cycle back to
+ * the start of the list.
+ */
+ _gettermname();
+ if (strncmp(first, terminaltype, sizeof(first)) != 0)
+ (void) strncpy(terminaltype, first, sizeof(first));
+ break;
+ }
+ }
+ }
+ }
+ return(retval);
+} /* end of getterminaltype */
+
+ void
+_gettermname()
+{
+ /*
+ * If the client turned off the option,
+ * we can't send another request, so we
+ * just return.
+ */
+ if (his_state_is_wont(TELOPT_TTYPE))
+ return;
+ settimer(baseline);
+ bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+ nfrontp += sizeof ttytype_sbbuf;
+ while (sequenceIs(ttypesubopt, baseline))
+ ttloop();
+}
+
+ int
+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);
}
-char *envinit[] = { "TERM=network", 0 };
-int cleanup();
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif /* MAXHOSTNAMELEN */
+
+char *hostname;
+char host_name[MAXHOSTNAMELEN];
+char remote_host_name[MAXHOSTNAMELEN];
+
+#ifndef convex
+extern void telnet P((int, int));
+#else
+extern void telnet P((int, int, char *));
+#endif
/*
* Get a pty, scan input lines.
*/
-doit(f, who)
- int f;
+doit(who)
struct sockaddr_in *who;
{
char *host, *inet_ntoa();
- int i, p, t;
- struct sgttyb b;
+ int t;
struct hostent *hp;
- char c;
+ int level;
+ int ptynum;
+ char user_name[256];
- for (c = 'p'; c <= 's'; c++) {
- struct stat stb;
+ /*
+ * Find an available pty to use.
+ */
+#ifndef convex
+ pty = getpty(&ptynum);
+ if (pty < 0)
+ fatal(net, "All network ports in use");
+#else
+ for (;;) {
+ char *lp;
+ extern char *line, *getpty();
+
+ if ((lp = getpty()) == NULL)
+ fatal(net, "Out of ptys");
- line = "/dev/ptyXX";
- line[strlen("/dev/pty")] = c;
- line[strlen("/dev/ptyp")] = '0';
- if (stat(line, &stb) < 0)
+ if ((pty = open(lp, 2)) >= 0) {
+ strcpy(line,lp);
+ line[5] = 't';
break;
- for (i = 0; i < 16; i++) {
- line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
- p = open(line, 2);
- if (p > 0)
- goto gotpty;
}
}
- fatal(f, "All network ports in use");
- /*NOTREACHED*/
-gotpty:
- dup2(f, 0);
- line[strlen("/dev/")] = 't';
- t = open("/dev/tty", O_RDWR);
- if (t >= 0) {
- ioctl(t, TIOCNOTTY, 0);
- close(t);
+#endif
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ /*
+ * set ttyp line security label
+ */
+ if (secflag) {
+ char slave_dev[16];
+
+ sprintf(tty_dev, "/dev/pty/%03d", ptynum);
+ if (setdevs(tty_dev, &dv) < 0)
+ fatal(net, "cannot set pty security");
+ sprintf(slave_dev, "/dev/ttyp%03d", ptynum);
+ if (setdevs(slave_dev, &dv) < 0)
+ fatal(net, "cannot set tty security");
}
- t = open(line, O_RDWR);
- if (t < 0)
- fatalperror(f, line, errno);
- ioctl(t, TIOCGETP, &b);
- b.sg_flags = CRMOD|XTABS|ANYP;
- ioctl(t, TIOCSETP, &b);
- ioctl(p, TIOCGETP, &b);
- b.sg_flags &= ~ECHO;
- ioctl(p, TIOCSETP, &b);
- hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
+#endif /* _SC_CRAY_SECURE_SYS */
+
+ /* get name of connected client */
+ hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
who->sin_family);
- if (hp)
+
+ if (hp == NULL && registerd_host_only) {
+ fatal(net, "Couldn't resolve your address into a host name.\r\n\
+ Please contact your net administrator");
+ } else if (hp &&
+ (strlen(hp->h_name) <= ((utmp_len < 0) ? -utmp_len : utmp_len))) {
host = hp->h_name;
- else
+ } else {
host = inet_ntoa(who->sin_addr);
- if ((i = fork()) < 0)
- fatalperror(f, "fork", errno);
- if (i)
- telnet(f, p);
- close(f);
- close(p);
- dup2(t, 0);
- dup2(t, 1);
- dup2(t, 2);
- close(t);
- environ = envinit;
- execl("/bin/login", "login", "-h", host, 0);
- fatalperror(f, "/bin/login", errno);
- /*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);
-}
+ }
+ /*
+ * We must make a copy because Kerberos is probably going
+ * to also do a gethost* and overwrite the static data...
+ */
+ strncpy(remote_host_name, host, sizeof(remote_host_name)-1);
+ remote_host_name[sizeof(remote_host_name)-1] = 0;
+ host = remote_host_name;
-fatalperror(f, msg, errno)
- int f;
- char *msg;
- int errno;
-{
- char buf[BUFSIZ];
- extern char *sys_errlist[];
+ (void) gethostname(host_name, sizeof (host_name));
+ hostname = host_name;
- (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
- fatal(f, buf);
-}
+#if defined(AUTHENTICATION) || defined(ENCRYPTION)
+ auth_encrypt_init(hostname, host, "TELNETD", 1);
+#endif
+ init_env();
+ /*
+ * get terminal type.
+ */
+ *user_name = 0;
+ level = getterminaltype(user_name);
+ setenv("TERM", terminaltype ? terminaltype : "network", 1);
-/*
- * Check a descriptor to see if out of band data exists on it.
- */
+ /*
+ * Start up the login process on the slave side of the terminal
+ */
+#ifndef convex
+ startslave(host, level, user_name);
+
+#if defined(_SC_CRAY_SECURE_SYS)
+ if (secflag) {
+ if (setulvl(dv.dv_actlvl) < 0)
+ fatal(net,"cannot setulvl()");
+ if (setucmp(dv.dv_actcmp) < 0)
+ fatal(net, "cannot setucmp()");
+ }
+#endif /* _SC_CRAY_SECURE_SYS */
+ telnet(net, pty); /* begin server processing */
+#else
+ telnet(net, pty, host);
+#endif
+ /*NOTREACHED*/
+} /* end of doit */
-stilloob(s)
-int s; /* socket number */
+#if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50)
+ int
+Xterm_output(ibufp, obuf, icountp, ocount)
+ char **ibufp, *obuf;
+ int *icountp, ocount;
{
- 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", errno);
- }
- if (FD_ISSET(s, &excepts)) {
- return 1;
- } else {
- return 0;
- }
+ int ret;
+ ret = term_output(*ibufp, obuf, *icountp, ocount);
+ *ibufp += *icountp;
+ *icountp = 0;
+ return(ret);
}
-\f
+#define term_output Xterm_output
+#endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */
+
/*
* Main loop. Select from pty and network, and
* hand data to telnet receiver finite state machine.
*/
+ void
+#ifndef convex
telnet(f, p)
+#else
+telnet(f, p, host)
+#endif
+ int f, p;
+#ifdef convex
+ char *host;
+#endif
{
int on = 1;
- char hostname[32];
+#define TABBUFSIZ 512
+ char defent[TABBUFSIZ];
+ char defstrs[TABBUFSIZ];
+#undef TABBUFSIZ
+ char *HE;
+ char *HN;
+ char *IM;
+ void netflush();
- net = f, pty = p;
- ioctl(f, FIONBIO, &on);
- ioctl(p, FIONBIO, &on);
- signal(SIGTSTP, SIG_IGN);
- signal(SIGCHLD, cleanup);
- setpgrp(0, 0);
+ /*
+ * Initialize the slc mapping table.
+ */
+ 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.
*/
- dooption(TELOPT_ECHO);
- dooption(TELOPT_SGA);
+ if (my_state_is_wont(TELOPT_SGA))
+ send_will(TELOPT_SGA, 1);
+ /*
+ * Is the client side a 4.2 (NOT 4.3) system? We need to know this
+ * because 4.2 clients are unable to deal with TCP urgent data.
+ *
+ * To find out, we send out a "DO ECHO". If the remote system
+ * answers "WILL ECHO" it is probably a 4.2 client, and we note
+ * that fact ("WILL ECHO" ==> that the client will echo what
+ * WE, the server, sends it; it does NOT mean that the client will
+ * echo the terminal input).
+ */
+ send_do(TELOPT_ECHO, 1);
+
+#ifdef LINEMODE
+ if (his_state_is_wont(TELOPT_LINEMODE)) {
+ /* Query the peer for linemode support by trying to negotiate
+ * the linemode option.
+ */
+ linemode = 0;
+ editmode = 0;
+ send_do(TELOPT_LINEMODE, 1); /* send do linemode */
+ }
+#endif /* LINEMODE */
+
+ /*
+ * Send along a couple of other options that we wish to negotiate.
+ */
+ send_do(TELOPT_NAWS, 1);
+ send_will(TELOPT_STATUS, 1);
+ flowmode = 1; /* default flow control state */
+ restartany = -1; /* uninitialized... */
+ send_do(TELOPT_LFLOW, 1);
+
+ /*
+ * Spin, waiting for a response from the DO ECHO. However,
+ * some REALLY DUMB telnets out there might not respond
+ * to the DO ECHO. So, we spin looking for NAWS, (most dumb
+ * telnets so far seem to respond with WONT for a DO that
+ * they don't understand...) because by the time we get the
+ * response, it will already have processed the DO ECHO.
+ * Kludge upon kludge.
+ */
+ while (his_will_wont_is_changing(TELOPT_NAWS))
+ ttloop();
+
+ /*
+ * But...
+ * The client might have sent a WILL NAWS as part of its
+ * startup code; if so, we'll be here before we get the
+ * response to the DO ECHO. We'll make the assumption
+ * that any implementation that understands about NAWS
+ * is a modern enough implementation that it will respond
+ * to our DO ECHO request; hence we'll do another spin
+ * waiting for the ECHO option to settle down, which is
+ * what we wanted to do in the first place...
+ */
+ if (his_want_state_is_will(TELOPT_ECHO) &&
+ his_state_is_will(TELOPT_NAWS)) {
+ while (his_will_wont_is_changing(TELOPT_ECHO))
+ ttloop();
+ }
+ /*
+ * On the off chance that the telnet client is broken and does not
+ * respond to the DO ECHO we sent, (after all, we did send the
+ * DO NAWS negotiation after the DO ECHO, and we won't get here
+ * until a response to the DO NAWS comes back) simulate the
+ * receipt of a will echo. This will also send a WONT ECHO
+ * to the client, since we assume that the client failed to
+ * respond because it believes that it is already in DO ECHO
+ * mode, which we do not want.
+ */
+ if (his_want_state_is_will(TELOPT_ECHO)) {
+ DIAG(TD_OPTIONS,
+ {sprintf(nfrontp, "td: simulating recv\r\n");
+ nfrontp += strlen(nfrontp);});
+ willoption(TELOPT_ECHO);
+ }
+
+ /*
+ * Finally, to clean things up, we turn on our echo. This
+ * will break stupid 4.2 telnets out of local terminal echo.
+ */
+
+ if (my_state_is_wont(TELOPT_ECHO))
+ send_will(TELOPT_ECHO, 1);
+
+#ifndef STREAMSPTY
+ /*
+ * Turn on packet mode
+ */
+ (void) ioctl(p, TIOCPKT, (char *)&on);
+#endif
+
+#if defined(LINEMODE) && defined(KLUDGELINEMODE)
+ /*
+ * Continuing line mode support. If client does not support
+ * real linemode, attempt to negotiate kludge linemode by sending
+ * the do timing mark sequence.
+ */
+ if (lmodetype < REAL_LINEMODE)
+ send_do(TELOPT_TM, 1);
+#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
+
+ /*
+ * Call telrcv() once to pick up anything received during
+ * terminal type negotiation, 4.2/4.3 determination, and
+ * linemode negotiation.
+ */
+ telrcv();
+
+ (void) ioctl(f, FIONBIO, (char *)&on);
+ (void) ioctl(p, FIONBIO, (char *)&on);
+#if defined(CRAY2) && defined(UNICOS5)
+ init_termdriver(f, p, interrupt, sendbrk);
+#endif
+
+#if defined(SO_OOBINLINE)
+ (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE,
+ (char *)&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) && defined(UNICOS5)
+ /*
+ * 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
+
+#ifdef TIOCNOTTY
+ {
+ register int t;
+ t = open(_PATH_TTY, O_RDWR);
+ if (t >= 0) {
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+ }
+#endif
+
+#if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY)
+ (void) setsid();
+ ioctl(p, TIOCSCTTY, 0);
+#endif
+
/*
* Show banner that getty never gave.
+ *
+ * We put the banner in the pty input buffer. This way, it
+ * gets carriage return null processing, etc., just like all
+ * other pty --> client data.
+ */
+
+#if !defined(CRAY) || !defined(NEWINIT)
+ if (getenv("USER"))
+ hostinfo = 0;
+#endif
+
+ if (getent(defent, "default") == 1) {
+ char *getstr();
+ char *cp=defstrs;
+
+ HE = getstr("he", &cp);
+ HN = getstr("hn", &cp);
+ IM = getstr("im", &cp);
+ if (HN && *HN)
+ (void) strcpy(host_name, HN);
+ if (IM == 0)
+ IM = "";
+ } else {
+ IM = DEFAULT_IM;
+ HE = 0;
+ }
+ edithost(HE, host_name);
+ if (hostinfo && *IM)
+ putf(IM, ptyibuf2);
+
+ if (pcc)
+ (void) strncat(ptyibuf2, ptyip, pcc+1);
+ ptyip = ptyibuf2;
+ pcc = strlen(ptyip);
+#ifdef LINEMODE
+ /*
+ * Last check to make sure all our states are correct.
*/
- gethostname(hostname, sizeof (hostname));
- sprintf(nfrontp, BANNER, hostname, "");
- nfrontp += strlen(nfrontp);
+ init_termbuf();
+ localstat();
+#endif /* LINEMODE */
+
+ DIAG(TD_REPORT,
+ {sprintf(nfrontp, "td: Entering processing loop\r\n");
+ nfrontp += strlen(nfrontp);});
+
+#ifdef convex
+ startslave(host);
+#endif
+
for (;;) {
fd_set ibits, obits, xbits;
register int c;
if (ncc < 0 && pcc < 0)
break;
+#if defined(CRAY2) && defined(UNICOS5)
+ if (needtermstat)
+ _termstat();
+#endif /* defined(CRAY2) && defined(UNICOS5) */
FD_ZERO(&ibits);
FD_ZERO(&obits);
FD_ZERO(&xbits);
* Something to read from the network...
*/
if (FD_ISSET(net, &ibits)) {
-#if !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY)
+#if !defined(SO_OOBINLINE)
/*
- * In 4.2 (and some early 4.3) systems, the
+ * In 4.2 (and 4.3 beta) systems, the
* OOB indication and data handling in the kernel
* is such that if two separate TCP Urgent requests
* come in, one byte of TCP data will be overlaid.
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)) {
ncc = read(net, netibuf, sizeof (netibuf));
- if (clocks.didnetreceive < clocks.gotDM) {
+ if (sequenceIs(didnetreceive, gotDM)) {
SYNCHing = stilloob(net);
}
}
ncc = read(net, netibuf, sizeof (netibuf));
}
settimer(didnetreceive);
-#else /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */
+#else /* !defined(SO_OOBINLINE)) */
ncc = read(net, netibuf, sizeof (netibuf));
-#endif /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */
+#endif /* !defined(SO_OOBINLINE)) */
if (ncc < 0 && errno == EWOULDBLOCK)
ncc = 0;
else {
}
netip = netibuf;
}
+ DIAG((TD_REPORT | TD_NETDATA),
+ {sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
+ nfrontp += strlen(nfrontp);});
+ DIAG(TD_NETDATA, printdata("nd", netip, ncc));
}
/*
* Something to read from the pty...
*/
if (FD_ISSET(p, &ibits)) {
+#ifndef STREAMSPTY
pcc = read(p, ptyibuf, BUFSIZ);
- if (pcc < 0 && errno == EWOULDBLOCK)
+#else
+ pcc = readstream(p, ptyibuf, BUFSIZ);
+#endif
+ /*
+ * On some systems, if we try to read something
+ * off the master side before the slave side is
+ * opened, we get EIO.
+ */
+ if (pcc < 0 && (errno == EWOULDBLOCK ||
+#ifdef EAGAIN
+ errno == EAGAIN ||
+#endif
+ errno == EIO)) {
pcc = 0;
- else {
+ } else {
if (pcc <= 0)
break;
- ptyip = ptyibuf;
+#if !defined(CRAY2) || !defined(UNICOS5)
+#ifdef LINEMODE
+ /*
+ * If ioctl from pty, pass it through net
+ */
+ if (ptyibuf[0] & TIOCPKT_IOCTL) {
+ copy_termbuf(ptyibuf+1, pcc-1);
+ localstat();
+ pcc = 1;
+ }
+#endif /* LINEMODE */
+ if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
+ netclear(); /* clear buffer back */
+#ifndef NO_URGENT
+ /*
+ * There are client telnets on some
+ * operating systems get screwed up
+ * royally if we send them urgent
+ * mode data.
+ */
+ *nfrontp++ = IAC;
+ *nfrontp++ = DM;
+ neturg = nfrontp-1; /* off by one XXX */
+#endif
+ }
+ if (his_state_is_will(TELOPT_LFLOW) &&
+ (ptyibuf[0] &
+ (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
+ int newflow =
+ ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
+ if (newflow != flowmode) {
+ flowmode = newflow;
+ (void) sprintf(nfrontp,
+ "%c%c%c%c%c%c",
+ IAC, SB, TELOPT_LFLOW,
+ flowmode ? LFLOW_ON
+ : LFLOW_OFF,
+ IAC, SE);
+ nfrontp += 6;
+ }
+ }
+ pcc--;
+ ptyip = ptyibuf+1;
+#else /* defined(CRAY2) && defined(UNICOS5) */
+ if (!uselinemode) {
+ unpcc = pcc;
+ unptyip = ptyibuf;
+ pcc = term_output(&unptyip, ptyibuf2,
+ &unpcc, BUFSIZ);
+ ptyip = ptyibuf2;
+ } else
+ ptyip = ptyibuf;
+#endif /* defined(CRAY2) && defined(UNICOS5) */
}
}
c = *ptyip++ & 0377, pcc--;
if (c == IAC)
*nfrontp++ = c;
+#if defined(CRAY2) && defined(UNICOS5)
+ else if (c == '\n' &&
+ my_state_is_wont(TELOPT_BINARY) && newmap)
+ *nfrontp++ = '\r';
+#endif /* defined(CRAY2) && defined(UNICOS5) */
*nfrontp++ = c;
- if (c == '\r') {
+ if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
*nfrontp++ = *ptyip++ & 0377;
pcc--;
*nfrontp++ = '\0';
}
}
+#if defined(CRAY2) && defined(UNICOS5)
+ /*
+ * If chars were left over from the terminal driver,
+ * note their existence.
+ */
+ if (!uselinemode && unpcc) {
+ pcc = unpcc;
+ unpcc = 0;
+ ptyip = unptyip;
+ }
+#endif /* defined(CRAY2) && defined(UNICOS5) */
+
if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
netflush();
if (ncc > 0)
if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
ptyflush();
}
- cleanup();
-}
+ cleanup(0);
+} /* 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_BEGINNEG 3 /* throw away begin's... */
-#define TS_ENDNEG 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;
- if ((c == 0) || (c == '\n')) {
- break;
- }
- /* FALL THROUGH */
-
- case TS_DATA:
- if (c == IAC) {
- state = TS_IAC;
- break;
- }
- if (inter > 0)
- break;
- /*
- * We 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 (!myopts[TELOPT_BINARY] && c == '\r') {
- if ((ncc > 0) && ('\n' == *netip)) {
- netip++; ncc--;
- c = '\n';
- } else {
- state = TS_CR;
- }
- }
- *pfrontp++ = c;
- break;
+#ifndef TCSIG
+# ifdef TIOCSIG
+# define TCSIG TIOCSIG
+# endif
+#endif
- case TS_IAC:
- switch (c) {
+#ifdef STREAMSPTY
- /*
- * Send the process on the pty side an
- * interrupt. Do this with a NULL or
- * interrupt char; depending on the tty mode.
- */
- case BREAK:
- case IP:
- interrupt();
- 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;
- }
- *nfrontp++ = IAC;
- *nfrontp++ = DM;
- neturg = nfrontp;
- 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;
+int flowison = -1; /* current state of flow: -1 is unknown */
+int readstream(p, ibuf, bufsize)
+ int p;
+ char *ibuf;
+ int bufsize;
+{
+ int flags = 0;
+ int ret = 0;
+ struct termios *tsp;
+ struct termio *tp;
+ struct iocblk *ip;
+ char vstop, vstart;
+ int ixon;
+ int newflow;
+
+ strbufc.maxlen = BUFSIZ;
+ strbufc.buf = ctlbuf;
+ strbufd.maxlen = bufsize-1;
+ strbufd.len = 0;
+ strbufd.buf = ibuf+1;
+ ibuf[0] = 0;
+
+ ret = getmsg(p, &strbufc, &strbufd, &flags);
+ if (ret < 0) /* error of some sort -- probably EAGAIN */
+ return(-1);
+
+ if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
+ /* data message */
+ if (strbufd.len > 0) { /* real data */
+ return(strbufd.len + 1); /* count header char */
+ } else {
+ /* nothing there */
+ errno = EAGAIN;
+ return(-1);
+ }
+ }
- /*
- * Begin option subnegotiation...
- */
- case SB:
- state = TS_BEGINNEG;
- continue;
-
- case WILL:
- case WONT:
- case DO:
- case DONT:
- state = TS_WILL + (c - WILL);
- continue;
-
- case IAC:
- *pfrontp++ = c;
- break;
- }
- state = TS_DATA;
- break;
+ /*
+ * It's a control message. Return 1, to look at the flag we set
+ */
- case TS_BEGINNEG:
- if (c == IAC)
- state = TS_ENDNEG;
+ switch (ctlbuf[0]) {
+ case M_FLUSH:
+ if (ibuf[1] & FLUSHW)
+ ibuf[0] = TIOCPKT_FLUSHWRITE;
+ return(1);
+
+ case M_IOCTL:
+ ip = (struct iocblk *) (ibuf+1);
+
+ switch (ip->ioc_cmd) {
+ case TCSETS:
+ case TCSETSW:
+ case TCSETSF:
+ tsp = (struct termios *)
+ (ibuf+1 + sizeof(struct iocblk));
+ vstop = tsp->c_cc[VSTOP];
+ vstart = tsp->c_cc[VSTART];
+ ixon = tsp->c_iflag & IXON;
break;
-
- case TS_ENDNEG:
- state = c == SE ? TS_DATA : TS_BEGINNEG;
+ case TCSETA:
+ case TCSETAW:
+ case TCSETAF:
+ tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk));
+ vstop = tp->c_cc[VSTOP];
+ vstart = tp->c_cc[VSTART];
+ ixon = tp->c_iflag & IXON;
break;
-
- case TS_WILL:
- if (!hisopts[c])
- willoption(c);
- state = TS_DATA;
- continue;
-
- case TS_WONT:
- if (hisopts[c])
- wontoption(c);
- state = TS_DATA;
- continue;
-
- case TS_DO:
- if (!myopts[c])
- dooption(c);
- state = TS_DATA;
- continue;
-
- case TS_DONT:
- if (myopts[c]) {
- myopts[c] = 0;
- sprintf(nfrontp, wont, c);
- nfrontp += sizeof (wont) - 2;
- }
- state = TS_DATA;
- continue;
-
default:
- printf("telnetd: panic state=%d\n", state);
- exit(1);
+ errno = EAGAIN;
+ return(-1);
}
- }
-}
-
-willoption(option)
- int option;
-{
- char *fmt;
-
- switch (option) {
-
- case TELOPT_BINARY:
- mode(RAW, 0);
- goto common;
-
- case TELOPT_ECHO:
- mode(0, ECHO|CRMOD);
- /*FALL THRU*/
-
- case TELOPT_SGA:
- common:
- hisopts[option] = 1;
- fmt = doopt;
- break;
-
- case TELOPT_TM:
- fmt = dont;
- break;
-
- default:
- fmt = dont;
- break;
- }
- sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (dont) - 2;
-}
-
-wontoption(option)
- int option;
-{
- char *fmt;
-
- switch (option) {
-
- case TELOPT_ECHO:
- mode(ECHO|CRMOD, 0);
- goto common;
-
- case TELOPT_BINARY:
- mode(0, RAW);
- /*FALL THRU*/
-
- case TELOPT_SGA:
- common:
- hisopts[option] = 0;
- fmt = dont;
- break;
-
- default:
- fmt = dont;
- }
- 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);
- goto common;
-
- case TELOPT_BINARY:
- mode(RAW, 0);
- /*FALL THRU*/
-
- case TELOPT_SGA:
- common:
- fmt = will;
- break;
-
- default:
- fmt = wont;
- break;
+ newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
+ if (newflow != flowison) { /* it's a change */
+ flowison = newflow;
+ ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
+ return(1);
+ }
}
- sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (doopt) - 2;
-}
-
-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);
+ /* nothing worth doing anything about */
+ errno = EAGAIN;
+ return(-1);
}
+#endif /* STREAMSPTY */
/*
* Send interrupt to process on other side of pty.
* If it is in raw mode, just write NULL;
* otherwise, write intr char.
*/
+ void
interrupt()
{
- struct sgttyb b;
- struct tchars tchars;
-
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 ?
+ (unsigned char)*slctab[SLC_IP].sptr : '\177';
+#endif /* TCSIG */
}
-ptyflush()
+/*
+ * Send quit to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write quit char.
+ */
+ void
+sendbrk()
{
- int n;
-
- if ((n = pfrontp - pbackp) > 0)
- n = write(pty, pbackp, n);
- if (n < 0)
- return;
- pbackp += n;
- if (pbackp == pfrontp)
- pbackp = pfrontp = ptyobuf;
+ ptyflush(); /* half-hearted */
+#ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
+#else /* TCSIG */
+ init_termbuf();
+ *pfrontp++ = slctab[SLC_ABORT].sptr ?
+ (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
+#endif /* TCSIG */
}
-#if 0
-netflush()
+ void
+sendsusp()
{
- int n;
-
- if ((n = nfrontp - nbackp) > 0)
- n = write(net, nbackp, n);
- if (n < 0) {
- if (errno == EWOULDBLOCK)
- return;
- /* should blow this guy away... */
- return;
- }
- nbackp += n;
- if (nbackp == nfrontp)
- nbackp = nfrontp = netobuf;
+#ifdef SIGTSTP
+ ptyflush(); /* half-hearted */
+# ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
+# else /* TCSIG */
+ *pfrontp++ = slctab[SLC_SUSP].sptr ?
+ (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
+# endif /* TCSIG */
+#endif /* SIGTSTP */
}
-#else /* 0 */
-
/*
- * netflush
- * Send as much data as possible to the network,
- * handling requests for urgent data.
+ * When we get an AYT, if ^T is enabled, use that. Otherwise,
+ * just send back "[Yes]".
*/
-
-
-netflush()
+ void
+recv_ayt()
{
- int n;
-
- if ((n = nfrontp - nbackp) > 0) {
- if (!neturg) {
- 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 defined(SIGINFO) && defined(TCSIG)
+ if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
+ (void) ioctl(pty, TCSIG, (char *)SIGINFO);
+ return;
}
- }
- 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;
- }
+#endif
+ (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
+ nfrontp += 9;
}
-#endif /* 0 */
-cleanup()
+ void
+doeof()
{
+ init_termbuf();
- rmut();
- vhangup(); /* XXX */
- shutdown(net, 2);
- exit(1);
-}
-
-#include <utmp.h>
-
-struct utmp wtmp;
-char wtmpf[] = "/usr/adm/wtmp";
-char utmpf[] = "/etc/utmp";
-#define SCPYN(a, b) strncpy(a, b, sizeof(a))
-#define SCMPN(a, b) strncmp(a, b, sizeof(a))
-
-rmut()
-{
- register f;
- int found = 0;
- struct utmp *u, *utmp;
- int nutmp;
- struct stat statbf;
-
- f = open(utmpf, O_RDWR);
- if (f >= 0) {
- fstat(f, &statbf);
- utmp = (struct utmp *)malloc(statbf.st_size);
- if (!utmp)
- syslog(LOG_ERR, "utmp malloc failed");
- if (statbf.st_size && utmp) {
- nutmp = read(f, utmp, 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;
- lseek(f, ((long)u)-((long)utmp), L_SET);
- SCPYN(u->ut_name, "");
- SCPYN(u->ut_host, "");
- time(&u->ut_time);
- write(f, (char *)u, sizeof(wtmp));
- found++;
- }
- }
- 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, "");
- time(&wtmp.ut_time);
- write(f, (char *)&wtmp, sizeof(wtmp));
- close(f);
- }
+#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
+ if (!tty_isediting()) {
+ extern char oldeofc;
+ *pfrontp++ = oldeofc;
+ return;
}
- chmod(line, 0666);
- chown(line, 0, 0);
- line[strlen("/dev/")] = 'p';
- chmod(line, 0666);
- chown(line, 0, 0);
+#endif
+ *pfrontp++ = slctab[SLC_EOF].sptr ?
+ (unsigned char)*slctab[SLC_EOF].sptr : '\004';
}