xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 45234)
121182Sdist /*
238904Sborman  * Copyright (c) 1989 Regents of the University of California.
333687Sbostic  * All rights reserved.
433687Sbostic  *
542673Sbostic  * %sccs.include.redist.c%
621182Sdist  */
721182Sdist 
86295Sroot #ifndef lint
921182Sdist char copyright[] =
1038904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\
1121182Sdist  All rights reserved.\n";
1233687Sbostic #endif /* not lint */
136295Sroot 
1421182Sdist #ifndef lint
15*45234Sborman static char sccsid[] = "@(#)telnetd.c	5.47 (Berkeley) 09/14/90";
1633687Sbostic #endif /* not lint */
1721182Sdist 
1838904Sborman #include "telnetd.h"
19*45234Sborman #include "pathnames.h"
2038904Sborman 
216002Sroot /*
2238904Sborman  * I/O data buffers,
2338904Sborman  * pointers, and counters.
246002Sroot  */
2538904Sborman char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
2638904Sborman char	ptyibuf2[BUFSIZ];
279218Ssam 
2838904Sborman int	hostinfo = 1;			/* do we print login banner? */
299218Ssam 
3038904Sborman #ifdef	CRAY
3138904Sborman extern int      newmap; /* nonzero if \n maps to ^M^J */
3240242Sborman int	lowpty = 0, highpty;	/* low, high pty numbers */
3338904Sborman #endif /* CRAY */
3412216Ssam 
3538904Sborman int debug = 0;
3638904Sborman char *progname;
379218Ssam 
3844363Sborman #if	defined(NEED_GETTOS)
3944363Sborman struct tosent {
4044363Sborman 	char	*t_name;	/* name */
4144363Sborman 	char	**t_aliases;	/* alias list */
4244363Sborman 	char	*t_proto;	/* protocol */
4344363Sborman 	int	t_tos;		/* Type Of Service bits */
4444363Sborman };
4544363Sborman 
4644363Sborman struct tosent *
4744363Sborman gettosbyname(name, proto)
4844363Sborman char *name, *proto;
4944363Sborman {
5044363Sborman 	static struct tosent te;
5144363Sborman 	static char *aliasp = 0;
5244363Sborman 
5344363Sborman 	te.t_name = name;
5444363Sborman 	te.t_aliases = &aliasp;
5544363Sborman 	te.t_proto = proto;
5644363Sborman 	te.t_tos = 020;	/* Low Delay bit */
5744363Sborman 	return(&te);
5844363Sborman }
5944363Sborman #endif
6044363Sborman 
6138904Sborman main(argc, argv)
6238904Sborman 	char *argv[];
6338904Sborman {
6438904Sborman 	struct sockaddr_in from;
6538904Sborman 	int on = 1, fromlen;
6644363Sborman #if	defined(HAS_IP_TOS) || defined(NEED_GETTOS)
6740242Sborman 	struct tosent *tp;
6844363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */
696002Sroot 
7038904Sborman 	pfrontp = pbackp = ptyobuf;
7138904Sborman 	netip = netibuf;
7238904Sborman 	nfrontp = nbackp = netobuf;
736002Sroot 
7438904Sborman 	progname = *argv;
7540242Sborman 
7640242Sborman #ifdef CRAY
7740242Sborman 	/*
7840242Sborman 	 * Get number of pty's before trying to process options,
7940242Sborman 	 * which may include changing pty range.
8040242Sborman 	 */
8140242Sborman 	highpty = getnpty();
8240242Sborman #endif /* CRAY */
8340242Sborman 
8438904Sborman top:
8538904Sborman 	argc--, argv++;
86*45234Sborman #ifdef convex
87*45234Sborman 	if (argc == 1 && !debug)
88*45234Sborman 		argc--;			/* ignore the host/port name */
89*45234Sborman #endif
9027649Sminshall 
9138904Sborman 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
9238904Sborman 		debug++;
9338904Sborman 		goto top;
9438904Sborman 	}
9527649Sminshall 
9638904Sborman #ifdef	LINEMODE
9738904Sborman 	if (argc > 0 && !strcmp(*argv, "-l")) {
9838904Sborman 		alwayslinemode = 1;
9938904Sborman 		goto top;
10038904Sborman 	}
10138904Sborman #endif	/* LINEMODE */
10227649Sminshall 
10338904Sborman 	if (argc > 0 && !strcmp(*argv, "-h")) {
10438904Sborman 		hostinfo = 0;
10538904Sborman 		goto top;
10638904Sborman 	}
10727649Sminshall 
108*45234Sborman #ifdef CRAY
10938904Sborman 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
11038904Sborman 		char *strchr();
11138904Sborman 		char *c;
11227649Sminshall 
11340242Sborman 		/*
11440242Sborman 		 * Allow the specification of alterations to the pty search
11540242Sborman 		 * range.  It is legal to specify only one, and not change the
11640242Sborman 		 * other from its default.
11740242Sborman 		 */
11838904Sborman 		*argv += 2;
11938904Sborman 		if (**argv == '\0' && argc)
12038904Sborman 			argv++, argc--;
12138904Sborman 		c = strchr(*argv, '-');
12238904Sborman 		if (c) {
12338904Sborman 			*c++ = '\0';
12438904Sborman 			highpty = atoi(c);
12540242Sborman 		}
12640242Sborman 		if (**argv != '\0')
12740242Sborman 			lowpty = atoi(*argv);
12840242Sborman 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) {
12944363Sborman 			usage();
13044363Sborman 			/* NOT REACHED */
13138904Sborman 		}
13238904Sborman 		goto top;
13338904Sborman 	}
13438904Sborman # ifdef	NEWINIT
13538904Sborman 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
13638904Sborman 		extern char *gen_id;
13727649Sminshall 
13838904Sborman 		*argv += 2;
13938904Sborman 		if (**argv == '\0') {
14044363Sborman 			if (argc < 2) {
14144363Sborman 				usage();
14244363Sborman 				/* NOT REACHED */
14344363Sborman 			}
14438904Sborman 			argv++, argc--;
14544363Sborman 			if (**argv == '\0') {
14644363Sborman 				usage();
14744363Sborman 				/* NOT REACHED */
14844363Sborman 			}
14938904Sborman 		}
15038904Sborman 		gen_id = *argv;
15138904Sborman 		goto top;
15238904Sborman 	}
15338904Sborman # endif	/* NEWINIT */
15438904Sborman #endif	/* CRAY */
1556002Sroot 
15644363Sborman #ifdef DIAGNOSTICS
15744363Sborman 	/*
15844363Sborman 	 * Check for desired diagnostics capabilities.
15944363Sborman 	 */
16044363Sborman 	if (argc > 0 && !strncmp(*argv, "-D", 2)) {
16144363Sborman 		*argv += 2;
16244363Sborman 		if (**argv == '\0') {
16344363Sborman 			if (argc < 2) {
16444363Sborman 				usage();
16544363Sborman 				/* NOT REACHED */
16644363Sborman 			}
16744363Sborman 			argv++, argc--;
16844363Sborman 			if (**argv == '\0') {
16944363Sborman 				usage();
17044363Sborman 				/* NOT REACHED */
17144363Sborman 			}
17244363Sborman 		}
17344363Sborman 		if (!strcmp(*argv, "report")) {
17444363Sborman 			diagnostic |= TD_REPORT|TD_OPTIONS;
17544363Sborman 		} else if (!strcmp(*argv, "exercise")) {
17644363Sborman 			diagnostic |= TD_EXERCISE;
17744363Sborman 		} else if (!strcmp(*argv, "netdata")) {
17844363Sborman 			diagnostic |= TD_NETDATA;
17944363Sborman 		} else if (!strcmp(*argv, "ptydata")) {
18044363Sborman 			diagnostic |= TD_PTYDATA;
18144363Sborman 		} else if (!strcmp(*argv, "options")) {
18244363Sborman 			diagnostic |= TD_OPTIONS;
18344363Sborman 		} else {
18444363Sborman 			usage();
18544363Sborman 			/* NOT REACHED */
18644363Sborman 		}
18744363Sborman 		goto top;
18844363Sborman 	}
18944363Sborman #endif /* DIAGNOSTICS */
19044363Sborman 
19144363Sborman #ifdef BFTPDAEMON
19244363Sborman 	/*
19344363Sborman 	 * Check for bftp daemon
19444363Sborman 	 */
19544363Sborman 	if (argc > 0 && !strncmp(*argv, "-B", 2)) {
19644363Sborman 		bftpd++;
19744363Sborman 		goto top;
19844363Sborman 	}
19944363Sborman #endif /* BFTPDAEMON */
20044363Sborman 
20144363Sborman 	if (argc > 0 && **argv == '-') {
20244363Sborman 		fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1);
20344363Sborman 		usage();
20444363Sborman 		/* NOT REACHED */
20544363Sborman 	}
20644363Sborman 
20738904Sborman 	if (debug) {
20827185Sminshall 	    int s, ns, foo;
20927185Sminshall 	    struct servent *sp;
21027185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
21127185Sminshall 
21244363Sborman 	    if (argc > 1) {
21344363Sborman 		usage();
21444363Sborman 		/* NOT REACHED */
21544363Sborman 	    } else if (argc == 1) {
21638904Sborman 		    if (sp = getservbyname(*argv, "tcp")) {
21738904Sborman 			sin.sin_port = sp->s_port;
21838904Sborman 		    } else {
21938904Sborman 			sin.sin_port = atoi(*argv);
22038904Sborman 			if ((int)sin.sin_port <= 0) {
22138904Sborman 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
22244363Sborman 			    usage();
22344363Sborman 			    /* NOT REACHED */
22438904Sborman 			}
22538904Sborman 			sin.sin_port = htons((u_short)sin.sin_port);
22638904Sborman 		   }
22737210Sminshall 	    } else {
22837210Sminshall 		sp = getservbyname("telnet", "tcp");
22937210Sminshall 		if (sp == 0) {
23044363Sborman 		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
23138904Sborman 		    exit(1);
23237210Sminshall 		}
23337210Sminshall 		sin.sin_port = sp->s_port;
23427185Sminshall 	    }
23527185Sminshall 
23627185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
23727185Sminshall 	    if (s < 0) {
23827185Sminshall 		    perror("telnetd: socket");;
23927185Sminshall 		    exit(1);
24027185Sminshall 	    }
24138904Sborman 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
24238904Sborman 	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
24327185Sminshall 		perror("bind");
24427185Sminshall 		exit(1);
24527185Sminshall 	    }
24627185Sminshall 	    if (listen(s, 1) < 0) {
24727185Sminshall 		perror("listen");
24827185Sminshall 		exit(1);
24927185Sminshall 	    }
25027185Sminshall 	    foo = sizeof sin;
25138904Sborman 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
25227185Sminshall 	    if (ns < 0) {
25327185Sminshall 		perror("accept");
25427185Sminshall 		exit(1);
25527185Sminshall 	    }
25638904Sborman 	    (void) dup2(ns, 0);
25738904Sborman 	    (void) close(ns);
25838904Sborman 	    (void) close(s);
25944363Sborman 	} else if (argc > 0) {
26044363Sborman 		usage();
26144363Sborman 		/* NOT REACHED */
26227185Sminshall 	}
26338904Sborman 
26424855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
26516371Skarels 	fromlen = sizeof (from);
26638904Sborman 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
26738904Sborman 		fprintf(stderr, "%s: ", progname);
26816371Skarels 		perror("getpeername");
26916371Skarels 		_exit(1);
2708346Ssam 	}
27117156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
27217187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
27310418Ssam 	}
27440242Sborman 
27544363Sborman #if	defined(HAS_IP_TOS) || defined(NEED_GETTOS)
27644363Sborman 	if ((tp = gettosbyname("telnet", "tcp")) &&
27744363Sborman 	    (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
27840242Sborman 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
27944363Sborman #endif	/* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */
28038904Sborman 	net = 0;
28138904Sborman 	doit(&from);
28238904Sborman 	/* NOTREACHED */
28338904Sborman }  /* end of main */
2846002Sroot 
28544363Sborman usage()
28644363Sborman {
28744363Sborman 	fprintf(stderr, "Usage: telnetd [-debug] [-h]");
28844363Sborman #ifdef	NEWINIT
28944363Sborman 	fprintf(stderr, " [-Iinitid]");
29044363Sborman #endif	/* NEWINIT */
29144363Sborman #ifdef DIAGNOSTICS
29244363Sborman 	fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]");
29344363Sborman #endif /* DIAGNOSTICS */
29444363Sborman #ifdef LINEMODE
29544363Sborman 	fprintf(stderr, " [-l]");
29644363Sborman #endif
29744363Sborman #ifdef	CRAY
29844363Sborman 	fprintf(stderr, " [-r[lowpty]-[highpty]]");
29944363Sborman #endif
30044363Sborman #ifdef BFTPDAEMON
30144363Sborman 	fprintf(stderr, " [-B]");
30244363Sborman #endif /* BFTPDAEMON */
30344363Sborman 	fprintf(stderr, " [port]\n");
30444363Sborman 	exit(1);
30544363Sborman }
30644363Sborman 
30740242Sborman void	cleanup();
30827649Sminshall 
30927649Sminshall /*
31027983Sminshall  * getterminaltype
31127649Sminshall  *
31238904Sborman  *	Ask the other end to send along its terminal type and speed.
31327983Sminshall  * Output is the variable terminaltype filled in.
31427649Sminshall  */
31538904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
31627983Sminshall void
31727983Sminshall getterminaltype()
31827649Sminshall {
31938904Sborman     void ttloop();
32027649Sminshall 
32138904Sborman     settimer(baseline);
32239503Sborman     send_do(TELOPT_TTYPE, 1);
32339503Sborman     send_do(TELOPT_TSPEED, 1);
32444363Sborman     send_do(TELOPT_XDISPLOC, 1);
32544363Sborman     send_do(TELOPT_ENVIRON, 1);
32644363Sborman     while (his_will_wont_is_changing(TELOPT_TTYPE) ||
32744363Sborman 	   his_will_wont_is_changing(TELOPT_TSPEED) ||
32844363Sborman 	   his_will_wont_is_changing(TELOPT_XDISPLOC) ||
32944363Sborman 	   his_will_wont_is_changing(TELOPT_ENVIRON)) {
33027983Sminshall 	ttloop();
33127649Sminshall     }
33244363Sborman     if (his_state_is_will(TELOPT_TSPEED)) {
33338904Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
33427983Sminshall 
33527983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
33627983Sminshall 	nfrontp += sizeof sbbuf;
33738904Sborman     }
33844363Sborman     if (his_state_is_will(TELOPT_XDISPLOC)) {
33944363Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
34038904Sborman 
34144363Sborman 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
34244363Sborman 	nfrontp += sizeof sbbuf;
34344363Sborman     }
34444363Sborman     if (his_state_is_will(TELOPT_ENVIRON)) {
34544363Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE };
34644363Sborman 
34744363Sborman 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
34844363Sborman 	nfrontp += sizeof sbbuf;
34944363Sborman     }
35044363Sborman     if (his_state_is_will(TELOPT_TTYPE)) {
35144363Sborman 
35238904Sborman 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
35338904Sborman 	nfrontp += sizeof ttytype_sbbuf;
35438904Sborman     }
35544363Sborman     if (his_state_is_will(TELOPT_TSPEED)) {
35638904Sborman 	while (sequenceIs(tspeedsubopt, baseline))
35727983Sminshall 	    ttloop();
35838904Sborman     }
35944363Sborman     if (his_state_is_will(TELOPT_XDISPLOC)) {
36044363Sborman 	while (sequenceIs(xdisplocsubopt, baseline))
36144363Sborman 	    ttloop();
36244363Sborman     }
36344363Sborman     if (his_state_is_will(TELOPT_ENVIRON)) {
36444363Sborman 	while (sequenceIs(environsubopt, baseline))
36544363Sborman 	    ttloop();
36644363Sborman     }
36744363Sborman     if (his_state_is_will(TELOPT_TTYPE)) {
36838904Sborman 	char first[256], last[256];
36938904Sborman 
37038904Sborman 	while (sequenceIs(ttypesubopt, baseline))
37138904Sborman 	    ttloop();
37238904Sborman 
37344363Sborman 	/*
37444363Sborman 	 * If the other side has already disabled the option, then
37544363Sborman 	 * we have to just go with what we (might) have already gotten.
37644363Sborman 	 */
37744363Sborman 	if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
37838904Sborman 	    (void) strncpy(first, terminaltype, sizeof(first));
37938904Sborman 	    for(;;) {
38038904Sborman 		/*
38138904Sborman 		 * Save the unknown name, and request the next name.
38238904Sborman 		 */
38338904Sborman 		(void) strncpy(last, terminaltype, sizeof(last));
38438904Sborman 		_gettermname();
38544363Sborman 		if (terminaltypeok(terminaltype))
38638904Sborman 		    break;
38744363Sborman 		if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
38844363Sborman 		    his_state_is_wont(TELOPT_TTYPE)) {
38938904Sborman 		    /*
39038904Sborman 		     * We've hit the end.  If this is the same as
39138904Sborman 		     * the first name, just go with it.
39238904Sborman 		     */
393*45234Sborman 		    if (strncmp(first, terminaltype, sizeof(first)) == 0)
39438904Sborman 			break;
39538904Sborman 		    /*
39644363Sborman 		     * Get the terminal name one more time, so that
39738904Sborman 		     * RFC1091 compliant telnets will cycle back to
39838904Sborman 		     * the start of the list.
39938904Sborman 		     */
40044363Sborman 		     _gettermname();
401*45234Sborman 		    if (strncmp(first, terminaltype, sizeof(first)) != 0)
40238904Sborman 			(void) strncpy(terminaltype, first, sizeof(first));
40338904Sborman 		    break;
40438904Sborman 		}
40538904Sborman 	    }
40627983Sminshall 	}
40727983Sminshall     }
40838904Sborman }  /* end of getterminaltype */
40938904Sborman 
41038904Sborman _gettermname()
41138904Sborman {
41244363Sborman     /*
41344363Sborman      * If the client turned off the option,
41444363Sborman      * we can't send another request, so we
41544363Sborman      * just return.
41644363Sborman      */
41744363Sborman     if (his_state_is_wont(TELOPT_TTYPE))
41844363Sborman 	return;
41938904Sborman     settimer(baseline);
42038904Sborman     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
42138904Sborman     nfrontp += sizeof ttytype_sbbuf;
42238904Sborman     while (sequenceIs(ttypesubopt, baseline))
42338904Sborman 	ttloop();
42427649Sminshall }
42527649Sminshall 
42638904Sborman terminaltypeok(s)
42738904Sborman char *s;
42838904Sborman {
42938904Sborman     char buf[1024];
43038904Sborman 
43138904Sborman     if (terminaltype == NULL)
43238904Sborman 	return(1);
43338904Sborman 
43438904Sborman     /*
43538904Sborman      * tgetent() will return 1 if the type is known, and
43638904Sborman      * 0 if it is not known.  If it returns -1, it couldn't
43738904Sborman      * open the database.  But if we can't open the database,
43838904Sborman      * it won't help to say we failed, because we won't be
43938904Sborman      * able to verify anything else.  So, we treat -1 like 1.
44038904Sborman      */
44138904Sborman     if (tgetent(buf, s) == 0)
44238904Sborman 	return(0);
44338904Sborman     return(1);
44438904Sborman }
44538904Sborman 
4466002Sroot /*
4476002Sroot  * Get a pty, scan input lines.
4486002Sroot  */
44938904Sborman doit(who)
45012683Ssam 	struct sockaddr_in *who;
4516002Sroot {
45220188Skarels 	char *host, *inet_ntoa();
45338904Sborman 	int t;
45412683Ssam 	struct hostent *hp;
4556002Sroot 
45638904Sborman 	/*
45738904Sborman 	 * Find an available pty to use.
45838904Sborman 	 */
459*45234Sborman #ifndef	convex
46038904Sborman 	pty = getpty();
46138904Sborman 	if (pty < 0)
46238904Sborman 		fatal(net, "All network ports in use");
463*45234Sborman #else
464*45234Sborman 	for (;;) {
465*45234Sborman 		char *lp;
466*45234Sborman 		extern char *line, *getpty();
46720188Skarels 
468*45234Sborman 		if ((lp = getpty()) == NULL)
469*45234Sborman 			fatal(net, "Out of ptys");
470*45234Sborman 
471*45234Sborman 		if ((pty = open(lp, 2)) >= 0) {
472*45234Sborman 			strcpy(line,lp);
473*45234Sborman 			line[5] = 't';
474*45234Sborman 			break;
475*45234Sborman 		}
476*45234Sborman 	}
47744545Smarc #endif
47838904Sborman 
47938904Sborman 	/* get name of connected client */
48038904Sborman 	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
48112683Ssam 		who->sin_family);
48212683Ssam 	if (hp)
48312683Ssam 		host = hp->h_name;
48412683Ssam 	else
48517444Sralph 		host = inet_ntoa(who->sin_addr);
48627983Sminshall 
48744363Sborman 	init_env();
48827983Sminshall 	/*
48938904Sborman 	 * get terminal type.
49027983Sminshall 	 */
49127983Sminshall 	getterminaltype();
49244363Sborman 	setenv("TERM", terminaltype ? terminaltype : "network", 1);
49327983Sminshall 
49427649Sminshall 	/*
49538904Sborman 	 * Start up the login process on the slave side of the terminal
49627649Sminshall 	 */
497*45234Sborman #ifndef	convex
498*45234Sborman 	startslave(host);
49938904Sborman 
50038904Sborman 	telnet(net, pty);  /* begin server processing */
501*45234Sborman #else
502*45234Sborman 	telnet(net, pty, host);
503*45234Sborman #endif
5049244Ssam 	/*NOTREACHED*/
50538904Sborman }  /* end of doit */
5069244Ssam 
50738904Sborman #ifndef	MAXHOSTNAMELEN
50838904Sborman #define	MAXHOSTNAMELEN 64
50938904Sborman #endif	MAXHOSTNAMELEN
5106002Sroot /*
5116002Sroot  * Main loop.  Select from pty and network, and
5126002Sroot  * hand data to telnet receiver finite state machine.
5136002Sroot  */
514*45234Sborman #ifndef	convex
5156002Sroot telnet(f, p)
516*45234Sborman #else
517*45234Sborman telnet(f, p, host)
518*45234Sborman #endif
51938904Sborman int f, p;
520*45234Sborman #ifdef convex
521*45234Sborman char *host;
522*45234Sborman #endif
5236002Sroot {
5246002Sroot 	int on = 1;
52527898Skarels 	char hostname[MAXHOSTNAMELEN];
52640242Sborman #if	defined(CRAY2) && defined(UNICOS5)
52738904Sborman 	int termstat();
52838904Sborman 	int interrupt(), sendbrk();
52938904Sborman #endif
53033271Sminshall #define	TABBUFSIZ	512
53133271Sminshall 	char	defent[TABBUFSIZ];
53233271Sminshall 	char	defstrs[TABBUFSIZ];
53333271Sminshall #undef	TABBUFSIZ
53433271Sminshall 	char *HE;
53533271Sminshall 	char *HN;
53633271Sminshall 	char *IM;
53738904Sborman 	void netflush();
53838904Sborman 
53932400Sminshall 	/*
54038904Sborman 	 * Initialize the slc mapping table.
54132400Sminshall 	 */
54238904Sborman 	get_slc_defaults();
5436002Sroot 
5448379Ssam 	/*
54538904Sborman 	 * Do some tests where it is desireable to wait for a response.
54638904Sborman 	 * Rather than doing them slowly, one at a time, do them all
54738904Sborman 	 * at once.
5488379Ssam 	 */
54944363Sborman 	if (my_state_is_wont(TELOPT_SGA))
55039503Sborman 		send_will(TELOPT_SGA, 1);
55112713Ssam 	/*
55227649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
55327649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
55427649Sminshall 	 *
55527649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
55627649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
55727649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
55827649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
55927649Sminshall 	 * echo the terminal input).
56027649Sminshall 	 */
56139503Sborman 	send_do(TELOPT_ECHO, 1);
56227649Sminshall 
56338904Sborman #ifdef	LINEMODE
56444363Sborman 	if (his_state_is_wont(TELOPT_LINEMODE)) {
56538904Sborman 		/* Query the peer for linemode support by trying to negotiate
56638904Sborman 		 * the linemode option.
56738904Sborman 		 */
56844363Sborman 		linemode = 0;
56938904Sborman 		editmode = 0;
57039503Sborman 		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
57138904Sborman 	}
57238904Sborman #endif	/* LINEMODE */
57338904Sborman 
57427649Sminshall 	/*
57538904Sborman 	 * Send along a couple of other options that we wish to negotiate.
57638904Sborman 	 */
57739503Sborman 	send_do(TELOPT_NAWS, 1);
57839503Sborman 	send_will(TELOPT_STATUS, 1);
57938904Sborman 	flowmode = 1;  /* default flow control state */
58039503Sborman 	send_do(TELOPT_LFLOW, 1);
58138904Sborman 
58238904Sborman 	/*
58338904Sborman 	 * Spin, waiting for a response from the DO ECHO.  However,
58438904Sborman 	 * some REALLY DUMB telnets out there might not respond
58538904Sborman 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
58638904Sborman 	 * telnets so far seem to respond with WONT for a DO that
58738904Sborman 	 * they don't understand...) because by the time we get the
58838904Sborman 	 * response, it will already have processed the DO ECHO.
58938904Sborman 	 * Kludge upon kludge.
59038904Sborman 	 */
59144363Sborman 	while (his_will_wont_is_changing(TELOPT_NAWS))
59238904Sborman 		ttloop();
59338904Sborman 
59438904Sborman 	/*
59544363Sborman 	 * But...
59644363Sborman 	 * The client might have sent a WILL NAWS as part of its
59744363Sborman 	 * startup code; if so, we'll be here before we get the
59844363Sborman 	 * response to the DO ECHO.  We'll make the assumption
59944363Sborman 	 * that any implementation that understands about NAWS
60044363Sborman 	 * is a modern enough implementation that it will respond
60144363Sborman 	 * to our DO ECHO request; hence we'll do another spin
60244363Sborman 	 * waiting for the ECHO option to settle down, which is
60344363Sborman 	 * what we wanted to do in the first place...
60444363Sborman 	 */
60544363Sborman 	if (his_want_state_is_will(TELOPT_ECHO) &&
60644363Sborman 	    his_state_is_will(TELOPT_NAWS)) {
60744363Sborman 		while (his_will_wont_is_changing(TELOPT_ECHO))
60844363Sborman 			ttloop();
60944363Sborman 	}
61044363Sborman 	/*
61138995Sborman 	 * On the off chance that the telnet client is broken and does not
61238995Sborman 	 * respond to the DO ECHO we sent, (after all, we did send the
61338995Sborman 	 * DO NAWS negotiation after the DO ECHO, and we won't get here
61438995Sborman 	 * until a response to the DO NAWS comes back) simulate the
61538995Sborman 	 * receipt of a will echo.  This will also send a WONT ECHO
61638995Sborman 	 * to the client, since we assume that the client failed to
61738995Sborman 	 * respond because it believes that it is already in DO ECHO
61838995Sborman 	 * mode, which we do not want.
61938995Sborman 	 */
62044363Sborman 	if (his_want_state_is_will(TELOPT_ECHO)) {
62144363Sborman #ifdef DIAGNOSTICS
62244363Sborman 		if (diagnostic & TD_OPTIONS) {
62344363Sborman 			sprintf(nfrontp, "td: simulating recv\r\n");
62444363Sborman 			nfrontp += strlen(nfrontp);
62544363Sborman 		}
62644363Sborman #endif /* DIAGNOSTICS */
62739503Sborman 		willoption(TELOPT_ECHO);
62840242Sborman 	}
62938995Sborman 
63038995Sborman 	/*
63138995Sborman 	 * Finally, to clean things up, we turn on our echo.  This
63238995Sborman 	 * will break stupid 4.2 telnets out of local terminal echo.
63338995Sborman 	 */
63438995Sborman 
63544363Sborman 	if (my_state_is_wont(TELOPT_ECHO))
63639503Sborman 		send_will(TELOPT_ECHO, 1);
63738995Sborman 
63838995Sborman 	/*
639*45234Sborman 	 * Turn on packet mode
64038904Sborman 	 */
64138904Sborman 	(void) ioctl(p, TIOCPKT, (char *)&on);
642*45234Sborman #ifdef	KLUDGELINEMODE
64338904Sborman 	/*
64438904Sborman 	 * Continuing line mode support.  If client does not support
64538904Sborman 	 * real linemode, attempt to negotiate kludge linemode by sending
64638904Sborman 	 * the do timing mark sequence.
64738904Sborman 	 */
64838904Sborman 	if (lmodetype < REAL_LINEMODE)
64939503Sborman 		send_do(TELOPT_TM, 1);
650*45234Sborman #endif	/* KLUDGELINEMODE */
65138904Sborman 
65238904Sborman 	/*
65338904Sborman 	 * Call telrcv() once to pick up anything received during
65438904Sborman 	 * terminal type negotiation, 4.2/4.3 determination, and
65538904Sborman 	 * linemode negotiation.
65638904Sborman 	 */
65738904Sborman 	telrcv();
65838904Sborman 
65938904Sborman 	(void) ioctl(f, FIONBIO, (char *)&on);
66038904Sborman 	(void) ioctl(p, FIONBIO, (char *)&on);
66140242Sborman #if	defined(CRAY2) && defined(UNICOS5)
66238904Sborman 	init_termdriver(f, p, interrupt, sendbrk);
66338904Sborman #endif
66438904Sborman 
66538904Sborman #if	defined(SO_OOBINLINE)
66638904Sborman 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
66738904Sborman #endif	/* defined(SO_OOBINLINE) */
66838904Sborman 
66938904Sborman #ifdef	SIGTSTP
67038904Sborman 	(void) signal(SIGTSTP, SIG_IGN);
67138904Sborman #endif
67238904Sborman #ifdef	SIGTTOU
67338904Sborman 	/*
67438904Sborman 	 * Ignoring SIGTTOU keeps the kernel from blocking us
67538904Sborman 	 * in ttioct() in /sys/tty.c.
67638904Sborman 	 */
67738904Sborman 	(void) signal(SIGTTOU, SIG_IGN);
67838904Sborman #endif
67938904Sborman 
68038904Sborman 	(void) signal(SIGCHLD, cleanup);
68138904Sborman 
68240242Sborman #if	defined(CRAY2) && defined(UNICOS5)
68338904Sborman 	/*
68438904Sborman 	 * Cray-2 will send a signal when pty modes are changed by slave
68538904Sborman 	 * side.  Set up signal handler now.
68638904Sborman 	 */
68738904Sborman 	if ((int)signal(SIGUSR1, termstat) < 0)
68838904Sborman 		perror("signal");
68938904Sborman 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
69038904Sborman 		perror("ioctl:TCSIGME");
69138904Sborman 	/*
69238904Sborman 	 * Make processing loop check terminal characteristics early on.
69338904Sborman 	 */
69438904Sborman 	termstat();
69538904Sborman #endif
69638904Sborman 
697*45234Sborman #ifdef  TIOCNOTTY
698*45234Sborman 	{
699*45234Sborman 		register int t;
700*45234Sborman 		t = open(_PATH_TTY, O_RDWR);
701*45234Sborman 		if (t >= 0) {
702*45234Sborman 			(void) ioctl(t, TIOCNOTTY, (char *)0);
703*45234Sborman 			(void) close(t);
704*45234Sborman 		}
705*45234Sborman 	}
70640242Sborman #endif
707*45234Sborman 
70844363Sborman #if	defined(TIOCSCTTY) && defined(CRAY)
709*45234Sborman 	(void) setsid();
71044363Sborman 	ioctl(p, TIOCSCTTY, 0);
71144363Sborman #endif
71238904Sborman 
71338904Sborman 	/*
71412713Ssam 	 * Show banner that getty never gave.
71527797Sminshall 	 *
71633271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
71733271Sminshall 	 * gets carriage return null processing, etc., just like all
71833271Sminshall 	 * other pty --> client data.
71912713Ssam 	 */
72027797Sminshall 
721*45234Sborman #if	!defined(CRAY) || !defined(NEWINIT)
722*45234Sborman 	if (getenv("USER"))
723*45234Sborman 		hostinfo = 0;
724*45234Sborman #endif
72538904Sborman 	(void) gethostname(hostname, sizeof (hostname));
72638904Sborman 
72733271Sminshall 	if (getent(defent, "default") == 1) {
72833271Sminshall 		char *getstr();
72938904Sborman 		char *cp=defstrs;
73027649Sminshall 
73138904Sborman 		HE = getstr("he", &cp);
73238904Sborman 		HN = getstr("hn", &cp);
73338904Sborman 		IM = getstr("im", &cp);
73433271Sminshall 		if (HN && *HN)
73538904Sborman 			(void) strcpy(hostname, HN);
73638904Sborman 		if (IM == 0)
73738904Sborman 			IM = "";
73833271Sminshall 	} else {
739*45234Sborman 		IM = DEFAULT_IM;
74038904Sborman 		HE = 0;
74133271Sminshall 	}
74238904Sborman 	edithost(HE, hostname);
743*45234Sborman 	if (hostinfo && *IM)
74438904Sborman 		putf(IM, ptyibuf2);
74527797Sminshall 
74638904Sborman 	if (pcc)
74738904Sborman 		(void) strncat(ptyibuf2, ptyip, pcc+1);
74838904Sborman 	ptyip = ptyibuf2;
74938904Sborman 	pcc = strlen(ptyip);
75040242Sborman #ifdef	LINEMODE
75140242Sborman 	/*
75240242Sborman 	 * Last check to make sure all our states are correct.
75340242Sborman 	 */
75440242Sborman 	init_termbuf();
75540242Sborman 	localstat();
75640242Sborman #endif	/* LINEMODE */
75733271Sminshall 
75844363Sborman #ifdef DIAGNOSTICS
75944363Sborman 	if (diagnostic & TD_REPORT) {
76044363Sborman 		sprintf(nfrontp, "td: Entering processing loop\r\n");
76144363Sborman 		nfrontp += strlen(nfrontp);
76244363Sborman 	}
76344363Sborman #endif /* DIAGNOSTICS */
76444363Sborman 
765*45234Sborman #ifdef	convex
766*45234Sborman 	startslave(host);
767*45234Sborman #endif
768*45234Sborman 
7696002Sroot 	for (;;) {
77027185Sminshall 		fd_set ibits, obits, xbits;
7716002Sroot 		register int c;
7726002Sroot 
77327185Sminshall 		if (ncc < 0 && pcc < 0)
77427185Sminshall 			break;
77527185Sminshall 
77640242Sborman #if	defined(CRAY2) && defined(UNICOS5)
77738904Sborman 		if (needtermstat)
77838904Sborman 			_termstat();
77940242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
78027185Sminshall 		FD_ZERO(&ibits);
78127185Sminshall 		FD_ZERO(&obits);
78227185Sminshall 		FD_ZERO(&xbits);
7836002Sroot 		/*
7846002Sroot 		 * Never look for input if there's still
7856002Sroot 		 * stuff in the corresponding output buffer
7866002Sroot 		 */
78727185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
78827185Sminshall 			FD_SET(f, &obits);
78927185Sminshall 		} else {
79027185Sminshall 			FD_SET(p, &ibits);
79127185Sminshall 		}
79227185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
79327185Sminshall 			FD_SET(p, &obits);
79427185Sminshall 		} else {
79527185Sminshall 			FD_SET(f, &ibits);
79627185Sminshall 		}
79727185Sminshall 		if (!SYNCHing) {
79827185Sminshall 			FD_SET(f, &xbits);
79927185Sminshall 		}
80027185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
80127185Sminshall 						(struct timeval *)0)) < 1) {
80227185Sminshall 			if (c == -1) {
80327185Sminshall 				if (errno == EINTR) {
80427185Sminshall 					continue;
80527185Sminshall 				}
80627185Sminshall 			}
8076002Sroot 			sleep(5);
8086002Sroot 			continue;
8096002Sroot 		}
8106002Sroot 
8116002Sroot 		/*
81227185Sminshall 		 * Any urgent data?
81327185Sminshall 		 */
81427185Sminshall 		if (FD_ISSET(net, &xbits)) {
81527185Sminshall 		    SYNCHing = 1;
81627185Sminshall 		}
81727185Sminshall 
81827185Sminshall 		/*
8196002Sroot 		 * Something to read from the network...
8206002Sroot 		 */
82127185Sminshall 		if (FD_ISSET(net, &ibits)) {
82227649Sminshall #if	!defined(SO_OOBINLINE)
82327185Sminshall 			/*
82427898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
82527185Sminshall 			 * OOB indication and data handling in the kernel
82627185Sminshall 			 * is such that if two separate TCP Urgent requests
82727185Sminshall 			 * come in, one byte of TCP data will be overlaid.
82827185Sminshall 			 * This is fatal for Telnet, but we try to live
82927185Sminshall 			 * with it.
83027185Sminshall 			 *
83127185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
83227185Sminshall 			 * is needed to pick up the TCP Urgent data in
83327185Sminshall 			 * the correct sequence.
83427185Sminshall 			 *
83527185Sminshall 			 * What we do is:  if we think we are in urgent
83627185Sminshall 			 * mode, we look to see if we are "at the mark".
83727185Sminshall 			 * If we are, we do an OOB receive.  If we run
83827185Sminshall 			 * this twice, we will do the OOB receive twice,
83927185Sminshall 			 * but the second will fail, since the second
84027185Sminshall 			 * time we were "at the mark", but there wasn't
84127185Sminshall 			 * any data there (the kernel doesn't reset
84227185Sminshall 			 * "at the mark" until we do a normal read).
84327185Sminshall 			 * Once we've read the OOB data, we go ahead
84427185Sminshall 			 * and do normal reads.
84527185Sminshall 			 *
84627185Sminshall 			 * There is also another problem, which is that
84727185Sminshall 			 * since the OOB byte we read doesn't put us
84827185Sminshall 			 * out of OOB state, and since that byte is most
84927185Sminshall 			 * likely the TELNET DM (data mark), we would
85027185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
85127185Sminshall 			 * So, clocks to the rescue.  If we've "just"
85227185Sminshall 			 * received a DM, then we test for the
85327185Sminshall 			 * presence of OOB data when the receive OOB
85427185Sminshall 			 * fails (and AFTER we did the normal mode read
85527185Sminshall 			 * to clear "at the mark").
85627185Sminshall 			 */
85727185Sminshall 		    if (SYNCHing) {
85827185Sminshall 			int atmark;
85927185Sminshall 
86038904Sborman 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
86127185Sminshall 			if (atmark) {
86227185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
86327185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
86427185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
86527983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
86627185Sminshall 				    SYNCHing = stilloob(net);
86727185Sminshall 				}
86827185Sminshall 			    }
86927185Sminshall 			} else {
87027185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
8716002Sroot 			}
87227185Sminshall 		    } else {
87327185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
87427185Sminshall 		    }
87527185Sminshall 		    settimer(didnetreceive);
87627649Sminshall #else	/* !defined(SO_OOBINLINE)) */
87727185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
87827649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
87927185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
88027185Sminshall 			ncc = 0;
88127185Sminshall 		    else {
88227185Sminshall 			if (ncc <= 0) {
88327185Sminshall 			    break;
88427185Sminshall 			}
88527185Sminshall 			netip = netibuf;
88627185Sminshall 		    }
88744363Sborman #ifdef DIAGNOSTICS
88844363Sborman 		    if (diagnostic & (TD_REPORT | TD_NETDATA)) {
88944363Sborman 			    sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
89044363Sborman 			    nfrontp += strlen(nfrontp);
89144363Sborman 		    }
89244363Sborman 		    if (diagnostic & TD_NETDATA) {
89344363Sborman 			    printdata("nd", netip, ncc);
89444363Sborman 		    }
89544363Sborman #endif /* DIAGNOSTICS */
8966002Sroot 		}
8976002Sroot 
8986002Sroot 		/*
8996002Sroot 		 * Something to read from the pty...
9006002Sroot 		 */
90138904Sborman 		if (FD_ISSET(p, &ibits)) {
9026002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
9036002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
9046002Sroot 				pcc = 0;
9056002Sroot 			else {
9066002Sroot 				if (pcc <= 0)
9076002Sroot 					break;
90840242Sborman #if	!defined(CRAY2) || !defined(UNICOS5)
90938904Sborman #ifdef	LINEMODE
91038904Sborman 				/*
91138904Sborman 				 * If ioctl from pty, pass it through net
91238904Sborman 				 */
91338904Sborman 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
91438904Sborman 					copy_termbuf(ptyibuf+1, pcc-1);
91538904Sborman 					localstat();
91638904Sborman 					pcc = 1;
91738904Sborman 				}
91838904Sborman #endif	LINEMODE
91937210Sminshall 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
92038904Sborman 					netclear();	/* clear buffer back */
921*45234Sborman #ifndef	NO_URGENT
92240242Sborman 					/*
923*45234Sborman 					 * There are client telnets on some
92440242Sborman 					 * operating systems get screwed up
92540242Sborman 					 * royally if we send them urgent
926*45234Sborman 					 * mode data.
92740242Sborman 					 */
92837210Sminshall 					*nfrontp++ = IAC;
92937210Sminshall 					*nfrontp++ = DM;
93037210Sminshall 					neturg = nfrontp-1; /* off by one XXX */
93140242Sborman #endif
93237210Sminshall 				}
93344363Sborman 				if (his_state_is_will(TELOPT_LFLOW) &&
93437210Sminshall 				    (ptyibuf[0] &
93538904Sborman 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
93638904Sborman 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
93737210Sminshall 					    IAC, SB, TELOPT_LFLOW,
93837210Sminshall 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
93937210Sminshall 					    IAC, SE);
94037210Sminshall 					nfrontp += 6;
94137210Sminshall 				}
94233267Sminshall 				pcc--;
94333267Sminshall 				ptyip = ptyibuf+1;
94440242Sborman #else	/* defined(CRAY2) && defined(UNICOS5) */
94538904Sborman 				if (!uselinemode) {
94639531Sborman 					unpcc = pcc;
94739531Sborman 					unptyip = ptyibuf;
94839531Sborman 					pcc = term_output(&unptyip, ptyibuf2,
94939531Sborman 								&unpcc, BUFSIZ);
95038904Sborman 					ptyip = ptyibuf2;
95138904Sborman 				} else
95238904Sborman 					ptyip = ptyibuf;
95340242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
95438904Sborman 			}
9556002Sroot 		}
9566002Sroot 
9576002Sroot 		while (pcc > 0) {
9586002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
9596002Sroot 				break;
9606002Sroot 			c = *ptyip++ & 0377, pcc--;
9616002Sroot 			if (c == IAC)
9626002Sroot 				*nfrontp++ = c;
96340242Sborman #if	defined(CRAY2) && defined(UNICOS5)
96438904Sborman 			else if (c == '\n' &&
96544363Sborman 				     my_state_is_wont(TELOPT_BINARY) && newmap)
96638904Sborman 				*nfrontp++ = '\r';
96740242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
9686002Sroot 			*nfrontp++ = c;
96944363Sborman 			if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
97027020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
97127020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
97227020Sminshall 					pcc--;
97327020Sminshall 				} else
97427020Sminshall 					*nfrontp++ = '\0';
97527020Sminshall 			}
9766002Sroot 		}
97740242Sborman #if	defined(CRAY2) && defined(UNICOS5)
97839531Sborman 		/*
97939531Sborman 		 * If chars were left over from the terminal driver,
98039531Sborman 		 * note their existence.
98139531Sborman 		 */
98239531Sborman 		 if (!uselinemode && unpcc) {
98339531Sborman 			pcc = unpcc;
98439531Sborman 			unpcc = 0;
98539531Sborman 			ptyip = unptyip;
98639531Sborman 		}
98740242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
98839531Sborman 
98927185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
9906002Sroot 			netflush();
9916002Sroot 		if (ncc > 0)
9926002Sroot 			telrcv();
99327185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
9946002Sroot 			ptyflush();
9956002Sroot 	}
9966002Sroot 	cleanup();
99738904Sborman }  /* end of telnet */
9986002Sroot 
99938904Sborman #ifndef	TCSIG
100038904Sborman # ifdef	TIOCSIG
100138904Sborman #  define TCSIG TIOCSIG
100238904Sborman # endif
100338904Sborman #endif
10046002Sroot 
100537212Sminshall /*
10066002Sroot  * Send interrupt to process on other side of pty.
10076002Sroot  * If it is in raw mode, just write NULL;
10086002Sroot  * otherwise, write intr char.
10096002Sroot  */
10106002Sroot interrupt()
10116002Sroot {
101238904Sborman 	ptyflush();	/* half-hearted */
10136002Sroot 
101438904Sborman #ifdef	TCSIG
101538904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
101638904Sborman #else	/* TCSIG */
101738904Sborman 	init_termbuf();
101840242Sborman 	*pfrontp++ = slctab[SLC_IP].sptr ?
101940242Sborman 			(unsigned char)*slctab[SLC_IP].sptr : '\177';
102038904Sborman #endif	/* TCSIG */
10216002Sroot }
10226002Sroot 
102327229Sminshall /*
102427229Sminshall  * Send quit to process on other side of pty.
102527229Sminshall  * If it is in raw mode, just write NULL;
102627229Sminshall  * otherwise, write quit char.
102727229Sminshall  */
102827229Sminshall sendbrk()
102927229Sminshall {
103027229Sminshall 	ptyflush();	/* half-hearted */
103138904Sborman #ifdef	TCSIG
103238904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
103338904Sborman #else	/* TCSIG */
103438904Sborman 	init_termbuf();
103540242Sborman 	*pfrontp++ = slctab[SLC_ABORT].sptr ?
103640242Sborman 			(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
103738904Sborman #endif	/* TCSIG */
103827229Sminshall }
103927229Sminshall 
104038904Sborman sendsusp()
10416002Sroot {
104238904Sborman #ifdef	SIGTSTP
104338904Sborman 	ptyflush();	/* half-hearted */
104438904Sborman # ifdef	TCSIG
104538904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
104638904Sborman # else	/* TCSIG */
104740242Sborman 	*pfrontp++ = slctab[SLC_SUSP].sptr ?
104840242Sborman 			(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
104938904Sborman # endif	/* TCSIG */
105038904Sborman #endif	/* SIGTSTP */
10516002Sroot }
10526002Sroot 
1053*45234Sborman /*
1054*45234Sborman  * When we get an AYT, if ^T is enabled, use that.  Otherwise,
1055*45234Sborman  * just send back "[Yes]".
1056*45234Sborman  */
1057*45234Sborman recv_ayt()
1058*45234Sborman {
1059*45234Sborman #if	defined(SIGINFO) && defined(TCSIG)
1060*45234Sborman 	if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
1061*45234Sborman 		(void) ioctl(pty, TCSIG, (char *)SIGINFO);
1062*45234Sborman 		return;
1063*45234Sborman 	}
1064*45234Sborman #endif
1065*45234Sborman 	(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
1066*45234Sborman 	nfrontp += 9;
1067*45234Sborman }
1068*45234Sborman 
106938904Sborman doeof()
10706002Sroot {
1071*45234Sborman #if	defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
107240242Sborman 	extern char oldeofc;
107340242Sborman #endif
107438904Sborman 	init_termbuf();
10756002Sroot 
1076*45234Sborman #if	defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
107740242Sborman 	if (!tty_isediting()) {
107840242Sborman 		*pfrontp++ = oldeofc;
107940242Sborman 		return;
108040242Sborman 	}
108140242Sborman #endif
108240242Sborman 	*pfrontp++ = slctab[SLC_EOF].sptr ?
108340242Sborman 			(unsigned char)*slctab[SLC_EOF].sptr : '\004';
10846002Sroot }
1085