xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 40242)
121182Sdist /*
238904Sborman  * Copyright (c) 1989 Regents of the University of California.
333687Sbostic  * All rights reserved.
433687Sbostic  *
533687Sbostic  * Redistribution and use in source and binary forms are permitted
634778Sbostic  * provided that the above copyright notice and this paragraph are
734778Sbostic  * duplicated in all such forms and that any documentation,
834778Sbostic  * advertising materials, and other materials related to such
934778Sbostic  * distribution and use acknowledge that the software was developed
1034778Sbostic  * by the University of California, Berkeley.  The name of the
1134778Sbostic  * University may not be used to endorse or promote products derived
1234778Sbostic  * from this software without specific prior written permission.
1334778Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434778Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534778Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621182Sdist  */
1721182Sdist 
186295Sroot #ifndef lint
1921182Sdist char copyright[] =
2038904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\
2121182Sdist  All rights reserved.\n";
2233687Sbostic #endif /* not lint */
236295Sroot 
2421182Sdist #ifndef lint
25*40242Sborman static char sccsid[] = "@(#)telnetd.c	5.42 (Berkeley) 02/28/90";
2633687Sbostic #endif /* not lint */
2721182Sdist 
2838904Sborman #include "telnetd.h"
2938904Sborman 
306002Sroot /*
3138904Sborman  * I/O data buffers,
3238904Sborman  * pointers, and counters.
336002Sroot  */
3438904Sborman char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
3538904Sborman char	ptyibuf2[BUFSIZ];
369218Ssam 
3738904Sborman #ifdef	CRAY
3838904Sborman int	hostinfo = 1;			/* do we print login banner? */
3938904Sborman #endif
409218Ssam 
4138904Sborman #ifdef	CRAY
4238904Sborman extern int      newmap; /* nonzero if \n maps to ^M^J */
43*40242Sborman int	lowpty = 0, highpty;	/* low, high pty numbers */
4438904Sborman #endif /* CRAY */
4512216Ssam 
4638904Sborman int debug = 0;
4738904Sborman char *progname;
489218Ssam 
49*40242Sborman #if	defined(IP_TOS) && defined(NEED_GETTOS)
50*40242Sborman struct tosent {
51*40242Sborman 	char	*t_name;	/* name */
52*40242Sborman 	char	**t_aliases;	/* alias list */
53*40242Sborman 	char	*t_proto;	/* protocol */
54*40242Sborman 	int	t_tos;		/* Type Of Service bits */
55*40242Sborman };
56*40242Sborman 
57*40242Sborman struct tosent *
58*40242Sborman gettosbyname(name, proto)
59*40242Sborman char *name, *proto;
60*40242Sborman {
61*40242Sborman 	static struct tosent te;
62*40242Sborman 	static char *aliasp = 0;
63*40242Sborman 
64*40242Sborman 	te.t_name = name;
65*40242Sborman 	te.t_aliases = &aliasp;
66*40242Sborman 	te.t_proto = proto;
67*40242Sborman 	te.t_tos = 020;	/* Low Delay bit */
68*40242Sborman 	return(&te);
69*40242Sborman }
70*40242Sborman #endif
71*40242Sborman 
7238904Sborman main(argc, argv)
7338904Sborman 	char *argv[];
7438904Sborman {
7538904Sborman 	struct sockaddr_in from;
7638904Sborman 	int on = 1, fromlen;
77*40242Sborman #ifdef IP_TOS
78*40242Sborman 	struct tosent *tp;
79*40242Sborman #endif /* IP_TOS */
806002Sroot 
8138904Sborman 	pfrontp = pbackp = ptyobuf;
8238904Sborman 	netip = netibuf;
8338904Sborman 	nfrontp = nbackp = netobuf;
846002Sroot 
8538904Sborman 	progname = *argv;
86*40242Sborman 
87*40242Sborman #ifdef CRAY
88*40242Sborman 	/*
89*40242Sborman 	 * Get number of pty's before trying to process options,
90*40242Sborman 	 * which may include changing pty range.
91*40242Sborman 	 */
92*40242Sborman 	highpty = getnpty();
93*40242Sborman #endif /* CRAY */
94*40242Sborman 
9538904Sborman top:
9638904Sborman 	argc--, argv++;
9727649Sminshall 
9838904Sborman 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
9938904Sborman 		debug++;
10038904Sborman 		goto top;
10138904Sborman 	}
10227649Sminshall 
10338904Sborman #ifdef	LINEMODE
10438904Sborman 	if (argc > 0 && !strcmp(*argv, "-l")) {
10538904Sborman 		alwayslinemode = 1;
10638904Sborman 		goto top;
10738904Sborman 	}
10838904Sborman #endif	/* LINEMODE */
10927649Sminshall 
11038904Sborman #ifdef CRAY
11138904Sborman 	if (argc > 0 && !strcmp(*argv, "-h")) {
11238904Sborman 		hostinfo = 0;
11338904Sborman 		goto top;
11438904Sborman 	}
11527649Sminshall 
11638904Sborman 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
11738904Sborman 		char *strchr();
11838904Sborman 		char *c;
11927649Sminshall 
120*40242Sborman 		/*
121*40242Sborman 		 * Allow the specification of alterations to the pty search
122*40242Sborman 		 * range.  It is legal to specify only one, and not change the
123*40242Sborman 		 * other from its default.
124*40242Sborman 		 */
12538904Sborman 		*argv += 2;
12638904Sborman 		if (**argv == '\0' && argc)
12738904Sborman 			argv++, argc--;
12838904Sborman 		c = strchr(*argv, '-');
12938904Sborman 		if (c) {
13038904Sborman 			*c++ = '\0';
13138904Sborman 			highpty = atoi(c);
132*40242Sborman 		}
133*40242Sborman 		if (**argv != '\0')
134*40242Sborman 			lowpty = atoi(*argv);
135*40242Sborman 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) {
13638904Sborman 	usage:
13738904Sborman 			fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
13838904Sborman # ifdef	NEWINIT
13938904Sborman 			fprintf(stderr, "[-Iinitid] ");
14038904Sborman # endif	/* NEWINIT */
141*40242Sborman 			fprintf(stderr, "[-l] [-r[lowpty]-[highpty]] [port]\n");
14238904Sborman 			exit(1);
14338904Sborman 		}
14438904Sborman 		goto top;
14538904Sborman 	}
14638904Sborman # ifdef	NEWINIT
14738904Sborman 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
14838904Sborman 		extern char *gen_id;
14927649Sminshall 
15038904Sborman 		*argv += 2;
15138904Sborman 		if (**argv == '\0') {
15238904Sborman 			if (argc < 2)
15338904Sborman 				goto usage;
15438904Sborman 			argv++, argc--;
15538904Sborman 			if (**argv == '\0')
15638904Sborman 				goto usage;
15738904Sborman 		}
15838904Sborman 		gen_id = *argv;
15938904Sborman 		goto top;
16038904Sborman 	}
16138904Sborman # endif	/* NEWINIT */
16238904Sborman #endif	/* CRAY */
1636002Sroot 
16438904Sborman 	if (debug) {
16527185Sminshall 	    int s, ns, foo;
16627185Sminshall 	    struct servent *sp;
16727185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
16827185Sminshall 
16938904Sborman 	    if (argc > 0) {
17038904Sborman 		    if (sp = getservbyname(*argv, "tcp")) {
17138904Sborman 			sin.sin_port = sp->s_port;
17238904Sborman 		    } else {
17338904Sborman 			sin.sin_port = atoi(*argv);
17438904Sborman 			if ((int)sin.sin_port <= 0) {
17538904Sborman 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
17638904Sborman 			    exit(1);
17738904Sborman 			}
17838904Sborman 			sin.sin_port = htons((u_short)sin.sin_port);
17938904Sborman 		   }
18037210Sminshall 	    } else {
18137210Sminshall 		sp = getservbyname("telnet", "tcp");
18237210Sminshall 		if (sp == 0) {
18337210Sminshall 			fprintf(stderr,
18437210Sminshall 				"telnetd: tcp/telnet: unknown service\n");
18538904Sborman 		    exit(1);
18637210Sminshall 		}
18737210Sminshall 		sin.sin_port = sp->s_port;
18827185Sminshall 	    }
18927185Sminshall 
19027185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
19127185Sminshall 	    if (s < 0) {
19227185Sminshall 		    perror("telnetd: socket");;
19327185Sminshall 		    exit(1);
19427185Sminshall 	    }
19538904Sborman 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
19638904Sborman 	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
19727185Sminshall 		perror("bind");
19827185Sminshall 		exit(1);
19927185Sminshall 	    }
20027185Sminshall 	    if (listen(s, 1) < 0) {
20127185Sminshall 		perror("listen");
20227185Sminshall 		exit(1);
20327185Sminshall 	    }
20427185Sminshall 	    foo = sizeof sin;
20538904Sborman 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
20627185Sminshall 	    if (ns < 0) {
20727185Sminshall 		perror("accept");
20827185Sminshall 		exit(1);
20927185Sminshall 	    }
21038904Sborman 	    (void) dup2(ns, 0);
21138904Sborman 	    (void) close(ns);
21238904Sborman 	    (void) close(s);
21327185Sminshall 	}
21438904Sborman 
21524855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
21616371Skarels 	fromlen = sizeof (from);
21738904Sborman 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
21838904Sborman 		fprintf(stderr, "%s: ", progname);
21916371Skarels 		perror("getpeername");
22016371Skarels 		_exit(1);
2218346Ssam 	}
22217156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
22317187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
22410418Ssam 	}
225*40242Sborman 
226*40242Sborman #ifdef IP_TOS
227*40242Sborman 	if ((tp = gettosbyname("telnet", "tcp")) &&
228*40242Sborman 	    (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
229*40242Sborman 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
230*40242Sborman #endif /* IP_TOS */
23138904Sborman 	net = 0;
23238904Sborman 	doit(&from);
23338904Sborman 	/* NOTREACHED */
23438904Sborman }  /* end of main */
2356002Sroot 
236*40242Sborman void	cleanup();
23727649Sminshall 
23827649Sminshall /*
23927983Sminshall  * getterminaltype
24027649Sminshall  *
24138904Sborman  *	Ask the other end to send along its terminal type and speed.
24227983Sminshall  * Output is the variable terminaltype filled in.
24327649Sminshall  */
24438904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
24527983Sminshall void
24627983Sminshall getterminaltype()
24727649Sminshall {
24838904Sborman     void ttloop();
24927649Sminshall 
25038904Sborman     settimer(baseline);
25139503Sborman     send_do(TELOPT_TTYPE, 1);
25239503Sborman     send_do(TELOPT_TSPEED, 1);
25338904Sborman     while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
25438904Sborman 	   (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
25527983Sminshall 	ttloop();
25627649Sminshall     }
25738904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
25838904Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
25927983Sminshall 
26027983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
26127983Sminshall 	nfrontp += sizeof sbbuf;
26238904Sborman     }
26338904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
26438904Sborman 
26538904Sborman 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
26638904Sborman 	nfrontp += sizeof ttytype_sbbuf;
26738904Sborman     }
26838904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
26938904Sborman 	while (sequenceIs(tspeedsubopt, baseline))
27027983Sminshall 	    ttloop();
27138904Sborman     }
27238904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
27338904Sborman 	char first[256], last[256];
27438904Sborman 
27538904Sborman 	while (sequenceIs(ttypesubopt, baseline))
27638904Sborman 	    ttloop();
27738904Sborman 
27838904Sborman 	if (!terminaltypeok(&terminaltype[5])) {
27938904Sborman 	    (void) strncpy(first, terminaltype, sizeof(first));
28038904Sborman 	    for(;;) {
28138904Sborman 		/*
28238904Sborman 		 * Save the unknown name, and request the next name.
28338904Sborman 		 */
28438904Sborman 		(void) strncpy(last, terminaltype, sizeof(last));
28538904Sborman 		_gettermname();
28638904Sborman 		if (terminaltypeok(&terminaltype[5]))
28738904Sborman 		    break;
28838904Sborman 		if (strncmp(last, terminaltype, sizeof(last)) == 0) {
28938904Sborman 		    /*
29038904Sborman 		     * We've hit the end.  If this is the same as
29138904Sborman 		     * the first name, just go with it.
29238904Sborman 		     */
29338904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) == 0))
29438904Sborman 			break;
29538904Sborman 		    /*
29638904Sborman 		     * Get the terminal name one more type, so that
29738904Sborman 		     * RFC1091 compliant telnets will cycle back to
29838904Sborman 		     * the start of the list.
29938904Sborman 		     */
30038904Sborman 		    _gettermname();
30138904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) != 0))
30238904Sborman 			(void) strncpy(terminaltype, first, sizeof(first));
30338904Sborman 		    break;
30438904Sborman 		}
30538904Sborman 	    }
30627983Sminshall 	}
30727983Sminshall     }
30838904Sborman }  /* end of getterminaltype */
30938904Sborman 
31038904Sborman _gettermname()
31138904Sborman {
31238904Sborman     settimer(baseline);
31338904Sborman     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
31438904Sborman     nfrontp += sizeof ttytype_sbbuf;
31538904Sborman     while (sequenceIs(ttypesubopt, baseline))
31638904Sborman 	ttloop();
31727649Sminshall }
31827649Sminshall 
31938904Sborman terminaltypeok(s)
32038904Sborman char *s;
32138904Sborman {
32238904Sborman     char buf[1024];
32338904Sborman 
32438904Sborman     if (terminaltype == NULL)
32538904Sborman 	return(1);
32638904Sborman 
32738904Sborman     /*
32838904Sborman      * tgetent() will return 1 if the type is known, and
32938904Sborman      * 0 if it is not known.  If it returns -1, it couldn't
33038904Sborman      * open the database.  But if we can't open the database,
33138904Sborman      * it won't help to say we failed, because we won't be
33238904Sborman      * able to verify anything else.  So, we treat -1 like 1.
33338904Sborman      */
33438904Sborman     if (tgetent(buf, s) == 0)
33538904Sborman 	return(0);
33638904Sborman     return(1);
33738904Sborman }
33838904Sborman 
3396002Sroot /*
3406002Sroot  * Get a pty, scan input lines.
3416002Sroot  */
34238904Sborman doit(who)
34312683Ssam 	struct sockaddr_in *who;
3446002Sroot {
34520188Skarels 	char *host, *inet_ntoa();
34638904Sborman 	int t;
34712683Ssam 	struct hostent *hp;
3486002Sroot 
34938904Sborman 	/*
35038904Sborman 	 * Find an available pty to use.
35138904Sborman 	 */
35238904Sborman 	pty = getpty();
35338904Sborman 	if (pty < 0)
35438904Sborman 		fatal(net, "All network ports in use");
35520188Skarels 
35638904Sborman 	t = getptyslave();
35738904Sborman 
35838904Sborman 	/* get name of connected client */
35938904Sborman 	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
36012683Ssam 		who->sin_family);
36112683Ssam 	if (hp)
36212683Ssam 		host = hp->h_name;
36312683Ssam 	else
36417444Sralph 		host = inet_ntoa(who->sin_addr);
36527983Sminshall 
36627983Sminshall 	/*
36738904Sborman 	 * get terminal type.
36827983Sminshall 	 */
36927983Sminshall 	getterminaltype();
37038904Sborman 	if (terminaltype == NULL)
37138904Sborman 		terminaltype = "TERM=network";
37227983Sminshall 
37327649Sminshall 	/*
37438904Sborman 	 * Start up the login process on the slave side of the terminal
37527649Sminshall 	 */
37638904Sborman 	startslave(t, host);
37738904Sborman 
37838904Sborman 	telnet(net, pty);  /* begin server processing */
3799244Ssam 	/*NOTREACHED*/
38038904Sborman }  /* end of doit */
3819244Ssam 
38238904Sborman #ifndef	MAXHOSTNAMELEN
38338904Sborman #define	MAXHOSTNAMELEN 64
38438904Sborman #endif	MAXHOSTNAMELEN
3856002Sroot /*
3866002Sroot  * Main loop.  Select from pty and network, and
3876002Sroot  * hand data to telnet receiver finite state machine.
3886002Sroot  */
3896002Sroot telnet(f, p)
39038904Sborman int f, p;
3916002Sroot {
3926002Sroot 	int on = 1;
39327898Skarels 	char hostname[MAXHOSTNAMELEN];
394*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
39538904Sborman 	int termstat();
39638904Sborman 	int interrupt(), sendbrk();
39738904Sborman #endif
39833271Sminshall #define	TABBUFSIZ	512
39933271Sminshall 	char	defent[TABBUFSIZ];
40033271Sminshall 	char	defstrs[TABBUFSIZ];
40133271Sminshall #undef	TABBUFSIZ
40233271Sminshall 	char *HE;
40333271Sminshall 	char *HN;
40433271Sminshall 	char *IM;
40538904Sborman 	void netflush();
40638904Sborman 
40732400Sminshall 	/*
40838904Sborman 	 * Initialize the slc mapping table.
40932400Sminshall 	 */
41038904Sborman 	get_slc_defaults();
4116002Sroot 
4128379Ssam 	/*
41338904Sborman 	 * Do some tests where it is desireable to wait for a response.
41438904Sborman 	 * Rather than doing them slowly, one at a time, do them all
41538904Sborman 	 * at once.
4168379Ssam 	 */
41738904Sborman 	if (!myopts[TELOPT_SGA])
41839503Sborman 		send_will(TELOPT_SGA, 1);
41912713Ssam 	/*
42027649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
42127649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
42227649Sminshall 	 *
42327649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
42427649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
42527649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
42627649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
42727649Sminshall 	 * echo the terminal input).
42827649Sminshall 	 */
42939503Sborman 	send_do(TELOPT_ECHO, 1);
43027649Sminshall 
43138904Sborman #ifdef	LINEMODE
43238904Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
43338904Sborman 		/* Query the peer for linemode support by trying to negotiate
43438904Sborman 		 * the linemode option.
43538904Sborman 		 */
43638904Sborman 		linemode = 1;
43738904Sborman 		editmode = 0;
43839503Sborman 		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
43938904Sborman 	}
44038904Sborman #endif	/* LINEMODE */
44138904Sborman 
44227649Sminshall 	/*
44338904Sborman 	 * Send along a couple of other options that we wish to negotiate.
44438904Sborman 	 */
44539503Sborman 	send_do(TELOPT_NAWS, 1);
44639503Sborman 	send_will(TELOPT_STATUS, 1);
44738904Sborman 	flowmode = 1;  /* default flow control state */
44839503Sborman 	send_do(TELOPT_LFLOW, 1);
44938904Sborman 
45038904Sborman 	/*
45138904Sborman 	 * Spin, waiting for a response from the DO ECHO.  However,
45238904Sborman 	 * some REALLY DUMB telnets out there might not respond
45338904Sborman 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
45438904Sborman 	 * telnets so far seem to respond with WONT for a DO that
45538904Sborman 	 * they don't understand...) because by the time we get the
45638904Sborman 	 * response, it will already have processed the DO ECHO.
45738904Sborman 	 * Kludge upon kludge.
45838904Sborman 	 */
45938904Sborman 	while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
46038904Sborman 		ttloop();
46138904Sborman 
46238904Sborman 	/*
46338995Sborman 	 * On the off chance that the telnet client is broken and does not
46438995Sborman 	 * respond to the DO ECHO we sent, (after all, we did send the
46538995Sborman 	 * DO NAWS negotiation after the DO ECHO, and we won't get here
46638995Sborman 	 * until a response to the DO NAWS comes back) simulate the
46738995Sborman 	 * receipt of a will echo.  This will also send a WONT ECHO
46838995Sborman 	 * to the client, since we assume that the client failed to
46938995Sborman 	 * respond because it believes that it is already in DO ECHO
47038995Sborman 	 * mode, which we do not want.
47138995Sborman 	 */
472*40242Sborman 	if (hiswants[TELOPT_ECHO] == OPT_YES) {
47339503Sborman 		willoption(TELOPT_ECHO);
474*40242Sborman 	}
47538995Sborman 
47638995Sborman 	/*
47738995Sborman 	 * Finally, to clean things up, we turn on our echo.  This
47838995Sborman 	 * will break stupid 4.2 telnets out of local terminal echo.
47938995Sborman 	 */
48038995Sborman 
48138995Sborman 	if (!myopts[TELOPT_ECHO])
48239503Sborman 		send_will(TELOPT_ECHO, 1);
48338995Sborman 
48438995Sborman 	/*
48538904Sborman 	 * Turn on packet mode, and default to line at at time mode.
48638904Sborman 	 */
48738904Sborman 	(void) ioctl(p, TIOCPKT, (char *)&on);
48838904Sborman #ifdef	LINEMODE
48938904Sborman 	tty_setlinemode(1);
49038904Sborman 
49138904Sborman # ifdef	KLUDGELINEMODE
49238904Sborman 	/*
49338904Sborman 	 * Continuing line mode support.  If client does not support
49438904Sborman 	 * real linemode, attempt to negotiate kludge linemode by sending
49538904Sborman 	 * the do timing mark sequence.
49638904Sborman 	 */
49738904Sborman 	if (lmodetype < REAL_LINEMODE)
49839503Sborman 		send_do(TELOPT_TM, 1);
49938904Sborman # endif	/* KLUDGELINEMODE */
50038904Sborman #endif	/* LINEMODE */
50138904Sborman 
50238904Sborman 	/*
50338904Sborman 	 * Call telrcv() once to pick up anything received during
50438904Sborman 	 * terminal type negotiation, 4.2/4.3 determination, and
50538904Sborman 	 * linemode negotiation.
50638904Sborman 	 */
50738904Sborman 	telrcv();
50838904Sborman 
50938904Sborman 	(void) ioctl(f, FIONBIO, (char *)&on);
51038904Sborman 	(void) ioctl(p, FIONBIO, (char *)&on);
511*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
51238904Sborman 	init_termdriver(f, p, interrupt, sendbrk);
51338904Sborman #endif
51438904Sborman 
51538904Sborman #if	defined(SO_OOBINLINE)
51638904Sborman 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
51738904Sborman #endif	/* defined(SO_OOBINLINE) */
51838904Sborman 
51938904Sborman #ifdef	SIGTSTP
52038904Sborman 	(void) signal(SIGTSTP, SIG_IGN);
52138904Sborman #endif
52238904Sborman #ifdef	SIGTTOU
52338904Sborman 	/*
52438904Sborman 	 * Ignoring SIGTTOU keeps the kernel from blocking us
52538904Sborman 	 * in ttioct() in /sys/tty.c.
52638904Sborman 	 */
52738904Sborman 	(void) signal(SIGTTOU, SIG_IGN);
52838904Sborman #endif
52938904Sborman 
53038904Sborman 	(void) signal(SIGCHLD, cleanup);
53138904Sborman 
532*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
53338904Sborman 	/*
53438904Sborman 	 * Cray-2 will send a signal when pty modes are changed by slave
53538904Sborman 	 * side.  Set up signal handler now.
53638904Sborman 	 */
53738904Sborman 	if ((int)signal(SIGUSR1, termstat) < 0)
53838904Sborman 		perror("signal");
53938904Sborman 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
54038904Sborman 		perror("ioctl:TCSIGME");
54138904Sborman 	/*
54238904Sborman 	 * Make processing loop check terminal characteristics early on.
54338904Sborman 	 */
54438904Sborman 	termstat();
54538904Sborman #endif
54638904Sborman 
54738904Sborman 	(void) setpgrp(0, 0);
548*40242Sborman #ifdef	TCSETCTTY
549*40242Sborman 	ioctl(p, TCSETCTTY, 0);
550*40242Sborman #endif
55138904Sborman 
55238904Sborman 	/*
55312713Ssam 	 * Show banner that getty never gave.
55427797Sminshall 	 *
55533271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
55633271Sminshall 	 * gets carriage return null processing, etc., just like all
55733271Sminshall 	 * other pty --> client data.
55812713Ssam 	 */
55927797Sminshall 
56038904Sborman 	(void) gethostname(hostname, sizeof (hostname));
56138904Sborman 
56233271Sminshall 	if (getent(defent, "default") == 1) {
56333271Sminshall 		char *getstr();
56438904Sborman 		char *cp=defstrs;
56527649Sminshall 
56638904Sborman 		HE = getstr("he", &cp);
56738904Sborman 		HN = getstr("hn", &cp);
56838904Sborman 		IM = getstr("im", &cp);
56933271Sminshall 		if (HN && *HN)
57038904Sborman 			(void) strcpy(hostname, HN);
57138904Sborman 		if (IM == 0)
57238904Sborman 			IM = "";
57333271Sminshall 	} else {
57438904Sborman #ifdef	CRAY
57538904Sborman 		if (hostinfo == 0)
57638904Sborman 			IM = 0;
57738904Sborman 		else
57838904Sborman #endif
57938904Sborman 			IM = DEFAULT_IM;
58038904Sborman 		HE = 0;
58133271Sminshall 	}
58238904Sborman 	edithost(HE, hostname);
58338904Sborman 	if (IM && *IM)
58438904Sborman 		putf(IM, ptyibuf2);
58527797Sminshall 
58638904Sborman 	if (pcc)
58738904Sborman 		(void) strncat(ptyibuf2, ptyip, pcc+1);
58838904Sborman 	ptyip = ptyibuf2;
58938904Sborman 	pcc = strlen(ptyip);
590*40242Sborman #ifdef	LINEMODE
591*40242Sborman 	/*
592*40242Sborman 	 * Last check to make sure all our states are correct.
593*40242Sborman 	 */
594*40242Sborman 	init_termbuf();
595*40242Sborman 	localstat();
596*40242Sborman #endif	/* LINEMODE */
59733271Sminshall 
5986002Sroot 	for (;;) {
59927185Sminshall 		fd_set ibits, obits, xbits;
6006002Sroot 		register int c;
6016002Sroot 
60227185Sminshall 		if (ncc < 0 && pcc < 0)
60327185Sminshall 			break;
60427185Sminshall 
605*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
60638904Sborman 		if (needtermstat)
60738904Sborman 			_termstat();
608*40242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
60927185Sminshall 		FD_ZERO(&ibits);
61027185Sminshall 		FD_ZERO(&obits);
61127185Sminshall 		FD_ZERO(&xbits);
6126002Sroot 		/*
6136002Sroot 		 * Never look for input if there's still
6146002Sroot 		 * stuff in the corresponding output buffer
6156002Sroot 		 */
61627185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
61727185Sminshall 			FD_SET(f, &obits);
61827185Sminshall 		} else {
61927185Sminshall 			FD_SET(p, &ibits);
62027185Sminshall 		}
62127185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
62227185Sminshall 			FD_SET(p, &obits);
62327185Sminshall 		} else {
62427185Sminshall 			FD_SET(f, &ibits);
62527185Sminshall 		}
62627185Sminshall 		if (!SYNCHing) {
62727185Sminshall 			FD_SET(f, &xbits);
62827185Sminshall 		}
62927185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
63027185Sminshall 						(struct timeval *)0)) < 1) {
63127185Sminshall 			if (c == -1) {
63227185Sminshall 				if (errno == EINTR) {
63327185Sminshall 					continue;
63427185Sminshall 				}
63527185Sminshall 			}
6366002Sroot 			sleep(5);
6376002Sroot 			continue;
6386002Sroot 		}
6396002Sroot 
6406002Sroot 		/*
64127185Sminshall 		 * Any urgent data?
64227185Sminshall 		 */
64327185Sminshall 		if (FD_ISSET(net, &xbits)) {
64427185Sminshall 		    SYNCHing = 1;
64527185Sminshall 		}
64627185Sminshall 
64727185Sminshall 		/*
6486002Sroot 		 * Something to read from the network...
6496002Sroot 		 */
65027185Sminshall 		if (FD_ISSET(net, &ibits)) {
65127649Sminshall #if	!defined(SO_OOBINLINE)
65227185Sminshall 			/*
65327898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
65427185Sminshall 			 * OOB indication and data handling in the kernel
65527185Sminshall 			 * is such that if two separate TCP Urgent requests
65627185Sminshall 			 * come in, one byte of TCP data will be overlaid.
65727185Sminshall 			 * This is fatal for Telnet, but we try to live
65827185Sminshall 			 * with it.
65927185Sminshall 			 *
66027185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
66127185Sminshall 			 * is needed to pick up the TCP Urgent data in
66227185Sminshall 			 * the correct sequence.
66327185Sminshall 			 *
66427185Sminshall 			 * What we do is:  if we think we are in urgent
66527185Sminshall 			 * mode, we look to see if we are "at the mark".
66627185Sminshall 			 * If we are, we do an OOB receive.  If we run
66727185Sminshall 			 * this twice, we will do the OOB receive twice,
66827185Sminshall 			 * but the second will fail, since the second
66927185Sminshall 			 * time we were "at the mark", but there wasn't
67027185Sminshall 			 * any data there (the kernel doesn't reset
67127185Sminshall 			 * "at the mark" until we do a normal read).
67227185Sminshall 			 * Once we've read the OOB data, we go ahead
67327185Sminshall 			 * and do normal reads.
67427185Sminshall 			 *
67527185Sminshall 			 * There is also another problem, which is that
67627185Sminshall 			 * since the OOB byte we read doesn't put us
67727185Sminshall 			 * out of OOB state, and since that byte is most
67827185Sminshall 			 * likely the TELNET DM (data mark), we would
67927185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
68027185Sminshall 			 * So, clocks to the rescue.  If we've "just"
68127185Sminshall 			 * received a DM, then we test for the
68227185Sminshall 			 * presence of OOB data when the receive OOB
68327185Sminshall 			 * fails (and AFTER we did the normal mode read
68427185Sminshall 			 * to clear "at the mark").
68527185Sminshall 			 */
68627185Sminshall 		    if (SYNCHing) {
68727185Sminshall 			int atmark;
68827185Sminshall 
68938904Sborman 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
69027185Sminshall 			if (atmark) {
69127185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
69227185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
69327185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
69427983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
69527185Sminshall 				    SYNCHing = stilloob(net);
69627185Sminshall 				}
69727185Sminshall 			    }
69827185Sminshall 			} else {
69927185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
7006002Sroot 			}
70127185Sminshall 		    } else {
70227185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
70327185Sminshall 		    }
70427185Sminshall 		    settimer(didnetreceive);
70527649Sminshall #else	/* !defined(SO_OOBINLINE)) */
70627185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
70727649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
70827185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
70927185Sminshall 			ncc = 0;
71027185Sminshall 		    else {
71127185Sminshall 			if (ncc <= 0) {
71227185Sminshall 			    break;
71327185Sminshall 			}
71427185Sminshall 			netip = netibuf;
71527185Sminshall 		    }
7166002Sroot 		}
7176002Sroot 
7186002Sroot 		/*
7196002Sroot 		 * Something to read from the pty...
7206002Sroot 		 */
72138904Sborman 		if (FD_ISSET(p, &ibits)) {
7226002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
7236002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
7246002Sroot 				pcc = 0;
7256002Sroot 			else {
7266002Sroot 				if (pcc <= 0)
7276002Sroot 					break;
728*40242Sborman #if	!defined(CRAY2) || !defined(UNICOS5)
72938904Sborman #ifdef	LINEMODE
73038904Sborman 				/*
73138904Sborman 				 * If ioctl from pty, pass it through net
73238904Sborman 				 */
73338904Sborman 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
73438904Sborman 					copy_termbuf(ptyibuf+1, pcc-1);
73538904Sborman 					localstat();
73638904Sborman 					pcc = 1;
73738904Sborman 				}
73838904Sborman #endif	LINEMODE
73937210Sminshall 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
74038904Sborman 					netclear();	/* clear buffer back */
741*40242Sborman #ifdef	notdef
742*40242Sborman 					/*
743*40242Sborman 					 * We really should have this in, but
744*40242Sborman 					 * there are client telnets on some
745*40242Sborman 					 * operating systems get screwed up
746*40242Sborman 					 * royally if we send them urgent
747*40242Sborman 					 * mode data.  So, for now, we'll not
748*40242Sborman 					 * do this...
749*40242Sborman 					 */
75037210Sminshall 					*nfrontp++ = IAC;
75137210Sminshall 					*nfrontp++ = DM;
75237210Sminshall 					neturg = nfrontp-1; /* off by one XXX */
753*40242Sborman #endif
75437210Sminshall 				}
75537210Sminshall 				if (hisopts[TELOPT_LFLOW] &&
75637210Sminshall 				    (ptyibuf[0] &
75738904Sborman 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
75838904Sborman 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
75937210Sminshall 					    IAC, SB, TELOPT_LFLOW,
76037210Sminshall 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
76137210Sminshall 					    IAC, SE);
76237210Sminshall 					nfrontp += 6;
76337210Sminshall 				}
76433267Sminshall 				pcc--;
76533267Sminshall 				ptyip = ptyibuf+1;
766*40242Sborman #else	/* defined(CRAY2) && defined(UNICOS5) */
76738904Sborman 				if (!uselinemode) {
76839531Sborman 					unpcc = pcc;
76939531Sborman 					unptyip = ptyibuf;
77039531Sborman 					pcc = term_output(&unptyip, ptyibuf2,
77139531Sborman 								&unpcc, BUFSIZ);
77238904Sborman 					ptyip = ptyibuf2;
77338904Sborman 				} else
77438904Sborman 					ptyip = ptyibuf;
775*40242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
77638904Sborman 			}
7776002Sroot 		}
7786002Sroot 
7796002Sroot 		while (pcc > 0) {
7806002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
7816002Sroot 				break;
7826002Sroot 			c = *ptyip++ & 0377, pcc--;
7836002Sroot 			if (c == IAC)
7846002Sroot 				*nfrontp++ = c;
785*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
78638904Sborman 			else if (c == '\n' &&
78738904Sborman 				     myopts[TELOPT_BINARY] == OPT_NO && newmap)
78838904Sborman 				*nfrontp++ = '\r';
789*40242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
7906002Sroot 			*nfrontp++ = c;
79131940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
79227020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
79327020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
79427020Sminshall 					pcc--;
79527020Sminshall 				} else
79627020Sminshall 					*nfrontp++ = '\0';
79727020Sminshall 			}
7986002Sroot 		}
799*40242Sborman #if	defined(CRAY2) && defined(UNICOS5)
80039531Sborman 		/*
80139531Sborman 		 * If chars were left over from the terminal driver,
80239531Sborman 		 * note their existence.
80339531Sborman 		 */
80439531Sborman 		 if (!uselinemode && unpcc) {
80539531Sborman 			pcc = unpcc;
80639531Sborman 			unpcc = 0;
80739531Sborman 			ptyip = unptyip;
80839531Sborman 		}
809*40242Sborman #endif	/* defined(CRAY2) && defined(UNICOS5) */
81039531Sborman 
81127185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
8126002Sroot 			netflush();
8136002Sroot 		if (ncc > 0)
8146002Sroot 			telrcv();
81527185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
8166002Sroot 			ptyflush();
8176002Sroot 	}
8186002Sroot 	cleanup();
81938904Sborman }  /* end of telnet */
8206002Sroot 
82138904Sborman #ifndef	TCSIG
82238904Sborman # ifdef	TIOCSIG
82338904Sborman #  define TCSIG TIOCSIG
82438904Sborman # endif
82538904Sborman #endif
8266002Sroot 
82737212Sminshall /*
8286002Sroot  * Send interrupt to process on other side of pty.
8296002Sroot  * If it is in raw mode, just write NULL;
8306002Sroot  * otherwise, write intr char.
8316002Sroot  */
8326002Sroot interrupt()
8336002Sroot {
83438904Sborman 	ptyflush();	/* half-hearted */
8356002Sroot 
83638904Sborman #ifdef	TCSIG
83738904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
83838904Sborman #else	/* TCSIG */
83938904Sborman 	init_termbuf();
840*40242Sborman 	*pfrontp++ = slctab[SLC_IP].sptr ?
841*40242Sborman 			(unsigned char)*slctab[SLC_IP].sptr : '\177';
84238904Sborman #endif	/* TCSIG */
8436002Sroot }
8446002Sroot 
84527229Sminshall /*
84627229Sminshall  * Send quit to process on other side of pty.
84727229Sminshall  * If it is in raw mode, just write NULL;
84827229Sminshall  * otherwise, write quit char.
84927229Sminshall  */
85027229Sminshall sendbrk()
85127229Sminshall {
85227229Sminshall 	ptyflush();	/* half-hearted */
85338904Sborman #ifdef	TCSIG
85438904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
85538904Sborman #else	/* TCSIG */
85638904Sborman 	init_termbuf();
857*40242Sborman 	*pfrontp++ = slctab[SLC_ABORT].sptr ?
858*40242Sborman 			(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
85938904Sborman #endif	/* TCSIG */
86027229Sminshall }
86127229Sminshall 
86238904Sborman sendsusp()
8636002Sroot {
86438904Sborman #ifdef	SIGTSTP
86538904Sborman 	ptyflush();	/* half-hearted */
86638904Sborman # ifdef	TCSIG
86738904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
86838904Sborman # else	/* TCSIG */
869*40242Sborman 	*pfrontp++ = slctab[SLC_SUSP].sptr ?
870*40242Sborman 			(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
87138904Sborman # endif	/* TCSIG */
87238904Sborman #endif	/* SIGTSTP */
8736002Sroot }
8746002Sroot 
87538904Sborman doeof()
8766002Sroot {
877*40242Sborman #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
878*40242Sborman 	extern char oldeofc;
879*40242Sborman #endif
88038904Sborman 	init_termbuf();
8816002Sroot 
882*40242Sborman #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
883*40242Sborman 	if (!tty_isediting()) {
884*40242Sborman 		*pfrontp++ = oldeofc;
885*40242Sborman 		return;
886*40242Sborman 	}
887*40242Sborman #endif
888*40242Sborman 	*pfrontp++ = slctab[SLC_EOF].sptr ?
889*40242Sborman 			(unsigned char)*slctab[SLC_EOF].sptr : '\004';
8906002Sroot }
891