telnetd.c revision 57416
157416Smarkm/*
257416Smarkm * Copyright (c) 1989, 1993
357416Smarkm *	The Regents of the University of California.  All rights reserved.
457416Smarkm *
557416Smarkm * Redistribution and use in source and binary forms, with or without
657416Smarkm * modification, are permitted provided that the following conditions
757416Smarkm * are met:
857416Smarkm * 1. Redistributions of source code must retain the above copyright
957416Smarkm *    notice, this list of conditions and the following disclaimer.
1057416Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1157416Smarkm *    notice, this list of conditions and the following disclaimer in the
1257416Smarkm *    documentation and/or other materials provided with the distribution.
1357416Smarkm * 3. All advertising materials mentioning features or use of this software
1457416Smarkm *    must display the following acknowledgement:
1557416Smarkm *	This product includes software developed by the University of
1657416Smarkm *	California, Berkeley and its contributors.
1757416Smarkm * 4. Neither the name of the University nor the names of its contributors
1857416Smarkm *    may be used to endorse or promote products derived from this software
1957416Smarkm *    without specific prior written permission.
2057416Smarkm *
2157416Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2257416Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2357416Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2457416Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2557416Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2657416Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2757416Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2857416Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2957416Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3057416Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3157416Smarkm * SUCH DAMAGE.
3257416Smarkm */
3357416Smarkm
3457416Smarkm#include "telnetd.h"
3557416Smarkm
3657416SmarkmRCSID("$Id: telnetd.c,v 1.60 1999/12/05 10:59:52 assar Exp $");
3757416Smarkm
3857416Smarkm#ifdef _SC_CRAY_SECURE_SYS
3957416Smarkm#include <sys/sysv.h>
4057416Smarkm#include <sys/secdev.h>
4157416Smarkm#include <sys/secparm.h>
4257416Smarkm#include <sys/usrv.h>
4357416Smarkmint	secflag;
4457416Smarkmchar	tty_dev[16];
4557416Smarkmstruct	secdev dv;
4657416Smarkmstruct	sysv sysv;
4757416Smarkmstruct	socksec ss;
4857416Smarkm#endif	/* _SC_CRAY_SECURE_SYS */
4957416Smarkm
5057416Smarkm#ifdef AUTHENTICATION
5157416Smarkmint	auth_level = 0;
5257416Smarkm#endif
5357416Smarkm
5457416Smarkmextern	int utmp_len;
5557416Smarkmint	registerd_host_only = 0;
5657416Smarkm
5757416Smarkm#ifdef	STREAMSPTY
5857416Smarkm# include <stropts.h>
5957416Smarkm# include <termios.h>
6057416Smarkm#ifdef HAVE_SYS_UIO_H
6157416Smarkm#include <sys/uio.h>
6257416Smarkm#endif /* HAVE_SYS_UIO_H */
6357416Smarkm#ifdef HAVE_SYS_STREAM_H
6457416Smarkm#include <sys/stream.h>
6557416Smarkm#endif
6657416Smarkm#ifdef _AIX
6757416Smarkm#include <sys/termio.h>
6857416Smarkm#endif
6957416Smarkm# ifdef HAVE_SYS_STRTTY_H
7057416Smarkm# include <sys/strtty.h>
7157416Smarkm# endif
7257416Smarkm# ifdef HAVE_SYS_STR_TTY_H
7357416Smarkm# include <sys/str_tty.h>
7457416Smarkm# endif
7557416Smarkm/* make sure we don't get the bsd version */
7657416Smarkm/* what is this here for? solaris? /joda */
7757416Smarkm# ifdef HAVE_SYS_TTY_H
7857416Smarkm# include "/usr/include/sys/tty.h"
7957416Smarkm# endif
8057416Smarkm# ifdef HAVE_SYS_PTYVAR_H
8157416Smarkm# include <sys/ptyvar.h>
8257416Smarkm# endif
8357416Smarkm
8457416Smarkm/*
8557416Smarkm * Because of the way ptyibuf is used with streams messages, we need
8657416Smarkm * ptyibuf+1 to be on a full-word boundary.  The following wierdness
8757416Smarkm * is simply to make that happen.
8857416Smarkm */
8957416Smarkmlong	ptyibufbuf[BUFSIZ/sizeof(long)+1];
9057416Smarkmchar	*ptyibuf = ((char *)&ptyibufbuf[1])-1;
9157416Smarkmchar	*ptyip = ((char *)&ptyibufbuf[1])-1;
9257416Smarkmchar	ptyibuf2[BUFSIZ];
9357416Smarkmunsigned char ctlbuf[BUFSIZ];
9457416Smarkmstruct	strbuf strbufc, strbufd;
9557416Smarkm
9657416Smarkmint readstream(int, char*, int);
9757416Smarkm
9857416Smarkm#else	/* ! STREAMPTY */
9957416Smarkm
10057416Smarkm/*
10157416Smarkm * I/O data buffers,
10257416Smarkm * pointers, and counters.
10357416Smarkm */
10457416Smarkmchar	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
10557416Smarkmchar	ptyibuf2[BUFSIZ];
10657416Smarkm
10757416Smarkm#endif /* ! STREAMPTY */
10857416Smarkm
10957416Smarkmint	hostinfo = 1;			/* do we print login banner? */
11057416Smarkm
11157416Smarkm#ifdef	_CRAY
11257416Smarkmextern int      newmap; /* nonzero if \n maps to ^M^J */
11357416Smarkmint	lowpty = 0, highpty;	/* low, high pty numbers */
11457416Smarkm#endif /* CRAY */
11557416Smarkm
11657416Smarkmint debug = 0;
11757416Smarkmint keepalive = 1;
11857416Smarkmchar *progname;
11957416Smarkm
12057416Smarkmstatic void usage (void);
12157416Smarkm
12257416Smarkm/*
12357416Smarkm * The string to pass to getopt().  We do it this way so
12457416Smarkm * that only the actual options that we support will be
12557416Smarkm * passed off to getopt().
12657416Smarkm */
12757416Smarkmchar valid_opts[] = "Bd:hklnS:u:UL:y"
12857416Smarkm#ifdef AUTHENTICATION
12957416Smarkm		    "a:X:z"
13057416Smarkm#endif
13157416Smarkm#ifdef DIAGNOSTICS
13257416Smarkm		    "D:"
13357416Smarkm#endif
13457416Smarkm#ifdef _CRAY
13557416Smarkm		    "r:"
13657416Smarkm#endif
13757416Smarkm		    ;
13857416Smarkm
13957416Smarkmstatic void doit(struct sockaddr*, int);
14057416Smarkm
14157416Smarkmint
14257416Smarkmmain(int argc, char **argv)
14357416Smarkm{
14457416Smarkm    struct sockaddr_storage __ss;
14557416Smarkm    struct sockaddr *sa = (struct sockaddr *)&__ss;
14657416Smarkm    int on = 1, sa_size;
14757416Smarkm    int ch;
14857416Smarkm#if	defined(IPPROTO_IP) && defined(IP_TOS)
14957416Smarkm    int tos = -1;
15057416Smarkm#endif
15157416Smarkm#ifdef ENCRYPTION
15257416Smarkm    extern int des_check_key;
15357416Smarkm    des_check_key = 1;	/* Kludge for Mac NCSA telnet 2.6 /bg */
15457416Smarkm#endif
15557416Smarkm    pfrontp = pbackp = ptyobuf;
15657416Smarkm    netip = netibuf;
15757416Smarkm    nfrontp = nbackp = netobuf;
15857416Smarkm
15957416Smarkm    progname = *argv;
16057416Smarkm#ifdef ENCRYPTION
16157416Smarkm    nclearto = 0;
16257416Smarkm#endif
16357416Smarkm
16457416Smarkm#ifdef _CRAY
16557416Smarkm    /*
16657416Smarkm     * Get number of pty's before trying to process options,
16757416Smarkm     * which may include changing pty range.
16857416Smarkm     */
16957416Smarkm    highpty = getnpty();
17057416Smarkm#endif /* CRAY */
17157416Smarkm
17257416Smarkm    while ((ch = getopt(argc, argv, valid_opts)) != -1) {
17357416Smarkm	switch(ch) {
17457416Smarkm
17557416Smarkm#ifdef	AUTHENTICATION
17657416Smarkm	case 'a':
17757416Smarkm	    /*
17857416Smarkm	     * Check for required authentication level
17957416Smarkm	     */
18057416Smarkm	    if (strcmp(optarg, "debug") == 0) {
18157416Smarkm		auth_debug_mode = 1;
18257416Smarkm	    } else if (strcasecmp(optarg, "none") == 0) {
18357416Smarkm		auth_level = 0;
18457416Smarkm	    } else if (strcasecmp(optarg, "otp") == 0) {
18557416Smarkm		auth_level = 0;
18657416Smarkm		require_otp = 1;
18757416Smarkm	    } else if (strcasecmp(optarg, "other") == 0) {
18857416Smarkm		auth_level = AUTH_OTHER;
18957416Smarkm	    } else if (strcasecmp(optarg, "user") == 0) {
19057416Smarkm		auth_level = AUTH_USER;
19157416Smarkm	    } else if (strcasecmp(optarg, "valid") == 0) {
19257416Smarkm		auth_level = AUTH_VALID;
19357416Smarkm	    } else if (strcasecmp(optarg, "off") == 0) {
19457416Smarkm		/*
19557416Smarkm		 * This hack turns off authentication
19657416Smarkm		 */
19757416Smarkm		auth_level = -1;
19857416Smarkm	    } else {
19957416Smarkm		fprintf(stderr,
20057416Smarkm			"telnetd: unknown authorization level for -a\n");
20157416Smarkm	    }
20257416Smarkm	    break;
20357416Smarkm#endif	/* AUTHENTICATION */
20457416Smarkm
20557416Smarkm	case 'B': /* BFTP mode is not supported any more */
20657416Smarkm	    break;
20757416Smarkm	case 'd':
20857416Smarkm	    if (strcmp(optarg, "ebug") == 0) {
20957416Smarkm		debug++;
21057416Smarkm		break;
21157416Smarkm	    }
21257416Smarkm	    usage();
21357416Smarkm	    /* NOTREACHED */
21457416Smarkm	    break;
21557416Smarkm
21657416Smarkm#ifdef DIAGNOSTICS
21757416Smarkm	case 'D':
21857416Smarkm	    /*
21957416Smarkm	     * Check for desired diagnostics capabilities.
22057416Smarkm	     */
22157416Smarkm	    if (!strcmp(optarg, "report")) {
22257416Smarkm		diagnostic |= TD_REPORT|TD_OPTIONS;
22357416Smarkm	    } else if (!strcmp(optarg, "exercise")) {
22457416Smarkm		diagnostic |= TD_EXERCISE;
22557416Smarkm	    } else if (!strcmp(optarg, "netdata")) {
22657416Smarkm		diagnostic |= TD_NETDATA;
22757416Smarkm	    } else if (!strcmp(optarg, "ptydata")) {
22857416Smarkm		diagnostic |= TD_PTYDATA;
22957416Smarkm	    } else if (!strcmp(optarg, "options")) {
23057416Smarkm		diagnostic |= TD_OPTIONS;
23157416Smarkm	    } else {
23257416Smarkm		usage();
23357416Smarkm		/* NOT REACHED */
23457416Smarkm	    }
23557416Smarkm	    break;
23657416Smarkm#endif /* DIAGNOSTICS */
23757416Smarkm
23857416Smarkm
23957416Smarkm	case 'h':
24057416Smarkm	    hostinfo = 0;
24157416Smarkm	    break;
24257416Smarkm
24357416Smarkm	case 'k': /* Linemode is not supported any more */
24457416Smarkm	case 'l':
24557416Smarkm	    break;
24657416Smarkm
24757416Smarkm	case 'n':
24857416Smarkm	    keepalive = 0;
24957416Smarkm	    break;
25057416Smarkm
25157416Smarkm#ifdef _CRAY
25257416Smarkm	case 'r':
25357416Smarkm	    {
25457416Smarkm		char *strchr();
25557416Smarkm		char *c;
25657416Smarkm
25757416Smarkm		/*
25857416Smarkm		 * Allow the specification of alterations
25957416Smarkm		 * to the pty search range.  It is legal to
26057416Smarkm		 * specify only one, and not change the
26157416Smarkm		 * other from its default.
26257416Smarkm		 */
26357416Smarkm		c = strchr(optarg, '-');
26457416Smarkm		if (c) {
26557416Smarkm		    *c++ = '\0';
26657416Smarkm		    highpty = atoi(c);
26757416Smarkm		}
26857416Smarkm		if (*optarg != '\0')
26957416Smarkm		    lowpty = atoi(optarg);
27057416Smarkm		if ((lowpty > highpty) || (lowpty < 0) ||
27157416Smarkm		    (highpty > 32767)) {
27257416Smarkm		    usage();
27357416Smarkm		    /* NOT REACHED */
27457416Smarkm		}
27557416Smarkm		break;
27657416Smarkm	    }
27757416Smarkm#endif	/* CRAY */
27857416Smarkm
27957416Smarkm	case 'S':
28057416Smarkm#ifdef	HAVE_PARSETOS
28157416Smarkm	    if ((tos = parsetos(optarg, "tcp")) < 0)
28257416Smarkm		fprintf(stderr, "%s%s%s\n",
28357416Smarkm			"telnetd: Bad TOS argument '", optarg,
28457416Smarkm			"'; will try to use default TOS");
28557416Smarkm#else
28657416Smarkm	    fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
28757416Smarkm		    "-S flag not supported\n");
28857416Smarkm#endif
28957416Smarkm	    break;
29057416Smarkm
29157416Smarkm	case 'u':
29257416Smarkm	    utmp_len = atoi(optarg);
29357416Smarkm	    break;
29457416Smarkm
29557416Smarkm	case 'U':
29657416Smarkm	    registerd_host_only = 1;
29757416Smarkm	    break;
29857416Smarkm
29957416Smarkm#ifdef	AUTHENTICATION
30057416Smarkm	case 'X':
30157416Smarkm	    /*
30257416Smarkm	     * Check for invalid authentication types
30357416Smarkm	     */
30457416Smarkm	    auth_disable_name(optarg);
30557416Smarkm	    break;
30657416Smarkm#endif
30757416Smarkm	case 'y':
30857416Smarkm	    no_warn = 1;
30957416Smarkm	    break;
31057416Smarkm#ifdef AUTHENTICATION
31157416Smarkm	case 'z':
31257416Smarkm	    log_unauth = 1;
31357416Smarkm	    break;
31457416Smarkm
31557416Smarkm#endif	/* AUTHENTICATION */
31657416Smarkm
31757416Smarkm	case 'L':
31857416Smarkm	    new_login = optarg;
31957416Smarkm	    break;
32057416Smarkm
32157416Smarkm	default:
32257416Smarkm	    fprintf(stderr, "telnetd: %c: unknown option\n", ch);
32357416Smarkm	    /* FALLTHROUGH */
32457416Smarkm	case '?':
32557416Smarkm	    usage();
32657416Smarkm	    /* NOTREACHED */
32757416Smarkm	}
32857416Smarkm    }
32957416Smarkm
33057416Smarkm    argc -= optind;
33157416Smarkm    argv += optind;
33257416Smarkm
33357416Smarkm    if (debug) {
33457416Smarkm	int port = 0;
33557416Smarkm	struct servent *sp;
33657416Smarkm
33757416Smarkm	if (argc > 1) {
33857416Smarkm	    usage ();
33957416Smarkm	} else if (argc == 1) {
34057416Smarkm	    sp = roken_getservbyname (*argv, "tcp");
34157416Smarkm	    if (sp)
34257416Smarkm		port = sp->s_port;
34357416Smarkm	    else
34457416Smarkm		port = htons(atoi(*argv));
34557416Smarkm	} else {
34657416Smarkm#ifdef KRB5
34757416Smarkm	    port = krb5_getportbyname (NULL, "telnet", "tcp", 23);
34857416Smarkm#else
34957416Smarkm	    port = k_getportbyname("telnet", "tcp", htons(23));
35057416Smarkm#endif
35157416Smarkm	}
35257416Smarkm	mini_inetd (port);
35357416Smarkm    } else if (argc > 0) {
35457416Smarkm	usage();
35557416Smarkm	/* NOT REACHED */
35657416Smarkm    }
35757416Smarkm
35857416Smarkm#ifdef _SC_CRAY_SECURE_SYS
35957416Smarkm    secflag = sysconf(_SC_CRAY_SECURE_SYS);
36057416Smarkm
36157416Smarkm    /*
36257416Smarkm     *	Get socket's security label
36357416Smarkm     */
36457416Smarkm    if (secflag)  {
36557416Smarkm	int szss = sizeof(ss);
36657416Smarkm	int sock_multi;
36757416Smarkm	int szi = sizeof(int);
36857416Smarkm
36957416Smarkm	memset(&dv, 0, sizeof(dv));
37057416Smarkm
37157416Smarkm	if (getsysv(&sysv, sizeof(struct sysv)) != 0)
37257416Smarkm	    fatalperror(net, "getsysv");
37357416Smarkm
37457416Smarkm	/*
37557416Smarkm	 *	Get socket security label and set device values
37657416Smarkm	 *	   {security label to be set on ttyp device}
37757416Smarkm	 */
37857416Smarkm#ifdef SO_SEC_MULTI			/* 8.0 code */
37957416Smarkm	if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,
38057416Smarkm			(void *)&ss, &szss) < 0) ||
38157416Smarkm	    (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,
38257416Smarkm			(void *)&sock_multi, &szi) < 0))
38357416Smarkm	    fatalperror(net, "getsockopt");
38457416Smarkm	else {
38557416Smarkm	    dv.dv_actlvl = ss.ss_actlabel.lt_level;
38657416Smarkm	    dv.dv_actcmp = ss.ss_actlabel.lt_compart;
38757416Smarkm	    if (!sock_multi) {
38857416Smarkm		dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;
38957416Smarkm		dv.dv_valcmp = dv.dv_actcmp;
39057416Smarkm	    } else {
39157416Smarkm		dv.dv_minlvl = ss.ss_minlabel.lt_level;
39257416Smarkm		dv.dv_maxlvl = ss.ss_maxlabel.lt_level;
39357416Smarkm		dv.dv_valcmp = ss.ss_maxlabel.lt_compart;
39457416Smarkm	    }
39557416Smarkm	    dv.dv_devflg = 0;
39657416Smarkm	}
39757416Smarkm#else /* SO_SEC_MULTI */		/* 7.0 code */
39857416Smarkm	if (getsockopt(0, SOL_SOCKET, SO_SECURITY,
39957416Smarkm		       (void *)&ss, &szss) >= 0) {
40057416Smarkm	    dv.dv_actlvl = ss.ss_slevel;
40157416Smarkm	    dv.dv_actcmp = ss.ss_compart;
40257416Smarkm	    dv.dv_minlvl = ss.ss_minlvl;
40357416Smarkm	    dv.dv_maxlvl = ss.ss_maxlvl;
40457416Smarkm	    dv.dv_valcmp = ss.ss_maxcmp;
40557416Smarkm	}
40657416Smarkm#endif /* SO_SEC_MULTI */
40757416Smarkm    }
40857416Smarkm#endif	/* _SC_CRAY_SECURE_SYS */
40957416Smarkm
41057416Smarkm    roken_openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
41157416Smarkm    sa_size = sizeof (__ss);
41257416Smarkm    if (getpeername(STDIN_FILENO, sa, &sa_size) < 0) {
41357416Smarkm	fprintf(stderr, "%s: ", progname);
41457416Smarkm	perror("getpeername");
41557416Smarkm	_exit(1);
41657416Smarkm    }
41757416Smarkm    if (keepalive &&
41857416Smarkm	setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE,
41957416Smarkm		   (void *)&on, sizeof (on)) < 0) {
42057416Smarkm	syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
42157416Smarkm    }
42257416Smarkm
42357416Smarkm#if	defined(IPPROTO_IP) && defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
42457416Smarkm    {
42557416Smarkm# ifdef HAVE_GETTOSBYNAME
42657416Smarkm	struct tosent *tp;
42757416Smarkm	if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
42857416Smarkm	    tos = tp->t_tos;
42957416Smarkm# endif
43057416Smarkm	if (tos < 0)
43157416Smarkm	    tos = 020;	/* Low Delay bit */
43257416Smarkm	if (tos
43357416Smarkm	    && sa->sa_family == AF_INET
43457416Smarkm	    && (setsockopt(STDIN_FILENO, IPPROTO_IP, IP_TOS,
43557416Smarkm			   (void *)&tos, sizeof(tos)) < 0)
43657416Smarkm	    && (errno != ENOPROTOOPT) )
43757416Smarkm	    syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
43857416Smarkm    }
43957416Smarkm#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
44057416Smarkm    net = STDIN_FILENO;
44157416Smarkm    doit(sa, sa_size);
44257416Smarkm    /* NOTREACHED */
44357416Smarkm    return 0;
44457416Smarkm}  /* end of main */
44557416Smarkm
44657416Smarkmstatic void
44757416Smarkmusage(void)
44857416Smarkm{
44957416Smarkm    fprintf(stderr, "Usage: telnetd");
45057416Smarkm#ifdef	AUTHENTICATION
45157416Smarkm    fprintf(stderr, " [-a (debug|other|otp|user|valid|off|none)]\n\t");
45257416Smarkm#endif
45357416Smarkm    fprintf(stderr, " [-debug]");
45457416Smarkm#ifdef DIAGNOSTICS
45557416Smarkm    fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
45657416Smarkm#endif
45757416Smarkm#ifdef	AUTHENTICATION
45857416Smarkm    fprintf(stderr, " [-edebug]");
45957416Smarkm#endif
46057416Smarkm    fprintf(stderr, " [-h]");
46157416Smarkm    fprintf(stderr, " [-L login]");
46257416Smarkm    fprintf(stderr, " [-n]");
46357416Smarkm#ifdef	_CRAY
46457416Smarkm    fprintf(stderr, " [-r[lowpty]-[highpty]]");
46557416Smarkm#endif
46657416Smarkm    fprintf(stderr, "\n\t");
46757416Smarkm#ifdef	HAVE_GETTOSBYNAME
46857416Smarkm    fprintf(stderr, " [-S tos]");
46957416Smarkm#endif
47057416Smarkm#ifdef	AUTHENTICATION
47157416Smarkm    fprintf(stderr, " [-X auth-type] [-y] [-z]");
47257416Smarkm#endif
47357416Smarkm    fprintf(stderr, " [-u utmp_hostname_length] [-U]");
47457416Smarkm    fprintf(stderr, " [port]\n");
47557416Smarkm    exit(1);
47657416Smarkm}
47757416Smarkm
47857416Smarkm/*
47957416Smarkm * getterminaltype
48057416Smarkm *
48157416Smarkm *	Ask the other end to send along its terminal type and speed.
48257416Smarkm * Output is the variable terminaltype filled in.
48357416Smarkm */
48457416Smarkmstatic unsigned char ttytype_sbbuf[] = {
48557416Smarkm    IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
48657416Smarkm};
48757416Smarkm
48857416Smarkmint
48957416Smarkmgetterminaltype(char *name, size_t name_sz)
49057416Smarkm{
49157416Smarkm    int retval = -1;
49257416Smarkm    void _gettermname();
49357416Smarkm
49457416Smarkm    settimer(baseline);
49557416Smarkm#ifdef AUTHENTICATION
49657416Smarkm    /*
49757416Smarkm     * Handle the Authentication option before we do anything else.
49857416Smarkm     */
49957416Smarkm    send_do(TELOPT_AUTHENTICATION, 1);
50057416Smarkm    while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
50157416Smarkm	ttloop();
50257416Smarkm    if (his_state_is_will(TELOPT_AUTHENTICATION)) {
50357416Smarkm	retval = auth_wait(name, name_sz);
50457416Smarkm    }
50557416Smarkm#endif
50657416Smarkm
50757416Smarkm#ifdef ENCRYPTION
50857416Smarkm    send_will(TELOPT_ENCRYPT, 1);
50957416Smarkm    send_do(TELOPT_ENCRYPT, 1);	/* esc@magic.fi */
51057416Smarkm#endif
51157416Smarkm    send_do(TELOPT_TTYPE, 1);
51257416Smarkm    send_do(TELOPT_TSPEED, 1);
51357416Smarkm    send_do(TELOPT_XDISPLOC, 1);
51457416Smarkm    send_do(TELOPT_NEW_ENVIRON, 1);
51557416Smarkm    send_do(TELOPT_OLD_ENVIRON, 1);
51657416Smarkm    while (
51757416Smarkm#ifdef ENCRYPTION
51857416Smarkm	   his_do_dont_is_changing(TELOPT_ENCRYPT) ||
51957416Smarkm#endif
52057416Smarkm	   his_will_wont_is_changing(TELOPT_TTYPE) ||
52157416Smarkm	   his_will_wont_is_changing(TELOPT_TSPEED) ||
52257416Smarkm	   his_will_wont_is_changing(TELOPT_XDISPLOC) ||
52357416Smarkm	   his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
52457416Smarkm	   his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
52557416Smarkm	ttloop();
52657416Smarkm    }
52757416Smarkm#ifdef ENCRYPTION
52857416Smarkm    /*
52957416Smarkm     * Wait for the negotiation of what type of encryption we can
53057416Smarkm     * send with.  If autoencrypt is not set, this will just return.
53157416Smarkm     */
53257416Smarkm    if (his_state_is_will(TELOPT_ENCRYPT)) {
53357416Smarkm	encrypt_wait();
53457416Smarkm    }
53557416Smarkm#endif
53657416Smarkm    if (his_state_is_will(TELOPT_TSPEED)) {
53757416Smarkm	static unsigned char sb[] =
53857416Smarkm	{ IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
53957416Smarkm
54057416Smarkm	telnet_net_write (sb, sizeof sb);
54157416Smarkm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
54257416Smarkm    }
54357416Smarkm    if (his_state_is_will(TELOPT_XDISPLOC)) {
54457416Smarkm	static unsigned char sb[] =
54557416Smarkm	{ IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
54657416Smarkm
54757416Smarkm	telnet_net_write (sb, sizeof sb);
54857416Smarkm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
54957416Smarkm    }
55057416Smarkm    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
55157416Smarkm	static unsigned char sb[] =
55257416Smarkm	{ IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
55357416Smarkm
55457416Smarkm	telnet_net_write (sb, sizeof sb);
55557416Smarkm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
55657416Smarkm    }
55757416Smarkm    else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
55857416Smarkm	static unsigned char sb[] =
55957416Smarkm	{ IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
56057416Smarkm
56157416Smarkm	telnet_net_write (sb, sizeof sb);
56257416Smarkm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
56357416Smarkm    }
56457416Smarkm    if (his_state_is_will(TELOPT_TTYPE)) {
56557416Smarkm
56657416Smarkm	telnet_net_write (ttytype_sbbuf, sizeof ttytype_sbbuf);
56757416Smarkm	DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
56857416Smarkm				  sizeof ttytype_sbbuf - 2););
56957416Smarkm    }
57057416Smarkm    if (his_state_is_will(TELOPT_TSPEED)) {
57157416Smarkm	while (sequenceIs(tspeedsubopt, baseline))
57257416Smarkm	    ttloop();
57357416Smarkm    }
57457416Smarkm    if (his_state_is_will(TELOPT_XDISPLOC)) {
57557416Smarkm	while (sequenceIs(xdisplocsubopt, baseline))
57657416Smarkm	    ttloop();
57757416Smarkm    }
57857416Smarkm    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
57957416Smarkm	while (sequenceIs(environsubopt, baseline))
58057416Smarkm	    ttloop();
58157416Smarkm    }
58257416Smarkm    if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
58357416Smarkm	while (sequenceIs(oenvironsubopt, baseline))
58457416Smarkm	    ttloop();
58557416Smarkm    }
58657416Smarkm    if (his_state_is_will(TELOPT_TTYPE)) {
58757416Smarkm	char first[256], last[256];
58857416Smarkm
58957416Smarkm	while (sequenceIs(ttypesubopt, baseline))
59057416Smarkm	    ttloop();
59157416Smarkm
59257416Smarkm	/*
59357416Smarkm	 * If the other side has already disabled the option, then
59457416Smarkm	 * we have to just go with what we (might) have already gotten.
59557416Smarkm	 */
59657416Smarkm	if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
59757416Smarkm	    strlcpy(first, terminaltype, sizeof(first));
59857416Smarkm	    for(;;) {
59957416Smarkm		/*
60057416Smarkm		 * Save the unknown name, and request the next name.
60157416Smarkm		 */
60257416Smarkm		strlcpy(last, terminaltype, sizeof(last));
60357416Smarkm		_gettermname();
60457416Smarkm		if (terminaltypeok(terminaltype))
60557416Smarkm		    break;
60657416Smarkm		if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
60757416Smarkm		    his_state_is_wont(TELOPT_TTYPE)) {
60857416Smarkm		    /*
60957416Smarkm		     * We've hit the end.  If this is the same as
61057416Smarkm		     * the first name, just go with it.
61157416Smarkm		     */
61257416Smarkm		    if (strncmp(first, terminaltype, sizeof(first)) == 0)
61357416Smarkm			break;
61457416Smarkm		    /*
61557416Smarkm		     * Get the terminal name one more time, so that
61657416Smarkm		     * RFC1091 compliant telnets will cycle back to
61757416Smarkm		     * the start of the list.
61857416Smarkm		     */
61957416Smarkm		    _gettermname();
62057416Smarkm		    if (strncmp(first, terminaltype, sizeof(first)) != 0)
62157416Smarkm			strcpy(terminaltype, first);
62257416Smarkm		    break;
62357416Smarkm		}
62457416Smarkm	    }
62557416Smarkm	}
62657416Smarkm    }
62757416Smarkm    return(retval);
62857416Smarkm}  /* end of getterminaltype */
62957416Smarkm
63057416Smarkmvoid
63157416Smarkm_gettermname()
63257416Smarkm{
63357416Smarkm    /*
63457416Smarkm     * If the client turned off the option,
63557416Smarkm     * we can't send another request, so we
63657416Smarkm     * just return.
63757416Smarkm     */
63857416Smarkm    if (his_state_is_wont(TELOPT_TTYPE))
63957416Smarkm	return;
64057416Smarkm    settimer(baseline);
64157416Smarkm    telnet_net_write (ttytype_sbbuf, sizeof ttytype_sbbuf);
64257416Smarkm    DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
64357416Smarkm			      sizeof ttytype_sbbuf - 2););
64457416Smarkm    while (sequenceIs(ttypesubopt, baseline))
64557416Smarkm	ttloop();
64657416Smarkm}
64757416Smarkm
64857416Smarkmint
64957416Smarkmterminaltypeok(char *s)
65057416Smarkm{
65157416Smarkm    return 1;
65257416Smarkm}
65357416Smarkm
65457416Smarkm
65557416Smarkmchar *hostname;
65657416Smarkmchar host_name[MaxHostNameLen];
65757416Smarkmchar remote_host_name[MaxHostNameLen];
65857416Smarkm
65957416Smarkm/*
66057416Smarkm * Get a pty, scan input lines.
66157416Smarkm */
66257416Smarkmstatic void
66357416Smarkmdoit(struct sockaddr *who, int who_len)
66457416Smarkm{
66557416Smarkm    char *host = NULL;
66657416Smarkm    int level;
66757416Smarkm    int ptynum;
66857416Smarkm    char user_name[256];
66957416Smarkm    int error;
67057416Smarkm    char host_addr[256];
67157416Smarkm
67257416Smarkm    /*
67357416Smarkm     * Find an available pty to use.
67457416Smarkm     */
67557416Smarkm    ourpty = getpty(&ptynum);
67657416Smarkm    if (ourpty < 0)
67757416Smarkm	fatal(net, "All network ports in use");
67857416Smarkm
67957416Smarkm#ifdef _SC_CRAY_SECURE_SYS
68057416Smarkm    /*
68157416Smarkm     *	set ttyp line security label
68257416Smarkm     */
68357416Smarkm    if (secflag) {
68457416Smarkm	char slave_dev[16];
68557416Smarkm
68657416Smarkm	snprintf(tty_dev, sizeof(tty_dev), "/dev/pty/%03d", ptynum);
68757416Smarkm	if (setdevs(tty_dev, &dv) < 0)
68857416Smarkm	    fatal(net, "cannot set pty security");
68957416Smarkm	snprintf(slave_dev, sizeof(slave_dev), "/dev/ttyp%03d", ptynum);
69057416Smarkm	if (setdevs(slave_dev, &dv) < 0)
69157416Smarkm	    fatal(net, "cannot set tty security");
69257416Smarkm    }
69357416Smarkm#endif	/* _SC_CRAY_SECURE_SYS */
69457416Smarkm
69557416Smarkm    error = getnameinfo_verified (who, who_len, host_addr, sizeof(host_addr),
69657416Smarkm				  NULL, 0,
69757416Smarkm				  registerd_host_only ? NI_NAMEREQD : 0);
69857416Smarkm    if (error)
69957416Smarkm	fatal(net, "Couldn't resolve your address into a host name.\r\n\
70057416SmarkmPlease contact your net administrator");
70157416Smarkm
70257416Smarkm    /*
70357416Smarkm     * We must make a copy because Kerberos is probably going
70457416Smarkm     * to also do a gethost* and overwrite the static data...
70557416Smarkm     */
70657416Smarkm    strlcpy(remote_host_name, host_addr, sizeof(remote_host_name));
70757416Smarkm    host = remote_host_name;
70857416Smarkm
70957416Smarkm    /* XXX - should be k_gethostname? */
71057416Smarkm    gethostname(host_name, sizeof (host_name));
71157416Smarkm    hostname = host_name;
71257416Smarkm
71357416Smarkm    /* Only trim if too long (and possible) */
71457416Smarkm    if (strlen(remote_host_name) > abs(utmp_len)) {
71557416Smarkm	char *domain = strchr(host_name, '.');
71657416Smarkm	char *p = strchr(remote_host_name, '.');
71757416Smarkm	if (domain && p && (strcmp(p, domain) == 0))
71857416Smarkm	    *p = 0; /* remove domain part */
71957416Smarkm    }
72057416Smarkm
72157416Smarkm
72257416Smarkm    /*
72357416Smarkm     * If hostname still doesn't fit utmp, use ipaddr.
72457416Smarkm     */
72557416Smarkm    if (strlen(remote_host_name) > abs(utmp_len))
72657416Smarkm	strlcpy(remote_host_name,
72757416Smarkm		host_addr,
72857416Smarkm		sizeof(remote_host_name));
72957416Smarkm
73057416Smarkm#ifdef AUTHENTICATION
73157416Smarkm    auth_encrypt_init(hostname, host, "TELNETD", 1);
73257416Smarkm#endif
73357416Smarkm
73457416Smarkm    init_env();
73557416Smarkm    /*
73657416Smarkm     * get terminal type.
73757416Smarkm     */
73857416Smarkm    *user_name = 0;
73957416Smarkm    level = getterminaltype(user_name, sizeof(user_name));
74057416Smarkm    setenv("TERM", terminaltype ? terminaltype : "network", 1);
74157416Smarkm
74257416Smarkm#ifdef _SC_CRAY_SECURE_SYS
74357416Smarkm    if (secflag) {
74457416Smarkm	if (setulvl(dv.dv_actlvl) < 0)
74557416Smarkm	    fatal(net,"cannot setulvl()");
74657416Smarkm	if (setucmp(dv.dv_actcmp) < 0)
74757416Smarkm	    fatal(net, "cannot setucmp()");
74857416Smarkm    }
74957416Smarkm#endif	/* _SC_CRAY_SECURE_SYS */
75057416Smarkm
75157416Smarkm    /* begin server processing */
75257416Smarkm    my_telnet(net, ourpty, host, level, user_name);
75357416Smarkm    /*NOTREACHED*/
75457416Smarkm}  /* end of doit */
75557416Smarkm
75657416Smarkm/* output contents of /etc/issue.net, or /etc/issue */
75757416Smarkmstatic void
75857416Smarkmshow_issue(void)
75957416Smarkm{
76057416Smarkm    FILE *f;
76157416Smarkm    char buf[128];
76257416Smarkm    f = fopen("/etc/issue.net", "r");
76357416Smarkm    if(f == NULL)
76457416Smarkm	f = fopen("/etc/issue", "r");
76557416Smarkm    if(f){
76657416Smarkm	while(fgets(buf, sizeof(buf)-2, f)){
76757416Smarkm	    strcpy(buf + strcspn(buf, "\r\n"), "\r\n");
76857416Smarkm	    writenet((unsigned char*)buf, strlen(buf));
76957416Smarkm	}
77057416Smarkm	fclose(f);
77157416Smarkm    }
77257416Smarkm}
77357416Smarkm
77457416Smarkm/*
77557416Smarkm * Main loop.  Select from pty and network, and
77657416Smarkm * hand data to telnet receiver finite state machine.
77757416Smarkm */
77857416Smarkmvoid
77957416Smarkmmy_telnet(int f, int p, char *host, int level, char *autoname)
78057416Smarkm{
78157416Smarkm    int on = 1;
78257416Smarkm    char *he;
78357416Smarkm    char *IM;
78457416Smarkm    int nfd;
78557416Smarkm    int startslave_called = 0;
78657416Smarkm    time_t timeout;
78757416Smarkm
78857416Smarkm    /*
78957416Smarkm     * Initialize the slc mapping table.
79057416Smarkm     */
79157416Smarkm    get_slc_defaults();
79257416Smarkm
79357416Smarkm    /*
79457416Smarkm     * Do some tests where it is desireable to wait for a response.
79557416Smarkm     * Rather than doing them slowly, one at a time, do them all
79657416Smarkm     * at once.
79757416Smarkm     */
79857416Smarkm    if (my_state_is_wont(TELOPT_SGA))
79957416Smarkm	send_will(TELOPT_SGA, 1);
80057416Smarkm    /*
80157416Smarkm     * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
80257416Smarkm     * because 4.2 clients are unable to deal with TCP urgent data.
80357416Smarkm     *
80457416Smarkm     * To find out, we send out a "DO ECHO".  If the remote system
80557416Smarkm     * answers "WILL ECHO" it is probably a 4.2 client, and we note
80657416Smarkm     * that fact ("WILL ECHO" ==> that the client will echo what
80757416Smarkm     * WE, the server, sends it; it does NOT mean that the client will
80857416Smarkm     * echo the terminal input).
80957416Smarkm     */
81057416Smarkm    send_do(TELOPT_ECHO, 1);
81157416Smarkm
81257416Smarkm    /*
81357416Smarkm     * Send along a couple of other options that we wish to negotiate.
81457416Smarkm     */
81557416Smarkm    send_do(TELOPT_NAWS, 1);
81657416Smarkm    send_will(TELOPT_STATUS, 1);
81757416Smarkm    flowmode = 1;		/* default flow control state */
81857416Smarkm    restartany = -1;	/* uninitialized... */
81957416Smarkm    send_do(TELOPT_LFLOW, 1);
82057416Smarkm
82157416Smarkm    /*
82257416Smarkm     * Spin, waiting for a response from the DO ECHO.  However,
82357416Smarkm     * some REALLY DUMB telnets out there might not respond
82457416Smarkm     * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
82557416Smarkm     * telnets so far seem to respond with WONT for a DO that
82657416Smarkm     * they don't understand...) because by the time we get the
82757416Smarkm     * response, it will already have processed the DO ECHO.
82857416Smarkm     * Kludge upon kludge.
82957416Smarkm     */
83057416Smarkm    while (his_will_wont_is_changing(TELOPT_NAWS))
83157416Smarkm	ttloop();
83257416Smarkm
83357416Smarkm    /*
83457416Smarkm     * But...
83557416Smarkm     * The client might have sent a WILL NAWS as part of its
83657416Smarkm     * startup code; if so, we'll be here before we get the
83757416Smarkm     * response to the DO ECHO.  We'll make the assumption
83857416Smarkm     * that any implementation that understands about NAWS
83957416Smarkm     * is a modern enough implementation that it will respond
84057416Smarkm     * to our DO ECHO request; hence we'll do another spin
84157416Smarkm     * waiting for the ECHO option to settle down, which is
84257416Smarkm     * what we wanted to do in the first place...
84357416Smarkm     */
84457416Smarkm    if (his_want_state_is_will(TELOPT_ECHO) &&
84557416Smarkm	his_state_is_will(TELOPT_NAWS)) {
84657416Smarkm	while (his_will_wont_is_changing(TELOPT_ECHO))
84757416Smarkm	    ttloop();
84857416Smarkm    }
84957416Smarkm    /*
85057416Smarkm     * On the off chance that the telnet client is broken and does not
85157416Smarkm     * respond to the DO ECHO we sent, (after all, we did send the
85257416Smarkm     * DO NAWS negotiation after the DO ECHO, and we won't get here
85357416Smarkm     * until a response to the DO NAWS comes back) simulate the
85457416Smarkm     * receipt of a will echo.  This will also send a WONT ECHO
85557416Smarkm     * to the client, since we assume that the client failed to
85657416Smarkm     * respond because it believes that it is already in DO ECHO
85757416Smarkm     * mode, which we do not want.
85857416Smarkm     */
85957416Smarkm    if (his_want_state_is_will(TELOPT_ECHO)) {
86057416Smarkm	DIAG(TD_OPTIONS,
86157416Smarkm	     {output_data("td: simulating recv\r\n");
86257416Smarkm	     });
86357416Smarkm	willoption(TELOPT_ECHO);
86457416Smarkm    }
86557416Smarkm
86657416Smarkm    /*
86757416Smarkm     * Finally, to clean things up, we turn on our echo.  This
86857416Smarkm     * will break stupid 4.2 telnets out of local terminal echo.
86957416Smarkm     */
87057416Smarkm
87157416Smarkm    if (my_state_is_wont(TELOPT_ECHO))
87257416Smarkm	send_will(TELOPT_ECHO, 1);
87357416Smarkm
87457416Smarkm#ifdef TIOCPKT
87557416Smarkm#ifdef	STREAMSPTY
87657416Smarkm    if (!really_stream)
87757416Smarkm#endif
87857416Smarkm	/*
87957416Smarkm	 * Turn on packet mode
88057416Smarkm	 */
88157416Smarkm	ioctl(p, TIOCPKT, (char *)&on);
88257416Smarkm#endif
88357416Smarkm
88457416Smarkm
88557416Smarkm    /*
88657416Smarkm     * Call telrcv() once to pick up anything received during
88757416Smarkm     * terminal type negotiation, 4.2/4.3 determination, and
88857416Smarkm     * linemode negotiation.
88957416Smarkm     */
89057416Smarkm    telrcv();
89157416Smarkm
89257416Smarkm    ioctl(f, FIONBIO, (char *)&on);
89357416Smarkm    ioctl(p, FIONBIO, (char *)&on);
89457416Smarkm
89557416Smarkm#if	defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT)
89657416Smarkm    setsockopt(net, SOL_SOCKET, SO_OOBINLINE,
89757416Smarkm	       (void *)&on, sizeof on);
89857416Smarkm#endif	/* defined(SO_OOBINLINE) */
89957416Smarkm
90057416Smarkm#ifdef	SIGTSTP
90157416Smarkm    signal(SIGTSTP, SIG_IGN);
90257416Smarkm#endif
90357416Smarkm#ifdef	SIGTTOU
90457416Smarkm    /*
90557416Smarkm     * Ignoring SIGTTOU keeps the kernel from blocking us
90657416Smarkm     * in ttioct() in /sys/tty.c.
90757416Smarkm     */
90857416Smarkm    signal(SIGTTOU, SIG_IGN);
90957416Smarkm#endif
91057416Smarkm
91157416Smarkm    signal(SIGCHLD, cleanup);
91257416Smarkm
91357416Smarkm#ifdef  TIOCNOTTY
91457416Smarkm    {
91557416Smarkm	int t;
91657416Smarkm	t = open(_PATH_TTY, O_RDWR);
91757416Smarkm	if (t >= 0) {
91857416Smarkm	    ioctl(t, TIOCNOTTY, (char *)0);
91957416Smarkm	    close(t);
92057416Smarkm	}
92157416Smarkm    }
92257416Smarkm#endif
92357416Smarkm
92457416Smarkm    show_issue();
92557416Smarkm    /*
92657416Smarkm     * Show banner that getty never gave.
92757416Smarkm     *
92857416Smarkm     * We put the banner in the pty input buffer.  This way, it
92957416Smarkm     * gets carriage return null processing, etc., just like all
93057416Smarkm     * other pty --> client data.
93157416Smarkm     */
93257416Smarkm
93357416Smarkm    if (getenv("USER"))
93457416Smarkm	hostinfo = 0;
93557416Smarkm
93657416Smarkm    IM = DEFAULT_IM;
93757416Smarkm    he = 0;
93857416Smarkm    edithost(he, host_name);
93957416Smarkm    if (hostinfo && *IM)
94057416Smarkm	putf(IM, ptyibuf2);
94157416Smarkm
94257416Smarkm    if (pcc)
94357416Smarkm	strncat(ptyibuf2, ptyip, pcc+1);
94457416Smarkm    ptyip = ptyibuf2;
94557416Smarkm    pcc = strlen(ptyip);
94657416Smarkm
94757416Smarkm    DIAG(TD_REPORT, {
94857416Smarkm	output_data("td: Entering processing loop\r\n");
94957416Smarkm    });
95057416Smarkm
95157416Smarkm
95257416Smarkm    nfd = ((f > p) ? f : p) + 1;
95357416Smarkm    timeout = time(NULL) + 5;
95457416Smarkm    for (;;) {
95557416Smarkm	fd_set ibits, obits, xbits;
95657416Smarkm	int c;
95757416Smarkm
95857416Smarkm	/* wait for encryption to be turned on, but don't wait
95957416Smarkm           indefinitely */
96057416Smarkm	if(!startslave_called && (!encrypt_delay() || timeout > time(NULL))){
96157416Smarkm	    startslave_called = 1;
96257416Smarkm	    startslave(host, level, autoname);
96357416Smarkm	}
96457416Smarkm
96557416Smarkm	if (ncc < 0 && pcc < 0)
96657416Smarkm	    break;
96757416Smarkm
96857416Smarkm	FD_ZERO(&ibits);
96957416Smarkm	FD_ZERO(&obits);
97057416Smarkm	FD_ZERO(&xbits);
97157416Smarkm	/*
97257416Smarkm	 * Never look for input if there's still
97357416Smarkm	 * stuff in the corresponding output buffer
97457416Smarkm	 */
97557416Smarkm	if (nfrontp - nbackp || pcc > 0) {
97657416Smarkm	    FD_SET(f, &obits);
97757416Smarkm	} else {
97857416Smarkm	    FD_SET(p, &ibits);
97957416Smarkm	}
98057416Smarkm	if (pfrontp - pbackp || ncc > 0) {
98157416Smarkm	    FD_SET(p, &obits);
98257416Smarkm	} else {
98357416Smarkm	    FD_SET(f, &ibits);
98457416Smarkm	}
98557416Smarkm	if (!SYNCHing) {
98657416Smarkm	    FD_SET(f, &xbits);
98757416Smarkm	}
98857416Smarkm	if ((c = select(nfd, &ibits, &obits, &xbits,
98957416Smarkm			(struct timeval *)0)) < 1) {
99057416Smarkm	    if (c == -1) {
99157416Smarkm		if (errno == EINTR) {
99257416Smarkm		    continue;
99357416Smarkm		}
99457416Smarkm	    }
99557416Smarkm	    sleep(5);
99657416Smarkm	    continue;
99757416Smarkm	}
99857416Smarkm
99957416Smarkm	/*
100057416Smarkm	 * Any urgent data?
100157416Smarkm	 */
100257416Smarkm	if (FD_ISSET(net, &xbits)) {
100357416Smarkm	    SYNCHing = 1;
100457416Smarkm	}
100557416Smarkm
100657416Smarkm	/*
100757416Smarkm	 * Something to read from the network...
100857416Smarkm	 */
100957416Smarkm	if (FD_ISSET(net, &ibits)) {
101057416Smarkm#ifndef SO_OOBINLINE
101157416Smarkm	    /*
101257416Smarkm	     * In 4.2 (and 4.3 beta) systems, the
101357416Smarkm	     * OOB indication and data handling in the kernel
101457416Smarkm	     * is such that if two separate TCP Urgent requests
101557416Smarkm	     * come in, one byte of TCP data will be overlaid.
101657416Smarkm	     * This is fatal for Telnet, but we try to live
101757416Smarkm	     * with it.
101857416Smarkm	     *
101957416Smarkm	     * In addition, in 4.2 (and...), a special protocol
102057416Smarkm	     * is needed to pick up the TCP Urgent data in
102157416Smarkm	     * the correct sequence.
102257416Smarkm	     *
102357416Smarkm	     * What we do is:  if we think we are in urgent
102457416Smarkm	     * mode, we look to see if we are "at the mark".
102557416Smarkm	     * If we are, we do an OOB receive.  If we run
102657416Smarkm	     * this twice, we will do the OOB receive twice,
102757416Smarkm	     * but the second will fail, since the second
102857416Smarkm	     * time we were "at the mark", but there wasn't
102957416Smarkm	     * any data there (the kernel doesn't reset
103057416Smarkm	     * "at the mark" until we do a normal read).
103157416Smarkm	     * Once we've read the OOB data, we go ahead
103257416Smarkm	     * and do normal reads.
103357416Smarkm	     *
103457416Smarkm	     * There is also another problem, which is that
103557416Smarkm	     * since the OOB byte we read doesn't put us
103657416Smarkm	     * out of OOB state, and since that byte is most
103757416Smarkm	     * likely the TELNET DM (data mark), we would
103857416Smarkm	     * stay in the TELNET SYNCH (SYNCHing) state.
103957416Smarkm	     * So, clocks to the rescue.  If we've "just"
104057416Smarkm	     * received a DM, then we test for the
104157416Smarkm	     * presence of OOB data when the receive OOB
104257416Smarkm	     * fails (and AFTER we did the normal mode read
104357416Smarkm	     * to clear "at the mark").
104457416Smarkm	     */
104557416Smarkm	    if (SYNCHing) {
104657416Smarkm		int atmark;
104757416Smarkm
104857416Smarkm		ioctl(net, SIOCATMARK, (char *)&atmark);
104957416Smarkm		if (atmark) {
105057416Smarkm		    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
105157416Smarkm		    if ((ncc == -1) && (errno == EINVAL)) {
105257416Smarkm			ncc = read(net, netibuf, sizeof (netibuf));
105357416Smarkm			if (sequenceIs(didnetreceive, gotDM)) {
105457416Smarkm			    SYNCHing = stilloob(net);
105557416Smarkm			}
105657416Smarkm		    }
105757416Smarkm		} else {
105857416Smarkm		    ncc = read(net, netibuf, sizeof (netibuf));
105957416Smarkm		}
106057416Smarkm	    } else {
106157416Smarkm		ncc = read(net, netibuf, sizeof (netibuf));
106257416Smarkm	    }
106357416Smarkm	    settimer(didnetreceive);
106457416Smarkm#else	/* !defined(SO_OOBINLINE)) */
106557416Smarkm	    ncc = read(net, netibuf, sizeof (netibuf));
106657416Smarkm#endif	/* !defined(SO_OOBINLINE)) */
106757416Smarkm	    if (ncc < 0 && errno == EWOULDBLOCK)
106857416Smarkm		ncc = 0;
106957416Smarkm	    else {
107057416Smarkm		if (ncc <= 0) {
107157416Smarkm		    break;
107257416Smarkm		}
107357416Smarkm		netip = netibuf;
107457416Smarkm	    }
107557416Smarkm	    DIAG((TD_REPORT | TD_NETDATA), {
107657416Smarkm		output_data("td: netread %d chars\r\n", ncc);
107757416Smarkm		});
107857416Smarkm	    DIAG(TD_NETDATA, printdata("nd", netip, ncc));
107957416Smarkm	}
108057416Smarkm
108157416Smarkm	/*
108257416Smarkm	 * Something to read from the pty...
108357416Smarkm	 */
108457416Smarkm	if (FD_ISSET(p, &ibits)) {
108557416Smarkm#ifdef STREAMSPTY
108657416Smarkm	    if (really_stream)
108757416Smarkm		pcc = readstream(p, ptyibuf, BUFSIZ);
108857416Smarkm	    else
108957416Smarkm#endif
109057416Smarkm		pcc = read(p, ptyibuf, BUFSIZ);
109157416Smarkm
109257416Smarkm	    /*
109357416Smarkm	     * On some systems, if we try to read something
109457416Smarkm	     * off the master side before the slave side is
109557416Smarkm	     * opened, we get EIO.
109657416Smarkm	     */
109757416Smarkm	    if (pcc < 0 && (errno == EWOULDBLOCK ||
109857416Smarkm#ifdef	EAGAIN
109957416Smarkm			    errno == EAGAIN ||
110057416Smarkm#endif
110157416Smarkm			    errno == EIO)) {
110257416Smarkm		pcc = 0;
110357416Smarkm	    } else {
110457416Smarkm		if (pcc <= 0)
110557416Smarkm		    break;
110657416Smarkm		if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
110757416Smarkm		    netclear();	/* clear buffer back */
110857416Smarkm#ifndef	NO_URGENT
110957416Smarkm		    /*
111057416Smarkm		     * There are client telnets on some
111157416Smarkm		     * operating systems get screwed up
111257416Smarkm		     * royally if we send them urgent
111357416Smarkm		     * mode data.
111457416Smarkm		     */
111557416Smarkm		    output_data ("%c%c", IAC, DM);
111657416Smarkm
111757416Smarkm		    neturg = nfrontp-1; /* off by one XXX */
111857416Smarkm		    DIAG(TD_OPTIONS,
111957416Smarkm			 printoption("td: send IAC", DM));
112057416Smarkm
112157416Smarkm#endif
112257416Smarkm		}
112357416Smarkm		if (his_state_is_will(TELOPT_LFLOW) &&
112457416Smarkm		    (ptyibuf[0] &
112557416Smarkm		     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
112657416Smarkm		    int newflow =
112757416Smarkm			ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
112857416Smarkm		    if (newflow != flowmode) {
112957416Smarkm			flowmode = newflow;
113057416Smarkm			output_data("%c%c%c%c%c%c",
113157416Smarkm				    IAC, SB, TELOPT_LFLOW,
113257416Smarkm				    flowmode ? LFLOW_ON
113357416Smarkm				    : LFLOW_OFF,
113457416Smarkm				    IAC, SE);
113557416Smarkm			DIAG(TD_OPTIONS, printsub('>',
113657416Smarkm						  (unsigned char *)nfrontp-4,
113757416Smarkm						  4););
113857416Smarkm		    }
113957416Smarkm		}
114057416Smarkm		pcc--;
114157416Smarkm		ptyip = ptyibuf+1;
114257416Smarkm	    }
114357416Smarkm	}
114457416Smarkm
114557416Smarkm	while (pcc > 0) {
114657416Smarkm	    if ((&netobuf[BUFSIZ] - nfrontp) < 3)
114757416Smarkm		break;
114857416Smarkm	    c = *ptyip++ & 0377, pcc--;
114957416Smarkm	    if (c == IAC)
115057416Smarkm		*nfrontp++ = c;
115157416Smarkm	    *nfrontp++ = c;
115257416Smarkm	    if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
115357416Smarkm		if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
115457416Smarkm		    *nfrontp++ = *ptyip++ & 0377;
115557416Smarkm		    pcc--;
115657416Smarkm		} else
115757416Smarkm		    *nfrontp++ = '\0';
115857416Smarkm	    }
115957416Smarkm	}
116057416Smarkm
116157416Smarkm	if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
116257416Smarkm	    netflush();
116357416Smarkm	if (ncc > 0)
116457416Smarkm	    telrcv();
116557416Smarkm	if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
116657416Smarkm	    ptyflush();
116757416Smarkm    }
116857416Smarkm    cleanup(0);
116957416Smarkm}
117057416Smarkm
117157416Smarkm#ifndef	TCSIG
117257416Smarkm# ifdef	TIOCSIG
117357416Smarkm#  define TCSIG TIOCSIG
117457416Smarkm# endif
117557416Smarkm#endif
117657416Smarkm
117757416Smarkm#ifdef	STREAMSPTY
117857416Smarkm
117957416Smarkm    int flowison = -1;  /* current state of flow: -1 is unknown */
118057416Smarkm
118157416Smarkmint
118257416Smarkmreadstream(int p, char *ibuf, int bufsize)
118357416Smarkm{
118457416Smarkm    int flags = 0;
118557416Smarkm    int ret = 0;
118657416Smarkm    struct termios *tsp;
118757416Smarkm#if 0
118857416Smarkm    struct termio *tp;
118957416Smarkm#endif
119057416Smarkm    struct iocblk *ip;
119157416Smarkm    char vstop, vstart;
119257416Smarkm    int ixon;
119357416Smarkm    int newflow;
119457416Smarkm
119557416Smarkm    strbufc.maxlen = BUFSIZ;
119657416Smarkm    strbufc.buf = (char *)ctlbuf;
119757416Smarkm    strbufd.maxlen = bufsize-1;
119857416Smarkm    strbufd.len = 0;
119957416Smarkm    strbufd.buf = ibuf+1;
120057416Smarkm    ibuf[0] = 0;
120157416Smarkm
120257416Smarkm    ret = getmsg(p, &strbufc, &strbufd, &flags);
120357416Smarkm    if (ret < 0)  /* error of some sort -- probably EAGAIN */
120457416Smarkm	return(-1);
120557416Smarkm
120657416Smarkm    if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
120757416Smarkm	/* data message */
120857416Smarkm	if (strbufd.len > 0) {			/* real data */
120957416Smarkm	    return(strbufd.len + 1);	/* count header char */
121057416Smarkm	} else {
121157416Smarkm	    /* nothing there */
121257416Smarkm	    errno = EAGAIN;
121357416Smarkm	    return(-1);
121457416Smarkm	}
121557416Smarkm    }
121657416Smarkm
121757416Smarkm    /*
121857416Smarkm     * It's a control message.  Return 1, to look at the flag we set
121957416Smarkm     */
122057416Smarkm
122157416Smarkm    switch (ctlbuf[0]) {
122257416Smarkm    case M_FLUSH:
122357416Smarkm	if (ibuf[1] & FLUSHW)
122457416Smarkm	    ibuf[0] = TIOCPKT_FLUSHWRITE;
122557416Smarkm	return(1);
122657416Smarkm
122757416Smarkm    case M_IOCTL:
122857416Smarkm	ip = (struct iocblk *) (ibuf+1);
122957416Smarkm
123057416Smarkm	switch (ip->ioc_cmd) {
123157416Smarkm#ifdef TCSETS
123257416Smarkm	case TCSETS:
123357416Smarkm	case TCSETSW:
123457416Smarkm	case TCSETSF:
123557416Smarkm	    tsp = (struct termios *)
123657416Smarkm		(ibuf+1 + sizeof(struct iocblk));
123757416Smarkm	    vstop = tsp->c_cc[VSTOP];
123857416Smarkm	    vstart = tsp->c_cc[VSTART];
123957416Smarkm	    ixon = tsp->c_iflag & IXON;
124057416Smarkm	    break;
124157416Smarkm#endif
124257416Smarkm#if 0
124357416Smarkm	case TCSETA:
124457416Smarkm	case TCSETAW:
124557416Smarkm	case TCSETAF:
124657416Smarkm	    tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk));
124757416Smarkm	    vstop = tp->c_cc[VSTOP];
124857416Smarkm	    vstart = tp->c_cc[VSTART];
124957416Smarkm	    ixon = tp->c_iflag & IXON;
125057416Smarkm	    break;
125157416Smarkm#endif
125257416Smarkm	default:
125357416Smarkm	    errno = EAGAIN;
125457416Smarkm	    return(-1);
125557416Smarkm	}
125657416Smarkm
125757416Smarkm	newflow =  (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
125857416Smarkm	if (newflow != flowison) {  /* it's a change */
125957416Smarkm	    flowison = newflow;
126057416Smarkm	    ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
126157416Smarkm	    return(1);
126257416Smarkm	}
126357416Smarkm    }
126457416Smarkm
126557416Smarkm    /* nothing worth doing anything about */
126657416Smarkm    errno = EAGAIN;
126757416Smarkm    return(-1);
126857416Smarkm}
126957416Smarkm#endif /* STREAMSPTY */
127057416Smarkm
127157416Smarkm/*
127257416Smarkm * Send interrupt to process on other side of pty.
127357416Smarkm * If it is in raw mode, just write NULL;
127457416Smarkm * otherwise, write intr char.
127557416Smarkm */
127657416Smarkmvoid
127757416Smarkminterrupt()
127857416Smarkm{
127957416Smarkm    ptyflush();	/* half-hearted */
128057416Smarkm
128157416Smarkm#if defined(STREAMSPTY) && defined(TIOCSIGNAL)
128257416Smarkm    /* Streams PTY style ioctl to post a signal */
128357416Smarkm    if (really_stream)
128457416Smarkm	{
128557416Smarkm	    int sig = SIGINT;
128657416Smarkm	    ioctl(ourpty, TIOCSIGNAL, &sig);
128757416Smarkm	    ioctl(ourpty, I_FLUSH, FLUSHR);
128857416Smarkm	}
128957416Smarkm#else
129057416Smarkm#ifdef	TCSIG
129157416Smarkm    ioctl(ourpty, TCSIG, (char *)SIGINT);
129257416Smarkm#else	/* TCSIG */
129357416Smarkm    init_termbuf();
129457416Smarkm    *pfrontp++ = slctab[SLC_IP].sptr ?
129557416Smarkm	(unsigned char)*slctab[SLC_IP].sptr : '\177';
129657416Smarkm#endif	/* TCSIG */
129757416Smarkm#endif
129857416Smarkm}
129957416Smarkm
130057416Smarkm/*
130157416Smarkm * Send quit to process on other side of pty.
130257416Smarkm * If it is in raw mode, just write NULL;
130357416Smarkm * otherwise, write quit char.
130457416Smarkm */
130557416Smarkmvoid
130657416Smarkmsendbrk()
130757416Smarkm{
130857416Smarkm    ptyflush();	/* half-hearted */
130957416Smarkm#ifdef	TCSIG
131057416Smarkm    ioctl(ourpty, TCSIG, (char *)SIGQUIT);
131157416Smarkm#else	/* TCSIG */
131257416Smarkm    init_termbuf();
131357416Smarkm    *pfrontp++ = slctab[SLC_ABORT].sptr ?
131457416Smarkm	(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
131557416Smarkm#endif	/* TCSIG */
131657416Smarkm}
131757416Smarkm
131857416Smarkmvoid
131957416Smarkmsendsusp()
132057416Smarkm{
132157416Smarkm#ifdef	SIGTSTP
132257416Smarkm    ptyflush();	/* half-hearted */
132357416Smarkm# ifdef	TCSIG
132457416Smarkm    ioctl(ourpty, TCSIG, (char *)SIGTSTP);
132557416Smarkm# else	/* TCSIG */
132657416Smarkm    *pfrontp++ = slctab[SLC_SUSP].sptr ?
132757416Smarkm	(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
132857416Smarkm# endif	/* TCSIG */
132957416Smarkm#endif	/* SIGTSTP */
133057416Smarkm}
133157416Smarkm
133257416Smarkm/*
133357416Smarkm * When we get an AYT, if ^T is enabled, use that.  Otherwise,
133457416Smarkm * just send back "[Yes]".
133557416Smarkm */
133657416Smarkmvoid
133757416Smarkmrecv_ayt()
133857416Smarkm{
133957416Smarkm#if	defined(SIGINFO) && defined(TCSIG)
134057416Smarkm    if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
134157416Smarkm	ioctl(ourpty, TCSIG, (char *)SIGINFO);
134257416Smarkm	return;
134357416Smarkm    }
134457416Smarkm#endif
134557416Smarkm    output_data("\r\n[Yes]\r\n");
134657416Smarkm}
134757416Smarkm
134857416Smarkmvoid
134957416Smarkmdoeof()
135057416Smarkm{
135157416Smarkm    init_termbuf();
135257416Smarkm
135357416Smarkm    *pfrontp++ = slctab[SLC_EOF].sptr ?
135457416Smarkm	(unsigned char)*slctab[SLC_EOF].sptr : '\004';
135557416Smarkm}
1356