5c099b14c1
pull in just about all of the differences from the crypto-us telnet suite (which includes Kerberos 4 and connection encryption support). Also bring in the Kerberos 5 support from the Heimdal telnet, and frob a little so that it can work with the non-Heimdal telnet suite. There is still some work left to do, specifically: - Add Heimdal's ticket forwarding support to the Berkeley Kerberos 4 module. - Add connection encryption support to the Heimdal Kerberos 5 module. Hints on this can be taken from the MIT Kerberos 5 module which still exists in crypto-us. However, even with the shortcomings listed above, this is a better situation than using the stock Heimdal telnet suite, which does not understand the IPSec policy stuff, and is also based on much older code which contains bugs that we have already fixed in the NetBSD sources.
1726 lines
41 KiB
C
1726 lines
41 KiB
C
/* $NetBSD: telnetd.c,v 1.21 2000/06/22 06:47:49 thorpej Exp $ */
|
|
|
|
/*
|
|
* Copyright (C) 1997 and 1998 WIDE Project.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the project nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1989, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n");
|
|
#if 0
|
|
static char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95";
|
|
#else
|
|
__RCSID("$NetBSD: telnetd.c,v 1.21 2000/06/22 06:47:49 thorpej Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
#include "telnetd.h"
|
|
#include "pathnames.h"
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <err.h>
|
|
#include <termcap.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#define P __P
|
|
|
|
#if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)
|
|
/*
|
|
* 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.
|
|
*/
|
|
# 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(KRB5)
|
|
#define Authenticator k5_Authenticator
|
|
#include <krb5.h>
|
|
#undef Authenticator
|
|
#include <com_err.h>
|
|
#endif
|
|
|
|
#if defined(AUTHENTICATION)
|
|
#include <libtelnet/auth.h>
|
|
int auth_level = 0;
|
|
#endif
|
|
|
|
#if defined(ENCRYPTION)
|
|
#include <libtelnet/encrypt.h>
|
|
#endif
|
|
|
|
#if defined(AUTHENTICATION) || defined(ENCRYPTION)
|
|
#include <libtelnet/misc.h>
|
|
#endif
|
|
|
|
#if defined(SECURELOGIN)
|
|
int require_secure_login = 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>
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
long ptyibufbuf[BUFSIZ/sizeof(long)+1];
|
|
char *ptyibuf = ((char *)&ptyibufbuf[1])-1;
|
|
char *ptyip = ((char *)&ptyibufbuf[1])-1;
|
|
char ptyibuf2[BUFSIZ*4];
|
|
unsigned char ctlbuf[BUFSIZ];
|
|
struct strbuf strbufc, strbufd;
|
|
|
|
int readstream();
|
|
|
|
#else /* ! STREAMPTY */
|
|
|
|
/*
|
|
* I/O data buffers,
|
|
* pointers, and counters.
|
|
*/
|
|
char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
|
|
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 *gettyname = "default";
|
|
char *progname;
|
|
|
|
extern void usage P((void));
|
|
int main __P((int, char *[]));
|
|
void usage __P((void));
|
|
int getterminaltype __P((char *));
|
|
int getent __P((char *, char *));
|
|
void doit __P((struct sockaddr *));
|
|
void _gettermname __P((void));
|
|
int terminaltypeok __P((char *));
|
|
char *getstr __P((char *, char **));
|
|
|
|
/*
|
|
* The string to pass to getopt(). We do it this way so
|
|
* that only the actual options that we support will be
|
|
* passed off to getopt().
|
|
*/
|
|
char valid_opts[] = {
|
|
'd', ':', 'g', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',
|
|
'4', '6',
|
|
#ifdef AUTHENTICATION
|
|
'a', ':', 'X', ':',
|
|
#endif
|
|
#ifdef BFTPDAEMON
|
|
'B',
|
|
#endif
|
|
#ifdef ENCRYPTION
|
|
'e', ':',
|
|
#endif
|
|
#ifdef DIAGNOSTICS
|
|
'D', ':',
|
|
#endif
|
|
#if defined(CRAY) && defined(NEWINIT)
|
|
'I', ':',
|
|
#endif
|
|
#ifdef LINEMODE
|
|
'l',
|
|
#endif
|
|
#ifdef CRAY
|
|
'r', ':',
|
|
#endif
|
|
#ifdef SECURELOGIN
|
|
's',
|
|
#endif
|
|
#ifdef KRB5
|
|
'R', ':', 'H',
|
|
#endif
|
|
'\0'
|
|
};
|
|
|
|
int family = AF_INET;
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
struct sockaddr_storage from;
|
|
int on = 1, fromlen;
|
|
register int ch;
|
|
#if defined(IPPROTO_IP) && defined(IP_TOS)
|
|
int tos = -1;
|
|
#endif
|
|
|
|
pfrontp = pbackp = ptyobuf;
|
|
netip = netibuf;
|
|
nfrontp = nbackp = netobuf;
|
|
#ifdef ENCRYPTION
|
|
nclearto = 0;
|
|
#endif /* ENCRYPTION */
|
|
|
|
progname = *argv;
|
|
|
|
#ifdef CRAY
|
|
/*
|
|
* Get number of pty's before trying to process options,
|
|
* which may include changing pty range.
|
|
*/
|
|
highpty = getnpty();
|
|
#endif /* CRAY */
|
|
|
|
while ((ch = getopt(argc, argv, valid_opts)) != -1) {
|
|
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 'g':
|
|
gettyname = optarg;
|
|
break;
|
|
|
|
case 'h':
|
|
hostinfo = 0;
|
|
break;
|
|
|
|
#ifdef KRB5
|
|
case 'H':
|
|
{
|
|
extern int require_hwpreauth;
|
|
require_hwpreauth = 1;
|
|
break;
|
|
}
|
|
#endif /* KRB5 */
|
|
|
|
#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 KRB5
|
|
case 'R':
|
|
{
|
|
extern krb5_context telnet_context;
|
|
krb5_error_code retval;
|
|
|
|
if (telnet_context == 0) {
|
|
retval = krb5_init_context(&telnet_context);
|
|
if (retval) {
|
|
com_err("telnetd", retval,
|
|
"while initializing krb5");
|
|
exit(1);
|
|
}
|
|
}
|
|
krb5_set_default_realm(telnet_context, optarg);
|
|
break;
|
|
}
|
|
#endif /* KRB5 */
|
|
|
|
#ifdef SECURELOGIN
|
|
case 's':
|
|
/* Secure login required */
|
|
require_secure_login = 1;
|
|
break;
|
|
#endif /* SECURELOGIN */
|
|
case 'S':
|
|
#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 */
|
|
|
|
case '4':
|
|
family = AF_INET;
|
|
break;
|
|
|
|
case '6':
|
|
family = AF_INET6;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "telnetd: %c: unknown option\n", ch);
|
|
/* FALLTHROUGH */
|
|
case '?':
|
|
usage();
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (debug) {
|
|
int s, ns, foo, error;
|
|
char *service = "telnet";
|
|
struct addrinfo hints, *res;
|
|
|
|
if (argc > 1) {
|
|
usage();
|
|
/* NOT REACHED */
|
|
} else if (argc == 1)
|
|
service = *argv;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_flags = AI_PASSIVE;
|
|
hints.ai_family = family;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = 0;
|
|
error = getaddrinfo(NULL, service, &hints, &res);
|
|
|
|
if (error) {
|
|
errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
|
|
usage();
|
|
}
|
|
|
|
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (s < 0) {
|
|
perror("telnetd: socket");;
|
|
exit(1);
|
|
}
|
|
(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
|
|
(char *)&on, sizeof(on));
|
|
if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
|
|
perror("bind");
|
|
exit(1);
|
|
}
|
|
if (listen(s, 1) < 0) {
|
|
perror("listen");
|
|
exit(1);
|
|
}
|
|
foo = res->ai_addrlen;
|
|
ns = accept(s, res->ai_addr, &foo);
|
|
if (ns < 0) {
|
|
perror("accept");
|
|
exit(1);
|
|
}
|
|
(void) dup2(ns, 0);
|
|
(void) close(ns);
|
|
(void) close(s);
|
|
#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 */
|
|
|
|
memset((char *)&dv, 0, 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 /* _SC_CRAY_SECURE_SYS */
|
|
|
|
openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
|
|
fromlen = sizeof (from);
|
|
if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
|
|
fprintf(stderr, "%s: ", progname);
|
|
perror("getpeername");
|
|
_exit(1);
|
|
}
|
|
if (keepalive &&
|
|
setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
|
|
(char *)&on, sizeof (on)) < 0) {
|
|
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
|
|
}
|
|
|
|
#if defined(IPPROTO_IP) && defined(IP_TOS)
|
|
if (((struct sockaddr *)&from)->sa_family == AF_INET) {
|
|
# if 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((struct sockaddr *)&from);
|
|
/* NOTREACHED */
|
|
#ifdef __GNUC__
|
|
exit(0);
|
|
#endif
|
|
} /* 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 SECURELOGIN
|
|
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 unsigned char ttytype_sbbuf[] = {
|
|
IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
|
|
};
|
|
|
|
int
|
|
getterminaltype(name)
|
|
char *name;
|
|
{
|
|
int retval = -1;
|
|
|
|
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_NEW_ENVIRON, 1);
|
|
send_do(TELOPT_OLD_ENVIRON, 1);
|
|
while (
|
|
#ifdef ENCRYPTION
|
|
his_do_dont_is_changing(TELOPT_ENCRYPT) ||
|
|
#endif /* ENCRYPTION */
|
|
his_will_wont_is_changing(TELOPT_TTYPE) ||
|
|
his_will_wont_is_changing(TELOPT_TSPEED) ||
|
|
his_will_wont_is_changing(TELOPT_XDISPLOC) ||
|
|
his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
|
|
his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
|
|
ttloop();
|
|
}
|
|
if (his_state_is_will(TELOPT_TSPEED)) {
|
|
static unsigned char sb[] =
|
|
{ IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
|
|
|
|
memmove(nfrontp, sb, sizeof sb);
|
|
nfrontp += sizeof sb;
|
|
DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
|
|
}
|
|
#ifdef ENCRYPTION
|
|
/*
|
|
* Wait for the negotiation of what type of encryption we can
|
|
* send with. If autoencrypt is not set, this will just return.
|
|
*/
|
|
if (his_state_is_will(TELOPT_ENCRYPT)) {
|
|
encrypt_wait();
|
|
}
|
|
#endif /* ENCRYPTION */
|
|
if (his_state_is_will(TELOPT_XDISPLOC)) {
|
|
static unsigned char sb[] =
|
|
{ IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
|
|
|
|
memmove(nfrontp, sb, sizeof sb);
|
|
nfrontp += sizeof sb;
|
|
DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
|
|
}
|
|
if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
|
|
static unsigned char sb[] =
|
|
{ IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
|
|
|
|
memmove(nfrontp, sb, sizeof sb);
|
|
nfrontp += sizeof sb;
|
|
DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
|
|
}
|
|
else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
|
|
static unsigned char sb[] =
|
|
{ IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
|
|
|
|
memmove(nfrontp, sb, sizeof sb);
|
|
nfrontp += sizeof sb;
|
|
DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
|
|
}
|
|
if (his_state_is_will(TELOPT_TTYPE)) {
|
|
|
|
memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);
|
|
nfrontp += sizeof ttytype_sbbuf;
|
|
DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
|
|
sizeof ttytype_sbbuf - 2););
|
|
}
|
|
if (his_state_is_will(TELOPT_TSPEED)) {
|
|
while (sequenceIs(tspeedsubopt, baseline))
|
|
ttloop();
|
|
}
|
|
if (his_state_is_will(TELOPT_XDISPLOC)) {
|
|
while (sequenceIs(xdisplocsubopt, baseline))
|
|
ttloop();
|
|
}
|
|
if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
|
|
while (sequenceIs(environsubopt, baseline))
|
|
ttloop();
|
|
}
|
|
if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
|
|
while (sequenceIs(oenvironsubopt, baseline))
|
|
ttloop();
|
|
}
|
|
if (his_state_is_will(TELOPT_TTYPE)) {
|
|
char first[256], last[256];
|
|
|
|
while (sequenceIs(ttypesubopt, baseline))
|
|
ttloop();
|
|
|
|
/*
|
|
* If the other side has already disabled the option, then
|
|
* we have to just go with what we (might) have already gotten.
|
|
*/
|
|
if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
|
|
(void) strncpy(first, terminaltype, sizeof(first) - 1);
|
|
first[sizeof(first) - 1] = '\0';
|
|
for(;;) {
|
|
/*
|
|
* Save the unknown name, and request the next name.
|
|
*/
|
|
(void) strncpy(last, terminaltype, sizeof(last) - 1);
|
|
last[sizeof(last) - 1] = '\0';
|
|
_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) - 1);
|
|
terminaltype[sizeof(terminaltype) - 1] = '\0';
|
|
}
|
|
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);
|
|
memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);
|
|
nfrontp += sizeof ttytype_sbbuf;
|
|
DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
|
|
sizeof ttytype_sbbuf - 2););
|
|
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);
|
|
}
|
|
|
|
#ifndef MAXHOSTNAMELEN
|
|
#define MAXHOSTNAMELEN 64
|
|
#endif /* MAXHOSTNAMELEN */
|
|
|
|
char *hostname;
|
|
char host_name[MAXHOSTNAMELEN + 1];
|
|
char remote_host_name[MAXHOSTNAMELEN + 1];
|
|
|
|
#ifndef convex
|
|
extern void telnet P((int, int));
|
|
#else
|
|
extern void telnet P((int, int, char *));
|
|
#endif
|
|
|
|
/*
|
|
* Get a pty, scan input lines.
|
|
*/
|
|
void
|
|
doit(who)
|
|
struct sockaddr *who;
|
|
{
|
|
char *host;
|
|
int error;
|
|
int level;
|
|
int ptynum;
|
|
char user_name[256];
|
|
|
|
/*
|
|
* 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;
|
|
|
|
if ((lp = getpty()) == NULL)
|
|
fatal(net, "Out of ptys");
|
|
|
|
if ((pty = open(lp, 2)) >= 0) {
|
|
strcpy(line,lp);
|
|
line[5] = 't';
|
|
break;
|
|
}
|
|
}
|
|
#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");
|
|
}
|
|
#endif /* _SC_CRAY_SECURE_SYS */
|
|
|
|
/* get name of connected client */
|
|
error = getnameinfo(who, who->sa_len, remote_host_name,
|
|
sizeof(remote_host_name), NULL, 0, 0);
|
|
|
|
if (error) {
|
|
fatal(net, "Couldn't resolve your address into a host name.\r\n\
|
|
Please contact your net administrator");
|
|
#ifdef __GNUC__
|
|
host = NULL; /* XXX gcc */
|
|
#endif
|
|
}
|
|
|
|
remote_host_name[sizeof(remote_host_name)-1] = 0;
|
|
host = remote_host_name;
|
|
|
|
(void)gethostname(host_name, sizeof (host_name));
|
|
host_name[sizeof(host_name) - 1] = '\0';
|
|
hostname = host_name;
|
|
|
|
#if defined(AUTHENTICATION) || defined(ENCRYPTION)
|
|
auth_encrypt_init(hostname, host, "TELNETD", 1);
|
|
#endif
|
|
|
|
init_env();
|
|
/*
|
|
* get terminal type.
|
|
*/
|
|
*user_name = 0;
|
|
level = getterminaltype(user_name);
|
|
setenv("TERM", terminaltype ? terminaltype : "network", 1);
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
#if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50)
|
|
int
|
|
Xterm_output(ibufp, obuf, icountp, ocount)
|
|
char **ibufp, *obuf;
|
|
int *icountp, ocount;
|
|
{
|
|
int ret;
|
|
ret = term_output(*ibufp, obuf, *icountp, ocount);
|
|
*ibufp += *icountp;
|
|
*icountp = 0;
|
|
return(ret);
|
|
}
|
|
#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;
|
|
#define TABBUFSIZ 512
|
|
char defent[TABBUFSIZ];
|
|
char defstrs[TABBUFSIZ];
|
|
#undef TABBUFSIZ
|
|
char *HE, *HN, *IM, *IF, *ptyibuf2ptr;
|
|
int nfd;
|
|
|
|
/*
|
|
* Initialize the slc mapping table.
|
|
*/
|
|
get_slc_defaults();
|
|
|
|
/*
|
|
* Do some tests where it is desireable to wait for a response.
|
|
* Rather than doing them slowly, one at a time, do them all
|
|
* at once.
|
|
*/
|
|
if (my_state_is_wont(TELOPT_SGA))
|
|
send_will(TELOPT_SGA, 1);
|
|
/*
|
|
* Is the client side a 4.2 (NOT 4.3) system? We need to know this
|
|
* because 4.2 clients are unable to deal with TCP urgent data.
|
|
*
|
|
* To find out, we send out a "DO ECHO". If the remote system
|
|
* answers "WILL ECHO" it is probably a 4.2 client, and we note
|
|
* that fact ("WILL ECHO" ==> that the client will echo what
|
|
* WE, the server, sends it; it does NOT mean that the client will
|
|
* echo the terminal input).
|
|
*/
|
|
send_do(TELOPT_ECHO, 1);
|
|
|
|
#ifdef LINEMODE
|
|
if (his_state_is_wont(TELOPT_LINEMODE)) {
|
|
/* Query the peer for linemode support by trying to negotiate
|
|
* the linemode option.
|
|
*/
|
|
linemode = 0;
|
|
editmode = 0;
|
|
send_do(TELOPT_LINEMODE, 1); /* send do linemode */
|
|
}
|
|
#endif /* LINEMODE */
|
|
|
|
/*
|
|
* Send along a couple of other options that we wish to negotiate.
|
|
*/
|
|
send_do(TELOPT_NAWS, 1);
|
|
send_will(TELOPT_STATUS, 1);
|
|
flowmode = 1; /* default flow control state */
|
|
restartany = -1; /* uninitialized... */
|
|
send_do(TELOPT_LFLOW, 1);
|
|
|
|
/*
|
|
* Spin, waiting for a response from the DO ECHO. However,
|
|
* some REALLY DUMB telnets out there might not respond
|
|
* to the DO ECHO. So, we spin looking for NAWS, (most dumb
|
|
* telnets so far seem to respond with WONT for a DO that
|
|
* they don't understand...) because by the time we get the
|
|
* response, it will already have processed the DO ECHO.
|
|
* Kludge upon kludge.
|
|
*/
|
|
while (his_will_wont_is_changing(TELOPT_NAWS))
|
|
ttloop();
|
|
|
|
/*
|
|
* But...
|
|
* The client might have sent a WILL NAWS as part of its
|
|
* startup code; if so, we'll be here before we get the
|
|
* response to the DO ECHO. We'll make the assumption
|
|
* that any implementation that understands about NAWS
|
|
* is a modern enough implementation that it will respond
|
|
* to our DO ECHO request; hence we'll do another spin
|
|
* waiting for the ECHO option to settle down, which is
|
|
* what we wanted to do in the first place...
|
|
*/
|
|
if (his_want_state_is_will(TELOPT_ECHO) &&
|
|
his_state_is_will(TELOPT_NAWS)) {
|
|
while (his_will_wont_is_changing(TELOPT_ECHO))
|
|
ttloop();
|
|
}
|
|
/*
|
|
* On the off chance that the telnet client is broken and does not
|
|
* respond to the DO ECHO we sent, (after all, we did send the
|
|
* DO NAWS negotiation after the DO ECHO, and we won't get here
|
|
* until a response to the DO NAWS comes back) simulate the
|
|
* receipt of a will echo. This will also send a WONT ECHO
|
|
* to the client, since we assume that the client failed to
|
|
* respond because it believes that it is already in DO ECHO
|
|
* mode, which we do not want.
|
|
*/
|
|
if (his_want_state_is_will(TELOPT_ECHO)) {
|
|
DIAG(TD_OPTIONS,
|
|
{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, gettyname) == 1) {
|
|
char *cp=defstrs;
|
|
|
|
HE = getstr("he", &cp);
|
|
HN = getstr("hn", &cp);
|
|
IM = getstr("im", &cp);
|
|
IF = getstr("if", &cp);
|
|
if (HN && *HN)
|
|
(void) strcpy(host_name, HN);
|
|
if (IM == 0)
|
|
IM = "";
|
|
} else {
|
|
IM = DEFAULT_IM;
|
|
HE = 0;
|
|
}
|
|
edithost(HE, host_name);
|
|
ptyibuf2ptr = ptyibuf2;
|
|
if (hostinfo) {
|
|
if (IF) {
|
|
char buf[_POSIX2_LINE_MAX];
|
|
FILE *fd;
|
|
|
|
if ((fd = fopen(IF, "r")) != NULL) {
|
|
while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
|
|
ptyibuf2ptr = putf(buf, ptyibuf2ptr);
|
|
fclose(fd);
|
|
}
|
|
}
|
|
if (*IM)
|
|
ptyibuf2ptr = putf(IM, ptyibuf2ptr);
|
|
}
|
|
|
|
if (pcc)
|
|
strncpy(ptyibuf2ptr, ptyip, pcc+1);
|
|
ptyip = ptyibuf2;
|
|
pcc = strlen(ptyip);
|
|
#ifdef LINEMODE
|
|
/*
|
|
* Last check to make sure all our states are correct.
|
|
*/
|
|
init_termbuf();
|
|
localstat();
|
|
#endif /* LINEMODE */
|
|
|
|
DIAG(TD_REPORT,
|
|
{sprintf(nfrontp, "td: Entering processing loop\r\n");
|
|
nfrontp += strlen(nfrontp);});
|
|
|
|
#ifdef convex
|
|
startslave(host);
|
|
#endif
|
|
|
|
nfd = ((f > p) ? f : p) + 1;
|
|
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);
|
|
/*
|
|
* Never look for input if there's still
|
|
* stuff in the corresponding output buffer
|
|
*/
|
|
if (nfrontp - nbackp || pcc > 0) {
|
|
FD_SET(f, &obits);
|
|
} else {
|
|
FD_SET(p, &ibits);
|
|
}
|
|
if (pfrontp - pbackp || ncc > 0) {
|
|
FD_SET(p, &obits);
|
|
} else {
|
|
FD_SET(f, &ibits);
|
|
}
|
|
if (!SYNCHing) {
|
|
FD_SET(f, &xbits);
|
|
}
|
|
if ((c = select(nfd, &ibits, &obits, &xbits,
|
|
(struct timeval *)0)) < 1) {
|
|
if (c == -1) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
}
|
|
}
|
|
sleep(5);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Any urgent data?
|
|
*/
|
|
if (FD_ISSET(net, &xbits)) {
|
|
SYNCHing = 1;
|
|
}
|
|
|
|
/*
|
|
* Something to read from the network...
|
|
*/
|
|
if (FD_ISSET(net, &ibits)) {
|
|
#if !defined(SO_OOBINLINE)
|
|
/*
|
|
* 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.
|
|
* This is fatal for Telnet, but we try to live
|
|
* with it.
|
|
*
|
|
* In addition, in 4.2 (and...), a special protocol
|
|
* is needed to pick up the TCP Urgent data in
|
|
* the correct sequence.
|
|
*
|
|
* What we do is: if we think we are in urgent
|
|
* mode, we look to see if we are "at the mark".
|
|
* If we are, we do an OOB receive. If we run
|
|
* this twice, we will do the OOB receive twice,
|
|
* but the second will fail, since the second
|
|
* time we were "at the mark", but there wasn't
|
|
* any data there (the kernel doesn't reset
|
|
* "at the mark" until we do a normal read).
|
|
* Once we've read the OOB data, we go ahead
|
|
* and do normal reads.
|
|
*
|
|
* There is also another problem, which is that
|
|
* since the OOB byte we read doesn't put us
|
|
* out of OOB state, and since that byte is most
|
|
* likely the TELNET DM (data mark), we would
|
|
* stay in the TELNET SYNCH (SYNCHing) state.
|
|
* So, clocks to the rescue. If we've "just"
|
|
* received a DM, then we test for the
|
|
* presence of OOB data when the receive OOB
|
|
* fails (and AFTER we did the normal mode read
|
|
* to clear "at the mark").
|
|
*/
|
|
if (SYNCHing) {
|
|
int 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 (sequenceIs(didnetreceive, gotDM)) {
|
|
SYNCHing = stilloob(net);
|
|
}
|
|
}
|
|
} else {
|
|
ncc = read(net, netibuf, sizeof (netibuf));
|
|
}
|
|
} else {
|
|
ncc = read(net, netibuf, sizeof (netibuf));
|
|
}
|
|
settimer(didnetreceive);
|
|
#else /* !defined(SO_OOBINLINE)) */
|
|
ncc = read(net, netibuf, sizeof (netibuf));
|
|
#endif /* !defined(SO_OOBINLINE)) */
|
|
if (ncc < 0 && errno == EWOULDBLOCK)
|
|
ncc = 0;
|
|
else {
|
|
if (ncc <= 0) {
|
|
break;
|
|
}
|
|
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);
|
|
#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 {
|
|
if (pcc <= 0)
|
|
break;
|
|
#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 */
|
|
DIAG(TD_OPTIONS,
|
|
printoption("td: send IAC", DM));
|
|
|
|
#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;
|
|
DIAG(TD_OPTIONS, printsub('>',
|
|
(unsigned char *)nfrontp-4,
|
|
4););
|
|
}
|
|
}
|
|
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) */
|
|
}
|
|
}
|
|
|
|
while (pcc > 0) {
|
|
if ((&netobuf[BUFSIZ] - nfrontp) < 2)
|
|
break;
|
|
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') && (my_state_is_wont(TELOPT_BINARY))) {
|
|
if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
|
|
*nfrontp++ = *ptyip++ & 0377;
|
|
pcc--;
|
|
} else
|
|
*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)
|
|
telrcv();
|
|
if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
|
|
ptyflush();
|
|
}
|
|
cleanup(0);
|
|
} /* end of telnet */
|
|
|
|
#ifndef TCSIG
|
|
# ifdef TIOCSIG
|
|
# define TCSIG TIOCSIG
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef STREAMSPTY
|
|
|
|
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 = (char *)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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* It's a control message. Return 1, to look at the flag we set
|
|
*/
|
|
|
|
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 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;
|
|
default:
|
|
errno = EAGAIN;
|
|
return(-1);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* 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()
|
|
{
|
|
ptyflush(); /* half-hearted */
|
|
|
|
#if defined(STREAMSPTY) && defined(TIOCSIGNAL)
|
|
/* Streams PTY style ioctl to post a signal */
|
|
{
|
|
int sig = SIGINT;
|
|
(void) ioctl(pty, TIOCSIGNAL, &sig);
|
|
(void) ioctl(pty, I_FLUSH, FLUSHR);
|
|
}
|
|
#else
|
|
#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 */
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Send quit to process on other side of pty.
|
|
* If it is in raw mode, just write NULL;
|
|
* otherwise, write quit char.
|
|
*/
|
|
void
|
|
sendbrk()
|
|
{
|
|
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 */
|
|
}
|
|
|
|
void
|
|
sendsusp()
|
|
{
|
|
#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 */
|
|
}
|
|
|
|
/*
|
|
* When we get an AYT, if ^T is enabled, use that. Otherwise,
|
|
* just send back "[Yes]".
|
|
*/
|
|
void
|
|
recv_ayt()
|
|
{
|
|
#if defined(SIGINFO) && defined(TCSIG)
|
|
if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
|
|
(void) ioctl(pty, TCSIG, (char *)SIGINFO);
|
|
return;
|
|
}
|
|
#endif
|
|
(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
|
|
nfrontp += 9;
|
|
}
|
|
|
|
void
|
|
doeof()
|
|
{
|
|
init_termbuf();
|
|
|
|
#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
|
|
if (!tty_isediting()) {
|
|
extern char oldeofc;
|
|
*pfrontp++ = oldeofc;
|
|
return;
|
|
}
|
|
#endif
|
|
*pfrontp++ = slctab[SLC_EOF].sptr ?
|
|
(unsigned char)*slctab[SLC_EOF].sptr : '\004';
|
|
}
|