xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 38995)
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*38995Sborman static char sccsid[] = "@(#)telnetd.c	5.39 (Berkeley) 09/05/89";
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 */
4338904Sborman int	lowpty = 0, highpty = 128;	/* low, high pty numbers */
4438904Sborman #endif /* CRAY */
4512216Ssam 
4638904Sborman int debug = 0;
4738904Sborman char *progname;
489218Ssam 
4938904Sborman main(argc, argv)
5038904Sborman 	char *argv[];
5138904Sborman {
5238904Sborman 	struct sockaddr_in from;
5338904Sborman 	int on = 1, fromlen;
546002Sroot 
5538904Sborman 	pfrontp = pbackp = ptyobuf;
5638904Sborman 	netip = netibuf;
5738904Sborman 	nfrontp = nbackp = netobuf;
586002Sroot 
5938904Sborman 	progname = *argv;
6038904Sborman top:
6138904Sborman 	argc--, argv++;
6227649Sminshall 
6338904Sborman 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
6438904Sborman 		debug++;
6538904Sborman 		goto top;
6638904Sborman 	}
6727649Sminshall 
6838904Sborman #ifdef	LINEMODE
6938904Sborman 	if (argc > 0 && !strcmp(*argv, "-l")) {
7038904Sborman 		alwayslinemode = 1;
7138904Sborman 		goto top;
7238904Sborman 	}
7338904Sborman #endif	/* LINEMODE */
7427649Sminshall 
7538904Sborman #ifdef CRAY
7638904Sborman 	if (argc > 0 && !strcmp(*argv, "-h")) {
7738904Sborman 		hostinfo = 0;
7838904Sborman 		goto top;
7938904Sborman 	}
8027649Sminshall 
8138904Sborman 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
8238904Sborman 		char *strchr();
8338904Sborman 		char *c;
8427649Sminshall 
8538904Sborman 		*argv += 2;
8638904Sborman 		if (**argv == '\0' && argc)
8738904Sborman 			argv++, argc--;
8838904Sborman 		c = strchr(*argv, '-');
8938904Sborman 		if (c) {
9038904Sborman 			*c++ = '\0';
9138904Sborman 			highpty = atoi(c);
9238904Sborman 		} else
9338904Sborman 			highpty = -1;
9438904Sborman 		lowpty = atoi(*argv);
9538904Sborman 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) {
9638904Sborman 	usage:
9738904Sborman 			fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
9838904Sborman # ifdef	NEWINIT
9938904Sborman 			fprintf(stderr, "[-Iinitid] ");
10038904Sborman # endif	/* NEWINIT */
10138904Sborman 			fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n");
10238904Sborman 			exit(1);
10338904Sborman 		}
10438904Sborman 		goto top;
10538904Sborman 	}
10638904Sborman # ifdef	NEWINIT
10738904Sborman 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
10838904Sborman 		extern char *gen_id;
10927649Sminshall 
11038904Sborman 		*argv += 2;
11138904Sborman 		if (**argv == '\0') {
11238904Sborman 			if (argc < 2)
11338904Sborman 				goto usage;
11438904Sborman 			argv++, argc--;
11538904Sborman 			if (**argv == '\0')
11638904Sborman 				goto usage;
11738904Sborman 		}
11838904Sborman 		gen_id = *argv;
11938904Sborman 		goto top;
12038904Sborman 	}
12138904Sborman # endif	/* NEWINIT */
12238904Sborman #endif	/* CRAY */
1236002Sroot 
12438904Sborman 	if (debug) {
12527185Sminshall 	    int s, ns, foo;
12627185Sminshall 	    struct servent *sp;
12727185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
12827185Sminshall 
12938904Sborman 	    if (argc > 0) {
13038904Sborman 		    if (sp = getservbyname(*argv, "tcp")) {
13138904Sborman 			sin.sin_port = sp->s_port;
13238904Sborman 		    } else {
13338904Sborman 			sin.sin_port = atoi(*argv);
13438904Sborman 			if ((int)sin.sin_port <= 0) {
13538904Sborman 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
13638904Sborman 			    exit(1);
13738904Sborman 			}
13838904Sborman 			sin.sin_port = htons((u_short)sin.sin_port);
13938904Sborman 		   }
14037210Sminshall 	    } else {
14137210Sminshall 		sp = getservbyname("telnet", "tcp");
14237210Sminshall 		if (sp == 0) {
14337210Sminshall 			fprintf(stderr,
14437210Sminshall 				"telnetd: tcp/telnet: unknown service\n");
14538904Sborman 		    exit(1);
14637210Sminshall 		}
14737210Sminshall 		sin.sin_port = sp->s_port;
14827185Sminshall 	    }
14927185Sminshall 
15027185Sminshall 	    s = socket(AF_INET, SOCK_STREAM, 0);
15127185Sminshall 	    if (s < 0) {
15227185Sminshall 		    perror("telnetd: socket");;
15327185Sminshall 		    exit(1);
15427185Sminshall 	    }
15538904Sborman 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
15638904Sborman 	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
15727185Sminshall 		perror("bind");
15827185Sminshall 		exit(1);
15927185Sminshall 	    }
16027185Sminshall 	    if (listen(s, 1) < 0) {
16127185Sminshall 		perror("listen");
16227185Sminshall 		exit(1);
16327185Sminshall 	    }
16427185Sminshall 	    foo = sizeof sin;
16538904Sborman 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
16627185Sminshall 	    if (ns < 0) {
16727185Sminshall 		perror("accept");
16827185Sminshall 		exit(1);
16927185Sminshall 	    }
17038904Sborman 	    (void) dup2(ns, 0);
17138904Sborman 	    (void) close(ns);
17238904Sborman 	    (void) close(s);
17327185Sminshall 	}
17438904Sborman 
17524855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
17616371Skarels 	fromlen = sizeof (from);
17738904Sborman 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
17838904Sborman 		fprintf(stderr, "%s: ", progname);
17916371Skarels 		perror("getpeername");
18016371Skarels 		_exit(1);
1818346Ssam 	}
18217156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
18317187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
18410418Ssam 	}
18538904Sborman 	net = 0;
18638904Sborman 	doit(&from);
18738904Sborman 	/* NOTREACHED */
18838904Sborman }  /* end of main */
1896002Sroot 
19027983Sminshall int	cleanup();
19127649Sminshall 
19227649Sminshall /*
19327983Sminshall  * getterminaltype
19427649Sminshall  *
19538904Sborman  *	Ask the other end to send along its terminal type and speed.
19627983Sminshall  * Output is the variable terminaltype filled in.
19727649Sminshall  */
19838904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
19927983Sminshall void
20027983Sminshall getterminaltype()
20127649Sminshall {
20238904Sborman     void ttloop();
20327649Sminshall 
20438904Sborman     settimer(baseline);
20538904Sborman     willoption(TELOPT_TTYPE, 1);
20638904Sborman     willoption(TELOPT_TSPEED, 1);
20738904Sborman     while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
20838904Sborman 	   (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
20927983Sminshall 	ttloop();
21027649Sminshall     }
21138904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
21238904Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
21327983Sminshall 
21427983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
21527983Sminshall 	nfrontp += sizeof sbbuf;
21638904Sborman     }
21738904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
21838904Sborman 
21938904Sborman 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
22038904Sborman 	nfrontp += sizeof ttytype_sbbuf;
22138904Sborman     }
22238904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
22338904Sborman 	while (sequenceIs(tspeedsubopt, baseline))
22427983Sminshall 	    ttloop();
22538904Sborman     }
22638904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
22738904Sborman 	char first[256], last[256];
22838904Sborman 
22938904Sborman 	while (sequenceIs(ttypesubopt, baseline))
23038904Sborman 	    ttloop();
23138904Sborman 
23238904Sborman 	if (!terminaltypeok(&terminaltype[5])) {
23338904Sborman 	    (void) strncpy(first, terminaltype, sizeof(first));
23438904Sborman 	    for(;;) {
23538904Sborman 		/*
23638904Sborman 		 * Save the unknown name, and request the next name.
23738904Sborman 		 */
23838904Sborman 		(void) strncpy(last, terminaltype, sizeof(last));
23938904Sborman 		_gettermname();
24038904Sborman 		if (terminaltypeok(&terminaltype[5]))
24138904Sborman 		    break;
24238904Sborman 		if (strncmp(last, terminaltype, sizeof(last)) == 0) {
24338904Sborman 		    /*
24438904Sborman 		     * We've hit the end.  If this is the same as
24538904Sborman 		     * the first name, just go with it.
24638904Sborman 		     */
24738904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) == 0))
24838904Sborman 			break;
24938904Sborman 		    /*
25038904Sborman 		     * Get the terminal name one more type, so that
25138904Sborman 		     * RFC1091 compliant telnets will cycle back to
25238904Sborman 		     * the start of the list.
25338904Sborman 		     */
25438904Sborman 		    _gettermname();
25538904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) != 0))
25638904Sborman 			(void) strncpy(terminaltype, first, sizeof(first));
25738904Sborman 		    break;
25838904Sborman 		}
25938904Sborman 	    }
26027983Sminshall 	}
26127983Sminshall     }
26238904Sborman }  /* end of getterminaltype */
26338904Sborman 
26438904Sborman _gettermname()
26538904Sborman {
26638904Sborman     settimer(baseline);
26738904Sborman     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
26838904Sborman     nfrontp += sizeof ttytype_sbbuf;
26938904Sborman     while (sequenceIs(ttypesubopt, baseline))
27038904Sborman 	ttloop();
27127649Sminshall }
27227649Sminshall 
27338904Sborman terminaltypeok(s)
27438904Sborman char *s;
27538904Sborman {
27638904Sborman     char buf[1024];
27738904Sborman 
27838904Sborman     if (terminaltype == NULL)
27938904Sborman 	return(1);
28038904Sborman 
28138904Sborman     /*
28238904Sborman      * tgetent() will return 1 if the type is known, and
28338904Sborman      * 0 if it is not known.  If it returns -1, it couldn't
28438904Sborman      * open the database.  But if we can't open the database,
28538904Sborman      * it won't help to say we failed, because we won't be
28638904Sborman      * able to verify anything else.  So, we treat -1 like 1.
28738904Sborman      */
28838904Sborman     if (tgetent(buf, s) == 0)
28938904Sborman 	return(0);
29038904Sborman     return(1);
29138904Sborman }
29238904Sborman 
2936002Sroot /*
2946002Sroot  * Get a pty, scan input lines.
2956002Sroot  */
29638904Sborman doit(who)
29712683Ssam 	struct sockaddr_in *who;
2986002Sroot {
29920188Skarels 	char *host, *inet_ntoa();
30038904Sborman 	int t;
30112683Ssam 	struct hostent *hp;
3026002Sroot 
30338904Sborman 	/*
30438904Sborman 	 * Find an available pty to use.
30538904Sborman 	 */
30638904Sborman 	pty = getpty();
30738904Sborman 	if (pty < 0)
30838904Sborman 		fatal(net, "All network ports in use");
30920188Skarels 
31038904Sborman 	t = getptyslave();
31138904Sborman 
31238904Sborman 	/* get name of connected client */
31338904Sborman 	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
31412683Ssam 		who->sin_family);
31512683Ssam 	if (hp)
31612683Ssam 		host = hp->h_name;
31712683Ssam 	else
31817444Sralph 		host = inet_ntoa(who->sin_addr);
31927983Sminshall 
32027983Sminshall 	/*
32138904Sborman 	 * get terminal type.
32227983Sminshall 	 */
32327983Sminshall 	getterminaltype();
32438904Sborman 	if (terminaltype == NULL)
32538904Sborman 		terminaltype = "TERM=network";
32627983Sminshall 
32727649Sminshall 	/*
32838904Sborman 	 * Start up the login process on the slave side of the terminal
32927649Sminshall 	 */
33038904Sborman 	startslave(t, host);
33138904Sborman 
33238904Sborman 	telnet(net, pty);  /* begin server processing */
3339244Ssam 	/*NOTREACHED*/
33438904Sborman }  /* end of doit */
3359244Ssam 
33638904Sborman #ifndef	MAXHOSTNAMELEN
33738904Sborman #define	MAXHOSTNAMELEN 64
33838904Sborman #endif	MAXHOSTNAMELEN
3396002Sroot /*
3406002Sroot  * Main loop.  Select from pty and network, and
3416002Sroot  * hand data to telnet receiver finite state machine.
3426002Sroot  */
3436002Sroot telnet(f, p)
34438904Sborman int f, p;
3456002Sroot {
3466002Sroot 	int on = 1;
34727898Skarels 	char hostname[MAXHOSTNAMELEN];
34838904Sborman #ifdef	CRAY2
34938904Sborman 	int termstat();
35038904Sborman 	int interrupt(), sendbrk();
35138904Sborman #endif
35233271Sminshall #define	TABBUFSIZ	512
35333271Sminshall 	char	defent[TABBUFSIZ];
35433271Sminshall 	char	defstrs[TABBUFSIZ];
35533271Sminshall #undef	TABBUFSIZ
35633271Sminshall 	char *HE;
35733271Sminshall 	char *HN;
35833271Sminshall 	char *IM;
35938904Sborman 	void netflush();
36038904Sborman 
36132400Sminshall 	/*
36238904Sborman 	 * Initialize the slc mapping table.
36332400Sminshall 	 */
36438904Sborman 	get_slc_defaults();
3656002Sroot 
3668379Ssam 	/*
36738904Sborman 	 * Do some tests where it is desireable to wait for a response.
36838904Sborman 	 * Rather than doing them slowly, one at a time, do them all
36938904Sborman 	 * at once.
3708379Ssam 	 */
37138904Sborman 	if (!myopts[TELOPT_SGA])
37238904Sborman 		dooption(TELOPT_SGA);
37312713Ssam 	/*
37427649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
37527649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
37627649Sminshall 	 *
37727649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
37827649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
37927649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
38027649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
38127649Sminshall 	 * echo the terminal input).
38227649Sminshall 	 */
38338904Sborman 	willoption(TELOPT_ECHO, 1);
38427649Sminshall 
38538904Sborman #ifdef	LINEMODE
38638904Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
38738904Sborman 		/* Query the peer for linemode support by trying to negotiate
38838904Sborman 		 * the linemode option.
38938904Sborman 		 */
39038904Sborman 		linemode = 1;
39138904Sborman 		editmode = 0;
39238904Sborman 		willoption(TELOPT_LINEMODE, 1);  /* send do linemode */
39338904Sborman 	}
39438904Sborman #endif	/* LINEMODE */
39538904Sborman 
39627649Sminshall 	/*
39738904Sborman 	 * Send along a couple of other options that we wish to negotiate.
39838904Sborman 	 */
39938904Sborman 	willoption(TELOPT_NAWS, 1);
40038904Sborman 	dooption(TELOPT_STATUS, 1);
40138904Sborman 	flowmode = 1;  /* default flow control state */
40238904Sborman 	willoption(TELOPT_LFLOW, 1);
40338904Sborman 
40438904Sborman 	/*
40538904Sborman 	 * Spin, waiting for a response from the DO ECHO.  However,
40638904Sborman 	 * some REALLY DUMB telnets out there might not respond
40738904Sborman 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
40838904Sborman 	 * telnets so far seem to respond with WONT for a DO that
40938904Sborman 	 * they don't understand...) because by the time we get the
41038904Sborman 	 * response, it will already have processed the DO ECHO.
41138904Sborman 	 * Kludge upon kludge.
41238904Sborman 	 */
41338904Sborman 	while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
41438904Sborman 		ttloop();
41538904Sborman 
41638904Sborman 	/*
417*38995Sborman 	 * On the off chance that the telnet client is broken and does not
418*38995Sborman 	 * respond to the DO ECHO we sent, (after all, we did send the
419*38995Sborman 	 * DO NAWS negotiation after the DO ECHO, and we won't get here
420*38995Sborman 	 * until a response to the DO NAWS comes back) simulate the
421*38995Sborman 	 * receipt of a will echo.  This will also send a WONT ECHO
422*38995Sborman 	 * to the client, since we assume that the client failed to
423*38995Sborman 	 * respond because it believes that it is already in DO ECHO
424*38995Sborman 	 * mode, which we do not want.
425*38995Sborman 	 */
426*38995Sborman 	if (hiswants[TELOPT_ECHO] == OPT_YES)
427*38995Sborman 		willoption(TELOPT_ECHO, 0);
428*38995Sborman 
429*38995Sborman 	/*
430*38995Sborman 	 * Finally, to clean things up, we turn on our echo.  This
431*38995Sborman 	 * will break stupid 4.2 telnets out of local terminal echo.
432*38995Sborman 	 */
433*38995Sborman 
434*38995Sborman 	if (!myopts[TELOPT_ECHO])
435*38995Sborman 		dooption(TELOPT_ECHO);
436*38995Sborman 
437*38995Sborman 	/*
43838904Sborman 	 * Turn on packet mode, and default to line at at time mode.
43938904Sborman 	 */
44038904Sborman 	(void) ioctl(p, TIOCPKT, (char *)&on);
44138904Sborman #ifdef	LINEMODE
44238904Sborman 	tty_setlinemode(1);
44338904Sborman 
44438904Sborman # ifdef	KLUDGELINEMODE
44538904Sborman 	/*
44638904Sborman 	 * Continuing line mode support.  If client does not support
44738904Sborman 	 * real linemode, attempt to negotiate kludge linemode by sending
44838904Sborman 	 * the do timing mark sequence.
44938904Sborman 	 */
45038904Sborman 	if (lmodetype < REAL_LINEMODE)
45138904Sborman 		willoption(TELOPT_TM, 1);
45238904Sborman # endif	/* KLUDGELINEMODE */
45338904Sborman #endif	/* LINEMODE */
45438904Sborman 
45538904Sborman 	/*
45638904Sborman 	 * Call telrcv() once to pick up anything received during
45738904Sborman 	 * terminal type negotiation, 4.2/4.3 determination, and
45838904Sborman 	 * linemode negotiation.
45938904Sborman 	 */
46038904Sborman 	telrcv();
46138904Sborman 
46238904Sborman 	(void) ioctl(f, FIONBIO, (char *)&on);
46338904Sborman 	(void) ioctl(p, FIONBIO, (char *)&on);
46438904Sborman #ifdef	CRAY2
46538904Sborman 	init_termdriver(f, p, interrupt, sendbrk);
46638904Sborman #endif
46738904Sborman 
46838904Sborman #if	defined(SO_OOBINLINE)
46938904Sborman 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
47038904Sborman #endif	/* defined(SO_OOBINLINE) */
47138904Sborman 
47238904Sborman #ifdef	SIGTSTP
47338904Sborman 	(void) signal(SIGTSTP, SIG_IGN);
47438904Sborman #endif
47538904Sborman #ifdef	SIGTTOU
47638904Sborman 	/*
47738904Sborman 	 * Ignoring SIGTTOU keeps the kernel from blocking us
47838904Sborman 	 * in ttioct() in /sys/tty.c.
47938904Sborman 	 */
48038904Sborman 	(void) signal(SIGTTOU, SIG_IGN);
48138904Sborman #endif
48238904Sborman 
48338904Sborman 	(void) signal(SIGCHLD, cleanup);
48438904Sborman 
48538904Sborman #if	defined(CRAY2)
48638904Sborman 	/*
48738904Sborman 	 * Cray-2 will send a signal when pty modes are changed by slave
48838904Sborman 	 * side.  Set up signal handler now.
48938904Sborman 	 */
49038904Sborman 	if ((int)signal(SIGUSR1, termstat) < 0)
49138904Sborman 		perror("signal");
49238904Sborman 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
49338904Sborman 		perror("ioctl:TCSIGME");
49438904Sborman 	/*
49538904Sborman 	 * Make processing loop check terminal characteristics early on.
49638904Sborman 	 */
49738904Sborman 	termstat();
49838904Sborman #endif
49938904Sborman 
50038904Sborman 	(void) setpgrp(0, 0);
50138904Sborman 
50238904Sborman 	/*
50312713Ssam 	 * Show banner that getty never gave.
50427797Sminshall 	 *
50533271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
50633271Sminshall 	 * gets carriage return null processing, etc., just like all
50733271Sminshall 	 * other pty --> client data.
50812713Ssam 	 */
50927797Sminshall 
51038904Sborman 	(void) gethostname(hostname, sizeof (hostname));
51138904Sborman 
51233271Sminshall 	if (getent(defent, "default") == 1) {
51333271Sminshall 		char *getstr();
51438904Sborman 		char *cp=defstrs;
51527649Sminshall 
51638904Sborman 		HE = getstr("he", &cp);
51738904Sborman 		HN = getstr("hn", &cp);
51838904Sborman 		IM = getstr("im", &cp);
51933271Sminshall 		if (HN && *HN)
52038904Sborman 			(void) strcpy(hostname, HN);
52138904Sborman 		if (IM == 0)
52238904Sborman 			IM = "";
52333271Sminshall 	} else {
52438904Sborman #ifdef	CRAY
52538904Sborman 		if (hostinfo == 0)
52638904Sborman 			IM = 0;
52738904Sborman 		else
52838904Sborman #endif
52938904Sborman 			IM = DEFAULT_IM;
53038904Sborman 		HE = 0;
53133271Sminshall 	}
53238904Sborman 	edithost(HE, hostname);
53338904Sborman 	if (IM && *IM)
53438904Sborman 		putf(IM, ptyibuf2);
53527797Sminshall 
53638904Sborman 	if (pcc)
53738904Sborman 		(void) strncat(ptyibuf2, ptyip, pcc+1);
53838904Sborman 	ptyip = ptyibuf2;
53938904Sborman 	pcc = strlen(ptyip);
54033271Sminshall 
5416002Sroot 	for (;;) {
54227185Sminshall 		fd_set ibits, obits, xbits;
5436002Sroot 		register int c;
5446002Sroot 
54527185Sminshall 		if (ncc < 0 && pcc < 0)
54627185Sminshall 			break;
54727185Sminshall 
54838904Sborman #ifdef	CRAY2
54938904Sborman 		if (needtermstat)
55038904Sborman 			_termstat();
55138904Sborman #endif	/* CRAY2 */
55227185Sminshall 		FD_ZERO(&ibits);
55327185Sminshall 		FD_ZERO(&obits);
55427185Sminshall 		FD_ZERO(&xbits);
5556002Sroot 		/*
5566002Sroot 		 * Never look for input if there's still
5576002Sroot 		 * stuff in the corresponding output buffer
5586002Sroot 		 */
55927185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
56027185Sminshall 			FD_SET(f, &obits);
56127185Sminshall 		} else {
56227185Sminshall 			FD_SET(p, &ibits);
56327185Sminshall 		}
56427185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
56527185Sminshall 			FD_SET(p, &obits);
56627185Sminshall 		} else {
56727185Sminshall 			FD_SET(f, &ibits);
56827185Sminshall 		}
56927185Sminshall 		if (!SYNCHing) {
57027185Sminshall 			FD_SET(f, &xbits);
57127185Sminshall 		}
57227185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
57327185Sminshall 						(struct timeval *)0)) < 1) {
57427185Sminshall 			if (c == -1) {
57527185Sminshall 				if (errno == EINTR) {
57627185Sminshall 					continue;
57727185Sminshall 				}
57827185Sminshall 			}
5796002Sroot 			sleep(5);
5806002Sroot 			continue;
5816002Sroot 		}
5826002Sroot 
5836002Sroot 		/*
58427185Sminshall 		 * Any urgent data?
58527185Sminshall 		 */
58627185Sminshall 		if (FD_ISSET(net, &xbits)) {
58727185Sminshall 		    SYNCHing = 1;
58827185Sminshall 		}
58927185Sminshall 
59027185Sminshall 		/*
5916002Sroot 		 * Something to read from the network...
5926002Sroot 		 */
59327185Sminshall 		if (FD_ISSET(net, &ibits)) {
59427649Sminshall #if	!defined(SO_OOBINLINE)
59527185Sminshall 			/*
59627898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
59727185Sminshall 			 * OOB indication and data handling in the kernel
59827185Sminshall 			 * is such that if two separate TCP Urgent requests
59927185Sminshall 			 * come in, one byte of TCP data will be overlaid.
60027185Sminshall 			 * This is fatal for Telnet, but we try to live
60127185Sminshall 			 * with it.
60227185Sminshall 			 *
60327185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
60427185Sminshall 			 * is needed to pick up the TCP Urgent data in
60527185Sminshall 			 * the correct sequence.
60627185Sminshall 			 *
60727185Sminshall 			 * What we do is:  if we think we are in urgent
60827185Sminshall 			 * mode, we look to see if we are "at the mark".
60927185Sminshall 			 * If we are, we do an OOB receive.  If we run
61027185Sminshall 			 * this twice, we will do the OOB receive twice,
61127185Sminshall 			 * but the second will fail, since the second
61227185Sminshall 			 * time we were "at the mark", but there wasn't
61327185Sminshall 			 * any data there (the kernel doesn't reset
61427185Sminshall 			 * "at the mark" until we do a normal read).
61527185Sminshall 			 * Once we've read the OOB data, we go ahead
61627185Sminshall 			 * and do normal reads.
61727185Sminshall 			 *
61827185Sminshall 			 * There is also another problem, which is that
61927185Sminshall 			 * since the OOB byte we read doesn't put us
62027185Sminshall 			 * out of OOB state, and since that byte is most
62127185Sminshall 			 * likely the TELNET DM (data mark), we would
62227185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
62327185Sminshall 			 * So, clocks to the rescue.  If we've "just"
62427185Sminshall 			 * received a DM, then we test for the
62527185Sminshall 			 * presence of OOB data when the receive OOB
62627185Sminshall 			 * fails (and AFTER we did the normal mode read
62727185Sminshall 			 * to clear "at the mark").
62827185Sminshall 			 */
62927185Sminshall 		    if (SYNCHing) {
63027185Sminshall 			int atmark;
63127185Sminshall 
63238904Sborman 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
63327185Sminshall 			if (atmark) {
63427185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
63527185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
63627185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
63727983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
63827185Sminshall 				    SYNCHing = stilloob(net);
63927185Sminshall 				}
64027185Sminshall 			    }
64127185Sminshall 			} else {
64227185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
6436002Sroot 			}
64427185Sminshall 		    } else {
64527185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
64627185Sminshall 		    }
64727185Sminshall 		    settimer(didnetreceive);
64827649Sminshall #else	/* !defined(SO_OOBINLINE)) */
64927185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
65027649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
65127185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
65227185Sminshall 			ncc = 0;
65327185Sminshall 		    else {
65427185Sminshall 			if (ncc <= 0) {
65527185Sminshall 			    break;
65627185Sminshall 			}
65727185Sminshall 			netip = netibuf;
65827185Sminshall 		    }
6596002Sroot 		}
6606002Sroot 
6616002Sroot 		/*
6626002Sroot 		 * Something to read from the pty...
6636002Sroot 		 */
66438904Sborman 		if (FD_ISSET(p, &ibits)) {
6656002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
6666002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
6676002Sroot 				pcc = 0;
6686002Sroot 			else {
6696002Sroot 				if (pcc <= 0)
6706002Sroot 					break;
67138904Sborman #ifndef	CRAY2
67238904Sborman #ifdef	LINEMODE
67338904Sborman 				/*
67438904Sborman 				 * If ioctl from pty, pass it through net
67538904Sborman 				 */
67638904Sborman 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
67738904Sborman 					copy_termbuf(ptyibuf+1, pcc-1);
67838904Sborman 					localstat();
67938904Sborman 					pcc = 1;
68038904Sborman 				}
68138904Sborman #endif	LINEMODE
68237210Sminshall 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
68338904Sborman 					netclear();	/* clear buffer back */
68437210Sminshall 					*nfrontp++ = IAC;
68537210Sminshall 					*nfrontp++ = DM;
68637210Sminshall 					neturg = nfrontp-1; /* off by one XXX */
68737210Sminshall 				}
68837210Sminshall 				if (hisopts[TELOPT_LFLOW] &&
68937210Sminshall 				    (ptyibuf[0] &
69038904Sborman 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
69138904Sborman 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
69237210Sminshall 					    IAC, SB, TELOPT_LFLOW,
69337210Sminshall 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
69437210Sminshall 					    IAC, SE);
69537210Sminshall 					nfrontp += 6;
69637210Sminshall 				}
69733267Sminshall 				pcc--;
69833267Sminshall 				ptyip = ptyibuf+1;
69938904Sborman #else	/* CRAY2 */
70038904Sborman 				if (!uselinemode) {
70138904Sborman 					pcc = term_output(ptyibuf, ptyibuf2,
70238904Sborman 								pcc, BUFSIZ);
70338904Sborman 					ptyip = ptyibuf2;
70438904Sborman 				} else
70538904Sborman 					ptyip = ptyibuf;
70638904Sborman #endif	/* CRAY2 */
70738904Sborman 			}
7086002Sroot 		}
7096002Sroot 
7106002Sroot 		while (pcc > 0) {
7116002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
7126002Sroot 				break;
7136002Sroot 			c = *ptyip++ & 0377, pcc--;
7146002Sroot 			if (c == IAC)
7156002Sroot 				*nfrontp++ = c;
71638904Sborman #ifdef	CRAY2
71738904Sborman 			else if (c == '\n' &&
71838904Sborman 				     myopts[TELOPT_BINARY] == OPT_NO && newmap)
71938904Sborman 				*nfrontp++ = '\r';
72038904Sborman #endif	/* CRAY2 */
7216002Sroot 			*nfrontp++ = c;
72231940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
72327020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
72427020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
72527020Sminshall 					pcc--;
72627020Sminshall 				} else
72727020Sminshall 					*nfrontp++ = '\0';
72827020Sminshall 			}
7296002Sroot 		}
73027185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
7316002Sroot 			netflush();
7326002Sroot 		if (ncc > 0)
7336002Sroot 			telrcv();
73427185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
7356002Sroot 			ptyflush();
7366002Sroot 	}
7376002Sroot 	cleanup();
73838904Sborman }  /* end of telnet */
7396002Sroot 
74038904Sborman #ifndef	TCSIG
74138904Sborman # ifdef	TIOCSIG
74238904Sborman #  define TCSIG TIOCSIG
74338904Sborman # endif
74438904Sborman #endif
7456002Sroot 
74637212Sminshall /*
7476002Sroot  * Send interrupt to process on other side of pty.
7486002Sroot  * If it is in raw mode, just write NULL;
7496002Sroot  * otherwise, write intr char.
7506002Sroot  */
7516002Sroot interrupt()
7526002Sroot {
75338904Sborman 	ptyflush();	/* half-hearted */
7546002Sroot 
75538904Sborman #ifdef	TCSIG
75638904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
75738904Sborman #else	/* TCSIG */
75838904Sborman 	init_termbuf();
75938904Sborman 	*pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177';
76038904Sborman #endif	/* TCSIG */
7616002Sroot }
7626002Sroot 
76327229Sminshall /*
76427229Sminshall  * Send quit to process on other side of pty.
76527229Sminshall  * If it is in raw mode, just write NULL;
76627229Sminshall  * otherwise, write quit char.
76727229Sminshall  */
76827229Sminshall sendbrk()
76927229Sminshall {
77027229Sminshall 	ptyflush();	/* half-hearted */
77138904Sborman #ifdef	TCSIG
77238904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
77338904Sborman #else	/* TCSIG */
77438904Sborman 	init_termbuf();
77538904Sborman 	*pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034';
77638904Sborman #endif	/* TCSIG */
77727229Sminshall }
77827229Sminshall 
77938904Sborman sendsusp()
7806002Sroot {
78138904Sborman #ifdef	SIGTSTP
78238904Sborman 	ptyflush();	/* half-hearted */
78338904Sborman # ifdef	TCSIG
78438904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
78538904Sborman # else	/* TCSIG */
78638904Sborman 	*pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032';
78738904Sborman # endif	/* TCSIG */
78838904Sborman #endif	/* SIGTSTP */
7896002Sroot }
7906002Sroot 
79138904Sborman doeof()
7926002Sroot {
79338904Sborman 	init_termbuf();
7946002Sroot 
79538904Sborman 	*pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004';
7966002Sroot }
797