xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 42673)
121182Sdist /*
238904Sborman  * Copyright (c) 1989 Regents of the University of California.
333687Sbostic  * All rights reserved.
433687Sbostic  *
5*42673Sbostic  * %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*42673Sbostic static char sccsid[] = "@(#)telnetd.c	5.43 (Berkeley) 06/01/90";
1633687Sbostic #endif /* not lint */
1721182Sdist 
1838904Sborman #include "telnetd.h"
1938904Sborman 
206002Sroot /*
2138904Sborman  * I/O data buffers,
2238904Sborman  * pointers, and counters.
236002Sroot  */
2438904Sborman char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
2538904Sborman char	ptyibuf2[BUFSIZ];
269218Ssam 
2738904Sborman #ifdef	CRAY
2838904Sborman int	hostinfo = 1;			/* do we print login banner? */
2938904Sborman #endif
309218Ssam 
3138904Sborman #ifdef	CRAY
3238904Sborman extern int      newmap; /* nonzero if \n maps to ^M^J */
3340242Sborman int	lowpty = 0, highpty;	/* low, high pty numbers */
3438904Sborman #endif /* CRAY */
3512216Ssam 
3638904Sborman int debug = 0;
3738904Sborman char *progname;
389218Ssam 
3940242Sborman #if	defined(IP_TOS) && defined(NEED_GETTOS)
4040242Sborman struct tosent {
4140242Sborman 	char	*t_name;	/* name */
4240242Sborman 	char	**t_aliases;	/* alias list */
4340242Sborman 	char	*t_proto;	/* protocol */
4440242Sborman 	int	t_tos;		/* Type Of Service bits */
4540242Sborman };
4640242Sborman 
4740242Sborman struct tosent *
4840242Sborman gettosbyname(name, proto)
4940242Sborman char *name, *proto;
5040242Sborman {
5140242Sborman 	static struct tosent te;
5240242Sborman 	static char *aliasp = 0;
5340242Sborman 
5440242Sborman 	te.t_name = name;
5540242Sborman 	te.t_aliases = &aliasp;
5640242Sborman 	te.t_proto = proto;
5740242Sborman 	te.t_tos = 020;	/* Low Delay bit */
5840242Sborman 	return(&te);
5940242Sborman }
6040242Sborman #endif
6140242Sborman 
6238904Sborman main(argc, argv)
6338904Sborman 	char *argv[];
6438904Sborman {
6538904Sborman 	struct sockaddr_in from;
6638904Sborman 	int on = 1, fromlen;
6740242Sborman #ifdef IP_TOS
6840242Sborman 	struct tosent *tp;
6940242Sborman #endif /* IP_TOS */
706002Sroot 
7138904Sborman 	pfrontp = pbackp = ptyobuf;
7238904Sborman 	netip = netibuf;
7338904Sborman 	nfrontp = nbackp = netobuf;
746002Sroot 
7538904Sborman 	progname = *argv;
7640242Sborman 
7740242Sborman #ifdef CRAY
7840242Sborman 	/*
7940242Sborman 	 * Get number of pty's before trying to process options,
8040242Sborman 	 * which may include changing pty range.
8140242Sborman 	 */
8240242Sborman 	highpty = getnpty();
8340242Sborman #endif /* CRAY */
8440242Sborman 
8538904Sborman top:
8638904Sborman 	argc--, argv++;
8727649Sminshall 
8838904Sborman 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
8938904Sborman 		debug++;
9038904Sborman 		goto top;
9138904Sborman 	}
9227649Sminshall 
9338904Sborman #ifdef	LINEMODE
9438904Sborman 	if (argc > 0 && !strcmp(*argv, "-l")) {
9538904Sborman 		alwayslinemode = 1;
9638904Sborman 		goto top;
9738904Sborman 	}
9838904Sborman #endif	/* LINEMODE */
9927649Sminshall 
10038904Sborman #ifdef CRAY
10138904Sborman 	if (argc > 0 && !strcmp(*argv, "-h")) {
10238904Sborman 		hostinfo = 0;
10338904Sborman 		goto top;
10438904Sborman 	}
10527649Sminshall 
10638904Sborman 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
10738904Sborman 		char *strchr();
10838904Sborman 		char *c;
10927649Sminshall 
11040242Sborman 		/*
11140242Sborman 		 * Allow the specification of alterations to the pty search
11240242Sborman 		 * range.  It is legal to specify only one, and not change the
11340242Sborman 		 * other from its default.
11440242Sborman 		 */
11538904Sborman 		*argv += 2;
11638904Sborman 		if (**argv == '\0' && argc)
11738904Sborman 			argv++, argc--;
11838904Sborman 		c = strchr(*argv, '-');
11938904Sborman 		if (c) {
12038904Sborman 			*c++ = '\0';
12138904Sborman 			highpty = atoi(c);
12240242Sborman 		}
12340242Sborman 		if (**argv != '\0')
12440242Sborman 			lowpty = atoi(*argv);
12540242Sborman 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) {
12638904Sborman 	usage:
12738904Sborman 			fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
12838904Sborman # ifdef	NEWINIT
12938904Sborman 			fprintf(stderr, "[-Iinitid] ");
13038904Sborman # endif	/* NEWINIT */
13140242Sborman 			fprintf(stderr, "[-l] [-r[lowpty]-[highpty]] [port]\n");
13238904Sborman 			exit(1);
13338904Sborman 		}
13438904Sborman 		goto top;
13538904Sborman 	}
13638904Sborman # ifdef	NEWINIT
13738904Sborman 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
13838904Sborman 		extern char *gen_id;
13927649Sminshall 
14038904Sborman 		*argv += 2;
14138904Sborman 		if (**argv == '\0') {
14238904Sborman 			if (argc < 2)
14338904Sborman 				goto usage;
14438904Sborman 			argv++, argc--;
14538904Sborman 			if (**argv == '\0')
14638904Sborman 				goto usage;
14738904Sborman 		}
14838904Sborman 		gen_id = *argv;
14938904Sborman 		goto top;
15038904Sborman 	}
15138904Sborman # endif	/* NEWINIT */
15238904Sborman #endif	/* CRAY */
1536002Sroot 
15438904Sborman 	if (debug) {
15527185Sminshall 	    int s, ns, foo;
15627185Sminshall 	    struct servent *sp;
15727185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
15827185Sminshall 
15938904Sborman 	    if (argc > 0) {
16038904Sborman 		    if (sp = getservbyname(*argv, "tcp")) {
16138904Sborman 			sin.sin_port = sp->s_port;
16238904Sborman 		    } else {
16338904Sborman 			sin.sin_port = atoi(*argv);
16438904Sborman 			if ((int)sin.sin_port <= 0) {
16538904Sborman 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
16638904Sborman 			    exit(1);
16738904Sborman 			}
16838904Sborman 			sin.sin_port = htons((u_short)sin.sin_port);
16938904Sborman 		   }
17037210Sminshall 	    } else {
17137210Sminshall 		sp = getservbyname("telnet", "tcp");
17237210Sminshall 		if (sp == 0) {
17337210Sminshall 			fprintf(stderr,
17437210Sminshall 				"telnetd: tcp/telnet: unknown service\n");
17538904Sborman 		    exit(1);
17637210Sminshall 		}
17737210Sminshall 		sin.sin_port = sp->s_port;
17827185Sminshall 	    }
17927185Sminshall 
18027185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
18127185Sminshall 	    if (s < 0) {
18227185Sminshall 		    perror("telnetd: socket");;
18327185Sminshall 		    exit(1);
18427185Sminshall 	    }
18538904Sborman 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
18638904Sborman 	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
18727185Sminshall 		perror("bind");
18827185Sminshall 		exit(1);
18927185Sminshall 	    }
19027185Sminshall 	    if (listen(s, 1) < 0) {
19127185Sminshall 		perror("listen");
19227185Sminshall 		exit(1);
19327185Sminshall 	    }
19427185Sminshall 	    foo = sizeof sin;
19538904Sborman 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
19627185Sminshall 	    if (ns < 0) {
19727185Sminshall 		perror("accept");
19827185Sminshall 		exit(1);
19927185Sminshall 	    }
20038904Sborman 	    (void) dup2(ns, 0);
20138904Sborman 	    (void) close(ns);
20238904Sborman 	    (void) close(s);
20327185Sminshall 	}
20438904Sborman 
20524855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
20616371Skarels 	fromlen = sizeof (from);
20738904Sborman 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
20838904Sborman 		fprintf(stderr, "%s: ", progname);
20916371Skarels 		perror("getpeername");
21016371Skarels 		_exit(1);
2118346Ssam 	}
21217156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
21317187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
21410418Ssam 	}
21540242Sborman 
21640242Sborman #ifdef IP_TOS
21740242Sborman 	if ((tp = gettosbyname("telnet", "tcp")) &&
21840242Sborman 	    (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
21940242Sborman 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
22040242Sborman #endif /* IP_TOS */
22138904Sborman 	net = 0;
22238904Sborman 	doit(&from);
22338904Sborman 	/* NOTREACHED */
22438904Sborman }  /* end of main */
2256002Sroot 
22640242Sborman void	cleanup();
22727649Sminshall 
22827649Sminshall /*
22927983Sminshall  * getterminaltype
23027649Sminshall  *
23138904Sborman  *	Ask the other end to send along its terminal type and speed.
23227983Sminshall  * Output is the variable terminaltype filled in.
23327649Sminshall  */
23438904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
23527983Sminshall void
23627983Sminshall getterminaltype()
23727649Sminshall {
23838904Sborman     void ttloop();
23927649Sminshall 
24038904Sborman     settimer(baseline);
24139503Sborman     send_do(TELOPT_TTYPE, 1);
24239503Sborman     send_do(TELOPT_TSPEED, 1);
24338904Sborman     while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
24438904Sborman 	   (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
24527983Sminshall 	ttloop();
24627649Sminshall     }
24738904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
24838904Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
24927983Sminshall 
25027983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
25127983Sminshall 	nfrontp += sizeof sbbuf;
25238904Sborman     }
25338904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
25438904Sborman 
25538904Sborman 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
25638904Sborman 	nfrontp += sizeof ttytype_sbbuf;
25738904Sborman     }
25838904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
25938904Sborman 	while (sequenceIs(tspeedsubopt, baseline))
26027983Sminshall 	    ttloop();
26138904Sborman     }
26238904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
26338904Sborman 	char first[256], last[256];
26438904Sborman 
26538904Sborman 	while (sequenceIs(ttypesubopt, baseline))
26638904Sborman 	    ttloop();
26738904Sborman 
26838904Sborman 	if (!terminaltypeok(&terminaltype[5])) {
26938904Sborman 	    (void) strncpy(first, terminaltype, sizeof(first));
27038904Sborman 	    for(;;) {
27138904Sborman 		/*
27238904Sborman 		 * Save the unknown name, and request the next name.
27338904Sborman 		 */
27438904Sborman 		(void) strncpy(last, terminaltype, sizeof(last));
27538904Sborman 		_gettermname();
27638904Sborman 		if (terminaltypeok(&terminaltype[5]))
27738904Sborman 		    break;
27838904Sborman 		if (strncmp(last, terminaltype, sizeof(last)) == 0) {
27938904Sborman 		    /*
28038904Sborman 		     * We've hit the end.  If this is the same as
28138904Sborman 		     * the first name, just go with it.
28238904Sborman 		     */
28338904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) == 0))
28438904Sborman 			break;
28538904Sborman 		    /*
28638904Sborman 		     * Get the terminal name one more type, so that
28738904Sborman 		     * RFC1091 compliant telnets will cycle back to
28838904Sborman 		     * the start of the list.
28938904Sborman 		     */
29038904Sborman 		    _gettermname();
29138904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) != 0))
29238904Sborman 			(void) strncpy(terminaltype, first, sizeof(first));
29338904Sborman 		    break;
29438904Sborman 		}
29538904Sborman 	    }
29627983Sminshall 	}
29727983Sminshall     }
29838904Sborman }  /* end of getterminaltype */
29938904Sborman 
30038904Sborman _gettermname()
30138904Sborman {
30238904Sborman     settimer(baseline);
30338904Sborman     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
30438904Sborman     nfrontp += sizeof ttytype_sbbuf;
30538904Sborman     while (sequenceIs(ttypesubopt, baseline))
30638904Sborman 	ttloop();
30727649Sminshall }
30827649Sminshall 
30938904Sborman terminaltypeok(s)
31038904Sborman char *s;
31138904Sborman {
31238904Sborman     char buf[1024];
31338904Sborman 
31438904Sborman     if (terminaltype == NULL)
31538904Sborman 	return(1);
31638904Sborman 
31738904Sborman     /*
31838904Sborman      * tgetent() will return 1 if the type is known, and
31938904Sborman      * 0 if it is not known.  If it returns -1, it couldn't
32038904Sborman      * open the database.  But if we can't open the database,
32138904Sborman      * it won't help to say we failed, because we won't be
32238904Sborman      * able to verify anything else.  So, we treat -1 like 1.
32338904Sborman      */
32438904Sborman     if (tgetent(buf, s) == 0)
32538904Sborman 	return(0);
32638904Sborman     return(1);
32738904Sborman }
32838904Sborman 
3296002Sroot /*
3306002Sroot  * Get a pty, scan input lines.
3316002Sroot  */
33238904Sborman doit(who)
33312683Ssam 	struct sockaddr_in *who;
3346002Sroot {
33520188Skarels 	char *host, *inet_ntoa();
33638904Sborman 	int t;
33712683Ssam 	struct hostent *hp;
3386002Sroot 
33938904Sborman 	/*
34038904Sborman 	 * Find an available pty to use.
34138904Sborman 	 */
34238904Sborman 	pty = getpty();
34338904Sborman 	if (pty < 0)
34438904Sborman 		fatal(net, "All network ports in use");
34520188Skarels 
34638904Sborman 	t = getptyslave();
34738904Sborman 
34838904Sborman 	/* get name of connected client */
34938904Sborman 	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
35012683Ssam 		who->sin_family);
35112683Ssam 	if (hp)
35212683Ssam 		host = hp->h_name;
35312683Ssam 	else
35417444Sralph 		host = inet_ntoa(who->sin_addr);
35527983Sminshall 
35627983Sminshall 	/*
35738904Sborman 	 * get terminal type.
35827983Sminshall 	 */
35927983Sminshall 	getterminaltype();
36038904Sborman 	if (terminaltype == NULL)
36138904Sborman 		terminaltype = "TERM=network";
36227983Sminshall 
36327649Sminshall 	/*
36438904Sborman 	 * Start up the login process on the slave side of the terminal
36527649Sminshall 	 */
36638904Sborman 	startslave(t, host);
36738904Sborman 
36838904Sborman 	telnet(net, pty);  /* begin server processing */
3699244Ssam 	/*NOTREACHED*/
37038904Sborman }  /* end of doit */
3719244Ssam 
37238904Sborman #ifndef	MAXHOSTNAMELEN
37338904Sborman #define	MAXHOSTNAMELEN 64
37438904Sborman #endif	MAXHOSTNAMELEN
3756002Sroot /*
3766002Sroot  * Main loop.  Select from pty and network, and
3776002Sroot  * hand data to telnet receiver finite state machine.
3786002Sroot  */
3796002Sroot telnet(f, p)
38038904Sborman int f, p;
3816002Sroot {
3826002Sroot 	int on = 1;
38327898Skarels 	char hostname[MAXHOSTNAMELEN];
38440242Sborman #if	defined(CRAY2) && defined(UNICOS5)
38538904Sborman 	int termstat();
38638904Sborman 	int interrupt(), sendbrk();
38738904Sborman #endif
38833271Sminshall #define	TABBUFSIZ	512
38933271Sminshall 	char	defent[TABBUFSIZ];
39033271Sminshall 	char	defstrs[TABBUFSIZ];
39133271Sminshall #undef	TABBUFSIZ
39233271Sminshall 	char *HE;
39333271Sminshall 	char *HN;
39433271Sminshall 	char *IM;
39538904Sborman 	void netflush();
39638904Sborman 
39732400Sminshall 	/*
39838904Sborman 	 * Initialize the slc mapping table.
39932400Sminshall 	 */
40038904Sborman 	get_slc_defaults();
4016002Sroot 
4028379Ssam 	/*
40338904Sborman 	 * Do some tests where it is desireable to wait for a response.
40438904Sborman 	 * Rather than doing them slowly, one at a time, do them all
40538904Sborman 	 * at once.
4068379Ssam 	 */
40738904Sborman 	if (!myopts[TELOPT_SGA])
40839503Sborman 		send_will(TELOPT_SGA, 1);
40912713Ssam 	/*
41027649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
41127649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
41227649Sminshall 	 *
41327649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
41427649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
41527649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
41627649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
41727649Sminshall 	 * echo the terminal input).
41827649Sminshall 	 */
41939503Sborman 	send_do(TELOPT_ECHO, 1);
42027649Sminshall 
42138904Sborman #ifdef	LINEMODE
42238904Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
42338904Sborman 		/* Query the peer for linemode support by trying to negotiate
42438904Sborman 		 * the linemode option.
42538904Sborman 		 */
42638904Sborman 		linemode = 1;
42738904Sborman 		editmode = 0;
42839503Sborman 		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
42938904Sborman 	}
43038904Sborman #endif	/* LINEMODE */
43138904Sborman 
43227649Sminshall 	/*
43338904Sborman 	 * Send along a couple of other options that we wish to negotiate.
43438904Sborman 	 */
43539503Sborman 	send_do(TELOPT_NAWS, 1);
43639503Sborman 	send_will(TELOPT_STATUS, 1);
43738904Sborman 	flowmode = 1;  /* default flow control state */
43839503Sborman 	send_do(TELOPT_LFLOW, 1);
43938904Sborman 
44038904Sborman 	/*
44138904Sborman 	 * Spin, waiting for a response from the DO ECHO.  However,
44238904Sborman 	 * some REALLY DUMB telnets out there might not respond
44338904Sborman 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
44438904Sborman 	 * telnets so far seem to respond with WONT for a DO that
44538904Sborman 	 * they don't understand...) because by the time we get the
44638904Sborman 	 * response, it will already have processed the DO ECHO.
44738904Sborman 	 * Kludge upon kludge.
44838904Sborman 	 */
44938904Sborman 	while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
45038904Sborman 		ttloop();
45138904Sborman 
45238904Sborman 	/*
45338995Sborman 	 * On the off chance that the telnet client is broken and does not
45438995Sborman 	 * respond to the DO ECHO we sent, (after all, we did send the
45538995Sborman 	 * DO NAWS negotiation after the DO ECHO, and we won't get here
45638995Sborman 	 * until a response to the DO NAWS comes back) simulate the
45738995Sborman 	 * receipt of a will echo.  This will also send a WONT ECHO
45838995Sborman 	 * to the client, since we assume that the client failed to
45938995Sborman 	 * respond because it believes that it is already in DO ECHO
46038995Sborman 	 * mode, which we do not want.
46138995Sborman 	 */
46240242Sborman 	if (hiswants[TELOPT_ECHO] == OPT_YES) {
46339503Sborman 		willoption(TELOPT_ECHO);
46440242Sborman 	}
46538995Sborman 
46638995Sborman 	/*
46738995Sborman 	 * Finally, to clean things up, we turn on our echo.  This
46838995Sborman 	 * will break stupid 4.2 telnets out of local terminal echo.
46938995Sborman 	 */
47038995Sborman 
47138995Sborman 	if (!myopts[TELOPT_ECHO])
47239503Sborman 		send_will(TELOPT_ECHO, 1);
47338995Sborman 
47438995Sborman 	/*
47538904Sborman 	 * Turn on packet mode, and default to line at at time mode.
47638904Sborman 	 */
47738904Sborman 	(void) ioctl(p, TIOCPKT, (char *)&on);
47838904Sborman #ifdef	LINEMODE
47938904Sborman 	tty_setlinemode(1);
48038904Sborman 
48138904Sborman # ifdef	KLUDGELINEMODE
48238904Sborman 	/*
48338904Sborman 	 * Continuing line mode support.  If client does not support
48438904Sborman 	 * real linemode, attempt to negotiate kludge linemode by sending
48538904Sborman 	 * the do timing mark sequence.
48638904Sborman 	 */
48738904Sborman 	if (lmodetype < REAL_LINEMODE)
48839503Sborman 		send_do(TELOPT_TM, 1);
48938904Sborman # endif	/* KLUDGELINEMODE */
49038904Sborman #endif	/* LINEMODE */
49138904Sborman 
49238904Sborman 	/*
49338904Sborman 	 * Call telrcv() once to pick up anything received during
49438904Sborman 	 * terminal type negotiation, 4.2/4.3 determination, and
49538904Sborman 	 * linemode negotiation.
49638904Sborman 	 */
49738904Sborman 	telrcv();
49838904Sborman 
49938904Sborman 	(void) ioctl(f, FIONBIO, (char *)&on);
50038904Sborman 	(void) ioctl(p, FIONBIO, (char *)&on);
50140242Sborman #if	defined(CRAY2) && defined(UNICOS5)
50238904Sborman 	init_termdriver(f, p, interrupt, sendbrk);
50338904Sborman #endif
50438904Sborman 
50538904Sborman #if	defined(SO_OOBINLINE)
50638904Sborman 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
50738904Sborman #endif	/* defined(SO_OOBINLINE) */
50838904Sborman 
50938904Sborman #ifdef	SIGTSTP
51038904Sborman 	(void) signal(SIGTSTP, SIG_IGN);
51138904Sborman #endif
51238904Sborman #ifdef	SIGTTOU
51338904Sborman 	/*
51438904Sborman 	 * Ignoring SIGTTOU keeps the kernel from blocking us
51538904Sborman 	 * in ttioct() in /sys/tty.c.
51638904Sborman 	 */
51738904Sborman 	(void) signal(SIGTTOU, SIG_IGN);
51838904Sborman #endif
51938904Sborman 
52038904Sborman 	(void) signal(SIGCHLD, cleanup);
52138904Sborman 
52240242Sborman #if	defined(CRAY2) && defined(UNICOS5)
52338904Sborman 	/*
52438904Sborman 	 * Cray-2 will send a signal when pty modes are changed by slave
52538904Sborman 	 * side.  Set up signal handler now.
52638904Sborman 	 */
52738904Sborman 	if ((int)signal(SIGUSR1, termstat) < 0)
52838904Sborman 		perror("signal");
52938904Sborman 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
53038904Sborman 		perror("ioctl:TCSIGME");
53138904Sborman 	/*
53238904Sborman 	 * Make processing loop check terminal characteristics early on.
53338904Sborman 	 */
53438904Sborman 	termstat();
53538904Sborman #endif
53638904Sborman 
53738904Sborman 	(void) setpgrp(0, 0);
53840242Sborman #ifdef	TCSETCTTY
53940242Sborman 	ioctl(p, TCSETCTTY, 0);
54040242Sborman #endif
54138904Sborman 
54238904Sborman 	/*
54312713Ssam 	 * Show banner that getty never gave.
54427797Sminshall 	 *
54533271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
54633271Sminshall 	 * gets carriage return null processing, etc., just like all
54733271Sminshall 	 * other pty --> client data.
54812713Ssam 	 */
54927797Sminshall 
55038904Sborman 	(void) gethostname(hostname, sizeof (hostname));
55138904Sborman 
55233271Sminshall 	if (getent(defent, "default") == 1) {
55333271Sminshall 		char *getstr();
55438904Sborman 		char *cp=defstrs;
55527649Sminshall 
55638904Sborman 		HE = getstr("he", &cp);
55738904Sborman 		HN = getstr("hn", &cp);
55838904Sborman 		IM = getstr("im", &cp);
55933271Sminshall 		if (HN && *HN)
56038904Sborman 			(void) strcpy(hostname, HN);
56138904Sborman 		if (IM == 0)
56238904Sborman 			IM = "";
56333271Sminshall 	} else {
56438904Sborman #ifdef	CRAY
56538904Sborman 		if (hostinfo == 0)
56638904Sborman 			IM = 0;
56738904Sborman 		else
56838904Sborman #endif
56938904Sborman 			IM = DEFAULT_IM;
57038904Sborman 		HE = 0;
57133271Sminshall 	}
57238904Sborman 	edithost(HE, hostname);
57338904Sborman 	if (IM && *IM)
57438904Sborman 		putf(IM, ptyibuf2);
57527797Sminshall 
57638904Sborman 	if (pcc)
57738904Sborman 		(void) strncat(ptyibuf2, ptyip, pcc+1);
57838904Sborman 	ptyip = ptyibuf2;
57938904Sborman 	pcc = strlen(ptyip);
58040242Sborman #ifdef	LINEMODE
58140242Sborman 	/*
58240242Sborman 	 * Last check to make sure all our states are correct.
58340242Sborman 	 */
58440242Sborman 	init_termbuf();
58540242Sborman 	localstat();
58640242Sborman #endif	/* LINEMODE */
58733271Sminshall 
5886002Sroot 	for (;;) {
58927185Sminshall 		fd_set ibits, obits, xbits;
5906002Sroot 		register int c;
5916002Sroot 
59227185Sminshall 		if (ncc < 0 && pcc < 0)
59327185Sminshall 			break;
59427185Sminshall 
59540242Sborman #if	defined(CRAY2) && defined(UNICOS5)
59638904Sborman 		if (needtermstat)
59738904Sborman 			_termstat();
59840242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
59927185Sminshall 		FD_ZERO(&ibits);
60027185Sminshall 		FD_ZERO(&obits);
60127185Sminshall 		FD_ZERO(&xbits);
6026002Sroot 		/*
6036002Sroot 		 * Never look for input if there's still
6046002Sroot 		 * stuff in the corresponding output buffer
6056002Sroot 		 */
60627185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
60727185Sminshall 			FD_SET(f, &obits);
60827185Sminshall 		} else {
60927185Sminshall 			FD_SET(p, &ibits);
61027185Sminshall 		}
61127185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
61227185Sminshall 			FD_SET(p, &obits);
61327185Sminshall 		} else {
61427185Sminshall 			FD_SET(f, &ibits);
61527185Sminshall 		}
61627185Sminshall 		if (!SYNCHing) {
61727185Sminshall 			FD_SET(f, &xbits);
61827185Sminshall 		}
61927185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
62027185Sminshall 						(struct timeval *)0)) < 1) {
62127185Sminshall 			if (c == -1) {
62227185Sminshall 				if (errno == EINTR) {
62327185Sminshall 					continue;
62427185Sminshall 				}
62527185Sminshall 			}
6266002Sroot 			sleep(5);
6276002Sroot 			continue;
6286002Sroot 		}
6296002Sroot 
6306002Sroot 		/*
63127185Sminshall 		 * Any urgent data?
63227185Sminshall 		 */
63327185Sminshall 		if (FD_ISSET(net, &xbits)) {
63427185Sminshall 		    SYNCHing = 1;
63527185Sminshall 		}
63627185Sminshall 
63727185Sminshall 		/*
6386002Sroot 		 * Something to read from the network...
6396002Sroot 		 */
64027185Sminshall 		if (FD_ISSET(net, &ibits)) {
64127649Sminshall #if	!defined(SO_OOBINLINE)
64227185Sminshall 			/*
64327898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
64427185Sminshall 			 * OOB indication and data handling in the kernel
64527185Sminshall 			 * is such that if two separate TCP Urgent requests
64627185Sminshall 			 * come in, one byte of TCP data will be overlaid.
64727185Sminshall 			 * This is fatal for Telnet, but we try to live
64827185Sminshall 			 * with it.
64927185Sminshall 			 *
65027185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
65127185Sminshall 			 * is needed to pick up the TCP Urgent data in
65227185Sminshall 			 * the correct sequence.
65327185Sminshall 			 *
65427185Sminshall 			 * What we do is:  if we think we are in urgent
65527185Sminshall 			 * mode, we look to see if we are "at the mark".
65627185Sminshall 			 * If we are, we do an OOB receive.  If we run
65727185Sminshall 			 * this twice, we will do the OOB receive twice,
65827185Sminshall 			 * but the second will fail, since the second
65927185Sminshall 			 * time we were "at the mark", but there wasn't
66027185Sminshall 			 * any data there (the kernel doesn't reset
66127185Sminshall 			 * "at the mark" until we do a normal read).
66227185Sminshall 			 * Once we've read the OOB data, we go ahead
66327185Sminshall 			 * and do normal reads.
66427185Sminshall 			 *
66527185Sminshall 			 * There is also another problem, which is that
66627185Sminshall 			 * since the OOB byte we read doesn't put us
66727185Sminshall 			 * out of OOB state, and since that byte is most
66827185Sminshall 			 * likely the TELNET DM (data mark), we would
66927185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
67027185Sminshall 			 * So, clocks to the rescue.  If we've "just"
67127185Sminshall 			 * received a DM, then we test for the
67227185Sminshall 			 * presence of OOB data when the receive OOB
67327185Sminshall 			 * fails (and AFTER we did the normal mode read
67427185Sminshall 			 * to clear "at the mark").
67527185Sminshall 			 */
67627185Sminshall 		    if (SYNCHing) {
67727185Sminshall 			int atmark;
67827185Sminshall 
67938904Sborman 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
68027185Sminshall 			if (atmark) {
68127185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
68227185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
68327185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
68427983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
68527185Sminshall 				    SYNCHing = stilloob(net);
68627185Sminshall 				}
68727185Sminshall 			    }
68827185Sminshall 			} else {
68927185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
6906002Sroot 			}
69127185Sminshall 		    } else {
69227185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
69327185Sminshall 		    }
69427185Sminshall 		    settimer(didnetreceive);
69527649Sminshall #else	/* !defined(SO_OOBINLINE)) */
69627185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
69727649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
69827185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
69927185Sminshall 			ncc = 0;
70027185Sminshall 		    else {
70127185Sminshall 			if (ncc <= 0) {
70227185Sminshall 			    break;
70327185Sminshall 			}
70427185Sminshall 			netip = netibuf;
70527185Sminshall 		    }
7066002Sroot 		}
7076002Sroot 
7086002Sroot 		/*
7096002Sroot 		 * Something to read from the pty...
7106002Sroot 		 */
71138904Sborman 		if (FD_ISSET(p, &ibits)) {
7126002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
7136002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
7146002Sroot 				pcc = 0;
7156002Sroot 			else {
7166002Sroot 				if (pcc <= 0)
7176002Sroot 					break;
71840242Sborman #if	!defined(CRAY2) || !defined(UNICOS5)
71938904Sborman #ifdef	LINEMODE
72038904Sborman 				/*
72138904Sborman 				 * If ioctl from pty, pass it through net
72238904Sborman 				 */
72338904Sborman 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
72438904Sborman 					copy_termbuf(ptyibuf+1, pcc-1);
72538904Sborman 					localstat();
72638904Sborman 					pcc = 1;
72738904Sborman 				}
72838904Sborman #endif	LINEMODE
72937210Sminshall 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
73038904Sborman 					netclear();	/* clear buffer back */
73140242Sborman #ifdef	notdef
73240242Sborman 					/*
73340242Sborman 					 * We really should have this in, but
73440242Sborman 					 * there are client telnets on some
73540242Sborman 					 * operating systems get screwed up
73640242Sborman 					 * royally if we send them urgent
73740242Sborman 					 * mode data.  So, for now, we'll not
73840242Sborman 					 * do this...
73940242Sborman 					 */
74037210Sminshall 					*nfrontp++ = IAC;
74137210Sminshall 					*nfrontp++ = DM;
74237210Sminshall 					neturg = nfrontp-1; /* off by one XXX */
74340242Sborman #endif
74437210Sminshall 				}
74537210Sminshall 				if (hisopts[TELOPT_LFLOW] &&
74637210Sminshall 				    (ptyibuf[0] &
74738904Sborman 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
74838904Sborman 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
74937210Sminshall 					    IAC, SB, TELOPT_LFLOW,
75037210Sminshall 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
75137210Sminshall 					    IAC, SE);
75237210Sminshall 					nfrontp += 6;
75337210Sminshall 				}
75433267Sminshall 				pcc--;
75533267Sminshall 				ptyip = ptyibuf+1;
75640242Sborman #else	/* defined(CRAY2) && defined(UNICOS5) */
75738904Sborman 				if (!uselinemode) {
75839531Sborman 					unpcc = pcc;
75939531Sborman 					unptyip = ptyibuf;
76039531Sborman 					pcc = term_output(&unptyip, ptyibuf2,
76139531Sborman 								&unpcc, BUFSIZ);
76238904Sborman 					ptyip = ptyibuf2;
76338904Sborman 				} else
76438904Sborman 					ptyip = ptyibuf;
76540242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
76638904Sborman 			}
7676002Sroot 		}
7686002Sroot 
7696002Sroot 		while (pcc > 0) {
7706002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
7716002Sroot 				break;
7726002Sroot 			c = *ptyip++ & 0377, pcc--;
7736002Sroot 			if (c == IAC)
7746002Sroot 				*nfrontp++ = c;
77540242Sborman #if	defined(CRAY2) && defined(UNICOS5)
77638904Sborman 			else if (c == '\n' &&
77738904Sborman 				     myopts[TELOPT_BINARY] == OPT_NO && newmap)
77838904Sborman 				*nfrontp++ = '\r';
77940242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
7806002Sroot 			*nfrontp++ = c;
78131940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
78227020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
78327020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
78427020Sminshall 					pcc--;
78527020Sminshall 				} else
78627020Sminshall 					*nfrontp++ = '\0';
78727020Sminshall 			}
7886002Sroot 		}
78940242Sborman #if	defined(CRAY2) && defined(UNICOS5)
79039531Sborman 		/*
79139531Sborman 		 * If chars were left over from the terminal driver,
79239531Sborman 		 * note their existence.
79339531Sborman 		 */
79439531Sborman 		 if (!uselinemode && unpcc) {
79539531Sborman 			pcc = unpcc;
79639531Sborman 			unpcc = 0;
79739531Sborman 			ptyip = unptyip;
79839531Sborman 		}
79940242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
80039531Sborman 
80127185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
8026002Sroot 			netflush();
8036002Sroot 		if (ncc > 0)
8046002Sroot 			telrcv();
80527185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
8066002Sroot 			ptyflush();
8076002Sroot 	}
8086002Sroot 	cleanup();
80938904Sborman }  /* end of telnet */
8106002Sroot 
81138904Sborman #ifndef	TCSIG
81238904Sborman # ifdef	TIOCSIG
81338904Sborman #  define TCSIG TIOCSIG
81438904Sborman # endif
81538904Sborman #endif
8166002Sroot 
81737212Sminshall /*
8186002Sroot  * Send interrupt to process on other side of pty.
8196002Sroot  * If it is in raw mode, just write NULL;
8206002Sroot  * otherwise, write intr char.
8216002Sroot  */
8226002Sroot interrupt()
8236002Sroot {
82438904Sborman 	ptyflush();	/* half-hearted */
8256002Sroot 
82638904Sborman #ifdef	TCSIG
82738904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
82838904Sborman #else	/* TCSIG */
82938904Sborman 	init_termbuf();
83040242Sborman 	*pfrontp++ = slctab[SLC_IP].sptr ?
83140242Sborman 			(unsigned char)*slctab[SLC_IP].sptr : '\177';
83238904Sborman #endif	/* TCSIG */
8336002Sroot }
8346002Sroot 
83527229Sminshall /*
83627229Sminshall  * Send quit to process on other side of pty.
83727229Sminshall  * If it is in raw mode, just write NULL;
83827229Sminshall  * otherwise, write quit char.
83927229Sminshall  */
84027229Sminshall sendbrk()
84127229Sminshall {
84227229Sminshall 	ptyflush();	/* half-hearted */
84338904Sborman #ifdef	TCSIG
84438904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
84538904Sborman #else	/* TCSIG */
84638904Sborman 	init_termbuf();
84740242Sborman 	*pfrontp++ = slctab[SLC_ABORT].sptr ?
84840242Sborman 			(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
84938904Sborman #endif	/* TCSIG */
85027229Sminshall }
85127229Sminshall 
85238904Sborman sendsusp()
8536002Sroot {
85438904Sborman #ifdef	SIGTSTP
85538904Sborman 	ptyflush();	/* half-hearted */
85638904Sborman # ifdef	TCSIG
85738904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
85838904Sborman # else	/* TCSIG */
85940242Sborman 	*pfrontp++ = slctab[SLC_SUSP].sptr ?
86040242Sborman 			(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
86138904Sborman # endif	/* TCSIG */
86238904Sborman #endif	/* SIGTSTP */
8636002Sroot }
8646002Sroot 
86538904Sborman doeof()
8666002Sroot {
86740242Sborman #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
86840242Sborman 	extern char oldeofc;
86940242Sborman #endif
87038904Sborman 	init_termbuf();
8716002Sroot 
87240242Sborman #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
87340242Sborman 	if (!tty_isediting()) {
87440242Sborman 		*pfrontp++ = oldeofc;
87540242Sborman 		return;
87640242Sborman 	}
87740242Sborman #endif
87840242Sborman 	*pfrontp++ = slctab[SLC_EOF].sptr ?
87940242Sborman 			(unsigned char)*slctab[SLC_EOF].sptr : '\004';
8806002Sroot }
881