xref: /csrg-svn/libexec/telnetd/telnetd.c (revision 38904)
121182Sdist /*
2*38904Sborman  * 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[] =
20*38904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\
2121182Sdist  All rights reserved.\n";
2233687Sbostic #endif /* not lint */
236295Sroot 
2421182Sdist #ifndef lint
25*38904Sborman static char sccsid[] = "@(#)telnetd.c	5.38 (Berkeley) 09/01/89";
2633687Sbostic #endif /* not lint */
2721182Sdist 
28*38904Sborman #include "telnetd.h"
29*38904Sborman 
306002Sroot /*
31*38904Sborman  * I/O data buffers,
32*38904Sborman  * pointers, and counters.
336002Sroot  */
34*38904Sborman char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
35*38904Sborman char	ptyibuf2[BUFSIZ];
369218Ssam 
37*38904Sborman #ifdef	CRAY
38*38904Sborman int	hostinfo = 1;			/* do we print login banner? */
39*38904Sborman #endif
409218Ssam 
41*38904Sborman #ifdef	CRAY
42*38904Sborman extern int      newmap; /* nonzero if \n maps to ^M^J */
43*38904Sborman int	lowpty = 0, highpty = 128;	/* low, high pty numbers */
44*38904Sborman #endif /* CRAY */
4512216Ssam 
46*38904Sborman int debug = 0;
47*38904Sborman char *progname;
489218Ssam 
49*38904Sborman main(argc, argv)
50*38904Sborman 	char *argv[];
51*38904Sborman {
52*38904Sborman 	struct sockaddr_in from;
53*38904Sborman 	int on = 1, fromlen;
546002Sroot 
55*38904Sborman 	pfrontp = pbackp = ptyobuf;
56*38904Sborman 	netip = netibuf;
57*38904Sborman 	nfrontp = nbackp = netobuf;
586002Sroot 
59*38904Sborman 	progname = *argv;
60*38904Sborman top:
61*38904Sborman 	argc--, argv++;
6227649Sminshall 
63*38904Sborman 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
64*38904Sborman 		debug++;
65*38904Sborman 		goto top;
66*38904Sborman 	}
6727649Sminshall 
68*38904Sborman #ifdef	LINEMODE
69*38904Sborman 	if (argc > 0 && !strcmp(*argv, "-l")) {
70*38904Sborman 		alwayslinemode = 1;
71*38904Sborman 		goto top;
72*38904Sborman 	}
73*38904Sborman #endif	/* LINEMODE */
7427649Sminshall 
75*38904Sborman #ifdef CRAY
76*38904Sborman 	if (argc > 0 && !strcmp(*argv, "-h")) {
77*38904Sborman 		hostinfo = 0;
78*38904Sborman 		goto top;
79*38904Sborman 	}
8027649Sminshall 
81*38904Sborman 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
82*38904Sborman 		char *strchr();
83*38904Sborman 		char *c;
8427649Sminshall 
85*38904Sborman 		*argv += 2;
86*38904Sborman 		if (**argv == '\0' && argc)
87*38904Sborman 			argv++, argc--;
88*38904Sborman 		c = strchr(*argv, '-');
89*38904Sborman 		if (c) {
90*38904Sborman 			*c++ = '\0';
91*38904Sborman 			highpty = atoi(c);
92*38904Sborman 		} else
93*38904Sborman 			highpty = -1;
94*38904Sborman 		lowpty = atoi(*argv);
95*38904Sborman 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) {
96*38904Sborman 	usage:
97*38904Sborman 			fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
98*38904Sborman # ifdef	NEWINIT
99*38904Sborman 			fprintf(stderr, "[-Iinitid] ");
100*38904Sborman # endif	/* NEWINIT */
101*38904Sborman 			fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n");
102*38904Sborman 			exit(1);
103*38904Sborman 		}
104*38904Sborman 		goto top;
105*38904Sborman 	}
106*38904Sborman # ifdef	NEWINIT
107*38904Sborman 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
108*38904Sborman 		extern char *gen_id;
10927649Sminshall 
110*38904Sborman 		*argv += 2;
111*38904Sborman 		if (**argv == '\0') {
112*38904Sborman 			if (argc < 2)
113*38904Sborman 				goto usage;
114*38904Sborman 			argv++, argc--;
115*38904Sborman 			if (**argv == '\0')
116*38904Sborman 				goto usage;
117*38904Sborman 		}
118*38904Sborman 		gen_id = *argv;
119*38904Sborman 		goto top;
120*38904Sborman 	}
121*38904Sborman # endif	/* NEWINIT */
122*38904Sborman #endif	/* CRAY */
1236002Sroot 
124*38904Sborman 	if (debug) {
12527185Sminshall 	    int s, ns, foo;
12627185Sminshall 	    struct servent *sp;
12727185Sminshall 	    static struct sockaddr_in sin = { AF_INET };
12827185Sminshall 
129*38904Sborman 	    if (argc > 0) {
130*38904Sborman 		    if (sp = getservbyname(*argv, "tcp")) {
131*38904Sborman 			sin.sin_port = sp->s_port;
132*38904Sborman 		    } else {
133*38904Sborman 			sin.sin_port = atoi(*argv);
134*38904Sborman 			if ((int)sin.sin_port <= 0) {
135*38904Sborman 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
136*38904Sborman 			    exit(1);
137*38904Sborman 			}
138*38904Sborman 			sin.sin_port = htons((u_short)sin.sin_port);
139*38904Sborman 		   }
14037210Sminshall 	    } else {
14137210Sminshall 		sp = getservbyname("telnet", "tcp");
14237210Sminshall 		if (sp == 0) {
14337210Sminshall 			fprintf(stderr,
14437210Sminshall 				"telnetd: tcp/telnet: unknown service\n");
145*38904Sborman 		    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 	    }
155*38904Sborman 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
156*38904Sborman 	    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;
165*38904Sborman 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
16627185Sminshall 	    if (ns < 0) {
16727185Sminshall 		perror("accept");
16827185Sminshall 		exit(1);
16927185Sminshall 	    }
170*38904Sborman 	    (void) dup2(ns, 0);
171*38904Sborman 	    (void) close(ns);
172*38904Sborman 	    (void) close(s);
17327185Sminshall 	}
174*38904Sborman 
17524855Seric 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
17616371Skarels 	fromlen = sizeof (from);
177*38904Sborman 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
178*38904Sborman 		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 	}
185*38904Sborman 	net = 0;
186*38904Sborman 	doit(&from);
187*38904Sborman 	/* NOTREACHED */
188*38904Sborman }  /* end of main */
1896002Sroot 
19027983Sminshall int	cleanup();
19127649Sminshall 
19227649Sminshall /*
19327983Sminshall  * getterminaltype
19427649Sminshall  *
195*38904Sborman  *	Ask the other end to send along its terminal type and speed.
19627983Sminshall  * Output is the variable terminaltype filled in.
19727649Sminshall  */
198*38904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
19927983Sminshall void
20027983Sminshall getterminaltype()
20127649Sminshall {
202*38904Sborman     void ttloop();
20327649Sminshall 
204*38904Sborman     settimer(baseline);
205*38904Sborman     willoption(TELOPT_TTYPE, 1);
206*38904Sborman     willoption(TELOPT_TSPEED, 1);
207*38904Sborman     while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
208*38904Sborman 	   (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
20927983Sminshall 	ttloop();
21027649Sminshall     }
211*38904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
212*38904Sborman 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
21327983Sminshall 
21427983Sminshall 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
21527983Sminshall 	nfrontp += sizeof sbbuf;
216*38904Sborman     }
217*38904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
218*38904Sborman 
219*38904Sborman 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
220*38904Sborman 	nfrontp += sizeof ttytype_sbbuf;
221*38904Sborman     }
222*38904Sborman     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
223*38904Sborman 	while (sequenceIs(tspeedsubopt, baseline))
22427983Sminshall 	    ttloop();
225*38904Sborman     }
226*38904Sborman     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
227*38904Sborman 	char first[256], last[256];
228*38904Sborman 
229*38904Sborman 	while (sequenceIs(ttypesubopt, baseline))
230*38904Sborman 	    ttloop();
231*38904Sborman 
232*38904Sborman 	if (!terminaltypeok(&terminaltype[5])) {
233*38904Sborman 	    (void) strncpy(first, terminaltype, sizeof(first));
234*38904Sborman 	    for(;;) {
235*38904Sborman 		/*
236*38904Sborman 		 * Save the unknown name, and request the next name.
237*38904Sborman 		 */
238*38904Sborman 		(void) strncpy(last, terminaltype, sizeof(last));
239*38904Sborman 		_gettermname();
240*38904Sborman 		if (terminaltypeok(&terminaltype[5]))
241*38904Sborman 		    break;
242*38904Sborman 		if (strncmp(last, terminaltype, sizeof(last)) == 0) {
243*38904Sborman 		    /*
244*38904Sborman 		     * We've hit the end.  If this is the same as
245*38904Sborman 		     * the first name, just go with it.
246*38904Sborman 		     */
247*38904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) == 0))
248*38904Sborman 			break;
249*38904Sborman 		    /*
250*38904Sborman 		     * Get the terminal name one more type, so that
251*38904Sborman 		     * RFC1091 compliant telnets will cycle back to
252*38904Sborman 		     * the start of the list.
253*38904Sborman 		     */
254*38904Sborman 		    _gettermname();
255*38904Sborman 		    if (strncmp(first, terminaltype, sizeof(first) != 0))
256*38904Sborman 			(void) strncpy(terminaltype, first, sizeof(first));
257*38904Sborman 		    break;
258*38904Sborman 		}
259*38904Sborman 	    }
26027983Sminshall 	}
26127983Sminshall     }
262*38904Sborman }  /* end of getterminaltype */
263*38904Sborman 
264*38904Sborman _gettermname()
265*38904Sborman {
266*38904Sborman     settimer(baseline);
267*38904Sborman     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
268*38904Sborman     nfrontp += sizeof ttytype_sbbuf;
269*38904Sborman     while (sequenceIs(ttypesubopt, baseline))
270*38904Sborman 	ttloop();
27127649Sminshall }
27227649Sminshall 
273*38904Sborman terminaltypeok(s)
274*38904Sborman char *s;
275*38904Sborman {
276*38904Sborman     char buf[1024];
277*38904Sborman 
278*38904Sborman     if (terminaltype == NULL)
279*38904Sborman 	return(1);
280*38904Sborman 
281*38904Sborman     /*
282*38904Sborman      * tgetent() will return 1 if the type is known, and
283*38904Sborman      * 0 if it is not known.  If it returns -1, it couldn't
284*38904Sborman      * open the database.  But if we can't open the database,
285*38904Sborman      * it won't help to say we failed, because we won't be
286*38904Sborman      * able to verify anything else.  So, we treat -1 like 1.
287*38904Sborman      */
288*38904Sborman     if (tgetent(buf, s) == 0)
289*38904Sborman 	return(0);
290*38904Sborman     return(1);
291*38904Sborman }
292*38904Sborman 
2936002Sroot /*
2946002Sroot  * Get a pty, scan input lines.
2956002Sroot  */
296*38904Sborman doit(who)
29712683Ssam 	struct sockaddr_in *who;
2986002Sroot {
29920188Skarels 	char *host, *inet_ntoa();
300*38904Sborman 	int t;
30112683Ssam 	struct hostent *hp;
3026002Sroot 
303*38904Sborman 	/*
304*38904Sborman 	 * Find an available pty to use.
305*38904Sborman 	 */
306*38904Sborman 	pty = getpty();
307*38904Sborman 	if (pty < 0)
308*38904Sborman 		fatal(net, "All network ports in use");
30920188Skarels 
310*38904Sborman 	t = getptyslave();
311*38904Sborman 
312*38904Sborman 	/* get name of connected client */
313*38904Sborman 	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 	/*
321*38904Sborman 	 * get terminal type.
32227983Sminshall 	 */
32327983Sminshall 	getterminaltype();
324*38904Sborman 	if (terminaltype == NULL)
325*38904Sborman 		terminaltype = "TERM=network";
32627983Sminshall 
32727649Sminshall 	/*
328*38904Sborman 	 * Start up the login process on the slave side of the terminal
32927649Sminshall 	 */
330*38904Sborman 	startslave(t, host);
331*38904Sborman 
332*38904Sborman 	telnet(net, pty);  /* begin server processing */
3339244Ssam 	/*NOTREACHED*/
334*38904Sborman }  /* end of doit */
3359244Ssam 
336*38904Sborman #ifndef	MAXHOSTNAMELEN
337*38904Sborman #define	MAXHOSTNAMELEN 64
338*38904Sborman #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)
344*38904Sborman int f, p;
3456002Sroot {
3466002Sroot 	int on = 1;
34727898Skarels 	char hostname[MAXHOSTNAMELEN];
348*38904Sborman #ifdef	CRAY2
349*38904Sborman 	int termstat();
350*38904Sborman 	int interrupt(), sendbrk();
351*38904Sborman #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;
359*38904Sborman 	void netflush();
360*38904Sborman 
36132400Sminshall 	/*
362*38904Sborman 	 * Initialize the slc mapping table.
36332400Sminshall 	 */
364*38904Sborman 	get_slc_defaults();
3656002Sroot 
3668379Ssam 	/*
367*38904Sborman 	 * Do some tests where it is desireable to wait for a response.
368*38904Sborman 	 * Rather than doing them slowly, one at a time, do them all
369*38904Sborman 	 * at once.
3708379Ssam 	 */
371*38904Sborman 	if (!myopts[TELOPT_ECHO])
372*38904Sborman 		dooption(TELOPT_ECHO);
373*38904Sborman 	if (!myopts[TELOPT_SGA])
374*38904Sborman 		dooption(TELOPT_SGA);
37512713Ssam 	/*
37627649Sminshall 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
37727649Sminshall 	 * because 4.2 clients are unable to deal with TCP urgent data.
37827649Sminshall 	 *
37927649Sminshall 	 * To find out, we send out a "DO ECHO".  If the remote system
38027649Sminshall 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
38127649Sminshall 	 * that fact ("WILL ECHO" ==> that the client will echo what
38227649Sminshall 	 * WE, the server, sends it; it does NOT mean that the client will
38327649Sminshall 	 * echo the terminal input).
38427649Sminshall 	 */
385*38904Sborman 	willoption(TELOPT_ECHO, 1);
38627649Sminshall 
387*38904Sborman #ifdef	LINEMODE
388*38904Sborman 	if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
389*38904Sborman 		/* Query the peer for linemode support by trying to negotiate
390*38904Sborman 		 * the linemode option.
391*38904Sborman 		 */
392*38904Sborman 		linemode = 1;
393*38904Sborman 		editmode = 0;
394*38904Sborman 		willoption(TELOPT_LINEMODE, 1);  /* send do linemode */
395*38904Sborman 	}
396*38904Sborman #endif	/* LINEMODE */
397*38904Sborman 
39827649Sminshall 	/*
399*38904Sborman 	 * Send along a couple of other options that we wish to negotiate.
400*38904Sborman 	 */
401*38904Sborman 	willoption(TELOPT_NAWS, 1);
402*38904Sborman 	dooption(TELOPT_STATUS, 1);
403*38904Sborman 	flowmode = 1;  /* default flow control state */
404*38904Sborman 	willoption(TELOPT_LFLOW, 1);
405*38904Sborman 
406*38904Sborman 	/*
407*38904Sborman 	 * Spin, waiting for a response from the DO ECHO.  However,
408*38904Sborman 	 * some REALLY DUMB telnets out there might not respond
409*38904Sborman 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
410*38904Sborman 	 * telnets so far seem to respond with WONT for a DO that
411*38904Sborman 	 * they don't understand...) because by the time we get the
412*38904Sborman 	 * response, it will already have processed the DO ECHO.
413*38904Sborman 	 * Kludge upon kludge.
414*38904Sborman 	 */
415*38904Sborman 	while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
416*38904Sborman 		ttloop();
417*38904Sborman 
418*38904Sborman 	/*
419*38904Sborman 	 * Turn on packet mode, and default to line at at time mode.
420*38904Sborman 	 */
421*38904Sborman 	(void) ioctl(p, TIOCPKT, (char *)&on);
422*38904Sborman #ifdef	LINEMODE
423*38904Sborman 	tty_setlinemode(1);
424*38904Sborman 
425*38904Sborman # ifdef	KLUDGELINEMODE
426*38904Sborman 	/*
427*38904Sborman 	 * Continuing line mode support.  If client does not support
428*38904Sborman 	 * real linemode, attempt to negotiate kludge linemode by sending
429*38904Sborman 	 * the do timing mark sequence.
430*38904Sborman 	 */
431*38904Sborman 	if (lmodetype < REAL_LINEMODE)
432*38904Sborman 		willoption(TELOPT_TM, 1);
433*38904Sborman # endif	/* KLUDGELINEMODE */
434*38904Sborman #endif	/* LINEMODE */
435*38904Sborman 
436*38904Sborman 	/*
437*38904Sborman 	 * Call telrcv() once to pick up anything received during
438*38904Sborman 	 * terminal type negotiation, 4.2/4.3 determination, and
439*38904Sborman 	 * linemode negotiation.
440*38904Sborman 	 */
441*38904Sborman 	telrcv();
442*38904Sborman 
443*38904Sborman 	(void) ioctl(f, FIONBIO, (char *)&on);
444*38904Sborman 	(void) ioctl(p, FIONBIO, (char *)&on);
445*38904Sborman #ifdef	CRAY2
446*38904Sborman 	init_termdriver(f, p, interrupt, sendbrk);
447*38904Sborman #endif
448*38904Sborman 
449*38904Sborman #if	defined(SO_OOBINLINE)
450*38904Sborman 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
451*38904Sborman #endif	/* defined(SO_OOBINLINE) */
452*38904Sborman 
453*38904Sborman #ifdef	SIGTSTP
454*38904Sborman 	(void) signal(SIGTSTP, SIG_IGN);
455*38904Sborman #endif
456*38904Sborman #ifdef	SIGTTOU
457*38904Sborman 	/*
458*38904Sborman 	 * Ignoring SIGTTOU keeps the kernel from blocking us
459*38904Sborman 	 * in ttioct() in /sys/tty.c.
460*38904Sborman 	 */
461*38904Sborman 	(void) signal(SIGTTOU, SIG_IGN);
462*38904Sborman #endif
463*38904Sborman 
464*38904Sborman 	(void) signal(SIGCHLD, cleanup);
465*38904Sborman 
466*38904Sborman #if	defined(CRAY2)
467*38904Sborman 	/*
468*38904Sborman 	 * Cray-2 will send a signal when pty modes are changed by slave
469*38904Sborman 	 * side.  Set up signal handler now.
470*38904Sborman 	 */
471*38904Sborman 	if ((int)signal(SIGUSR1, termstat) < 0)
472*38904Sborman 		perror("signal");
473*38904Sborman 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
474*38904Sborman 		perror("ioctl:TCSIGME");
475*38904Sborman 	/*
476*38904Sborman 	 * Make processing loop check terminal characteristics early on.
477*38904Sborman 	 */
478*38904Sborman 	termstat();
479*38904Sborman #endif
480*38904Sborman 
481*38904Sborman 	(void) setpgrp(0, 0);
482*38904Sborman 
483*38904Sborman 	/*
48412713Ssam 	 * Show banner that getty never gave.
48527797Sminshall 	 *
48633271Sminshall 	 * We put the banner in the pty input buffer.  This way, it
48733271Sminshall 	 * gets carriage return null processing, etc., just like all
48833271Sminshall 	 * other pty --> client data.
48912713Ssam 	 */
49027797Sminshall 
491*38904Sborman 	(void) gethostname(hostname, sizeof (hostname));
492*38904Sborman 
49333271Sminshall 	if (getent(defent, "default") == 1) {
49433271Sminshall 		char *getstr();
495*38904Sborman 		char *cp=defstrs;
49627649Sminshall 
497*38904Sborman 		HE = getstr("he", &cp);
498*38904Sborman 		HN = getstr("hn", &cp);
499*38904Sborman 		IM = getstr("im", &cp);
50033271Sminshall 		if (HN && *HN)
501*38904Sborman 			(void) strcpy(hostname, HN);
502*38904Sborman 		if (IM == 0)
503*38904Sborman 			IM = "";
50433271Sminshall 	} else {
505*38904Sborman #ifdef	CRAY
506*38904Sborman 		if (hostinfo == 0)
507*38904Sborman 			IM = 0;
508*38904Sborman 		else
509*38904Sborman #endif
510*38904Sborman 			IM = DEFAULT_IM;
511*38904Sborman 		HE = 0;
51233271Sminshall 	}
513*38904Sborman 	edithost(HE, hostname);
514*38904Sborman 	if (IM && *IM)
515*38904Sborman 		putf(IM, ptyibuf2);
51627797Sminshall 
517*38904Sborman 	if (pcc)
518*38904Sborman 		(void) strncat(ptyibuf2, ptyip, pcc+1);
519*38904Sborman 	ptyip = ptyibuf2;
520*38904Sborman 	pcc = strlen(ptyip);
52133271Sminshall 
5226002Sroot 	for (;;) {
52327185Sminshall 		fd_set ibits, obits, xbits;
5246002Sroot 		register int c;
5256002Sroot 
52627185Sminshall 		if (ncc < 0 && pcc < 0)
52727185Sminshall 			break;
52827185Sminshall 
529*38904Sborman #ifdef	CRAY2
530*38904Sborman 		if (needtermstat)
531*38904Sborman 			_termstat();
532*38904Sborman #endif	/* CRAY2 */
53327185Sminshall 		FD_ZERO(&ibits);
53427185Sminshall 		FD_ZERO(&obits);
53527185Sminshall 		FD_ZERO(&xbits);
5366002Sroot 		/*
5376002Sroot 		 * Never look for input if there's still
5386002Sroot 		 * stuff in the corresponding output buffer
5396002Sroot 		 */
54027185Sminshall 		if (nfrontp - nbackp || pcc > 0) {
54127185Sminshall 			FD_SET(f, &obits);
54227185Sminshall 		} else {
54327185Sminshall 			FD_SET(p, &ibits);
54427185Sminshall 		}
54527185Sminshall 		if (pfrontp - pbackp || ncc > 0) {
54627185Sminshall 			FD_SET(p, &obits);
54727185Sminshall 		} else {
54827185Sminshall 			FD_SET(f, &ibits);
54927185Sminshall 		}
55027185Sminshall 		if (!SYNCHing) {
55127185Sminshall 			FD_SET(f, &xbits);
55227185Sminshall 		}
55327185Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
55427185Sminshall 						(struct timeval *)0)) < 1) {
55527185Sminshall 			if (c == -1) {
55627185Sminshall 				if (errno == EINTR) {
55727185Sminshall 					continue;
55827185Sminshall 				}
55927185Sminshall 			}
5606002Sroot 			sleep(5);
5616002Sroot 			continue;
5626002Sroot 		}
5636002Sroot 
5646002Sroot 		/*
56527185Sminshall 		 * Any urgent data?
56627185Sminshall 		 */
56727185Sminshall 		if (FD_ISSET(net, &xbits)) {
56827185Sminshall 		    SYNCHing = 1;
56927185Sminshall 		}
57027185Sminshall 
57127185Sminshall 		/*
5726002Sroot 		 * Something to read from the network...
5736002Sroot 		 */
57427185Sminshall 		if (FD_ISSET(net, &ibits)) {
57527649Sminshall #if	!defined(SO_OOBINLINE)
57627185Sminshall 			/*
57727898Skarels 			 * In 4.2 (and 4.3 beta) systems, the
57827185Sminshall 			 * OOB indication and data handling in the kernel
57927185Sminshall 			 * is such that if two separate TCP Urgent requests
58027185Sminshall 			 * come in, one byte of TCP data will be overlaid.
58127185Sminshall 			 * This is fatal for Telnet, but we try to live
58227185Sminshall 			 * with it.
58327185Sminshall 			 *
58427185Sminshall 			 * In addition, in 4.2 (and...), a special protocol
58527185Sminshall 			 * is needed to pick up the TCP Urgent data in
58627185Sminshall 			 * the correct sequence.
58727185Sminshall 			 *
58827185Sminshall 			 * What we do is:  if we think we are in urgent
58927185Sminshall 			 * mode, we look to see if we are "at the mark".
59027185Sminshall 			 * If we are, we do an OOB receive.  If we run
59127185Sminshall 			 * this twice, we will do the OOB receive twice,
59227185Sminshall 			 * but the second will fail, since the second
59327185Sminshall 			 * time we were "at the mark", but there wasn't
59427185Sminshall 			 * any data there (the kernel doesn't reset
59527185Sminshall 			 * "at the mark" until we do a normal read).
59627185Sminshall 			 * Once we've read the OOB data, we go ahead
59727185Sminshall 			 * and do normal reads.
59827185Sminshall 			 *
59927185Sminshall 			 * There is also another problem, which is that
60027185Sminshall 			 * since the OOB byte we read doesn't put us
60127185Sminshall 			 * out of OOB state, and since that byte is most
60227185Sminshall 			 * likely the TELNET DM (data mark), we would
60327185Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
60427185Sminshall 			 * So, clocks to the rescue.  If we've "just"
60527185Sminshall 			 * received a DM, then we test for the
60627185Sminshall 			 * presence of OOB data when the receive OOB
60727185Sminshall 			 * fails (and AFTER we did the normal mode read
60827185Sminshall 			 * to clear "at the mark").
60927185Sminshall 			 */
61027185Sminshall 		    if (SYNCHing) {
61127185Sminshall 			int atmark;
61227185Sminshall 
613*38904Sborman 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
61427185Sminshall 			if (atmark) {
61527185Sminshall 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
61627185Sminshall 			    if ((ncc == -1) && (errno == EINVAL)) {
61727185Sminshall 				ncc = read(net, netibuf, sizeof (netibuf));
61827983Sminshall 				if (sequenceIs(didnetreceive, gotDM)) {
61927185Sminshall 				    SYNCHing = stilloob(net);
62027185Sminshall 				}
62127185Sminshall 			    }
62227185Sminshall 			} else {
62327185Sminshall 			    ncc = read(net, netibuf, sizeof (netibuf));
6246002Sroot 			}
62527185Sminshall 		    } else {
62627185Sminshall 			ncc = read(net, netibuf, sizeof (netibuf));
62727185Sminshall 		    }
62827185Sminshall 		    settimer(didnetreceive);
62927649Sminshall #else	/* !defined(SO_OOBINLINE)) */
63027185Sminshall 		    ncc = read(net, netibuf, sizeof (netibuf));
63127649Sminshall #endif	/* !defined(SO_OOBINLINE)) */
63227185Sminshall 		    if (ncc < 0 && errno == EWOULDBLOCK)
63327185Sminshall 			ncc = 0;
63427185Sminshall 		    else {
63527185Sminshall 			if (ncc <= 0) {
63627185Sminshall 			    break;
63727185Sminshall 			}
63827185Sminshall 			netip = netibuf;
63927185Sminshall 		    }
6406002Sroot 		}
6416002Sroot 
6426002Sroot 		/*
6436002Sroot 		 * Something to read from the pty...
6446002Sroot 		 */
645*38904Sborman 		if (FD_ISSET(p, &ibits)) {
6466002Sroot 			pcc = read(p, ptyibuf, BUFSIZ);
6476002Sroot 			if (pcc < 0 && errno == EWOULDBLOCK)
6486002Sroot 				pcc = 0;
6496002Sroot 			else {
6506002Sroot 				if (pcc <= 0)
6516002Sroot 					break;
652*38904Sborman #ifndef	CRAY2
653*38904Sborman #ifdef	LINEMODE
654*38904Sborman 				/*
655*38904Sborman 				 * If ioctl from pty, pass it through net
656*38904Sborman 				 */
657*38904Sborman 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
658*38904Sborman 					copy_termbuf(ptyibuf+1, pcc-1);
659*38904Sborman 					localstat();
660*38904Sborman 					pcc = 1;
661*38904Sborman 				}
662*38904Sborman #endif	LINEMODE
66337210Sminshall 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
664*38904Sborman 					netclear();	/* clear buffer back */
66537210Sminshall 					*nfrontp++ = IAC;
66637210Sminshall 					*nfrontp++ = DM;
66737210Sminshall 					neturg = nfrontp-1; /* off by one XXX */
66837210Sminshall 				}
66937210Sminshall 				if (hisopts[TELOPT_LFLOW] &&
67037210Sminshall 				    (ptyibuf[0] &
671*38904Sborman 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
672*38904Sborman 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
67337210Sminshall 					    IAC, SB, TELOPT_LFLOW,
67437210Sminshall 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
67537210Sminshall 					    IAC, SE);
67637210Sminshall 					nfrontp += 6;
67737210Sminshall 				}
67833267Sminshall 				pcc--;
67933267Sminshall 				ptyip = ptyibuf+1;
680*38904Sborman #else	/* CRAY2 */
681*38904Sborman 				if (!uselinemode) {
682*38904Sborman 					pcc = term_output(ptyibuf, ptyibuf2,
683*38904Sborman 								pcc, BUFSIZ);
684*38904Sborman 					ptyip = ptyibuf2;
685*38904Sborman 				} else
686*38904Sborman 					ptyip = ptyibuf;
687*38904Sborman #endif	/* CRAY2 */
688*38904Sborman 			}
6896002Sroot 		}
6906002Sroot 
6916002Sroot 		while (pcc > 0) {
6926002Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
6936002Sroot 				break;
6946002Sroot 			c = *ptyip++ & 0377, pcc--;
6956002Sroot 			if (c == IAC)
6966002Sroot 				*nfrontp++ = c;
697*38904Sborman #ifdef	CRAY2
698*38904Sborman 			else if (c == '\n' &&
699*38904Sborman 				     myopts[TELOPT_BINARY] == OPT_NO && newmap)
700*38904Sborman 				*nfrontp++ = '\r';
701*38904Sborman #endif	/* CRAY2 */
7026002Sroot 			*nfrontp++ = c;
70331940Sbostic 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
70427020Sminshall 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
70527020Sminshall 					*nfrontp++ = *ptyip++ & 0377;
70627020Sminshall 					pcc--;
70727020Sminshall 				} else
70827020Sminshall 					*nfrontp++ = '\0';
70927020Sminshall 			}
7106002Sroot 		}
71127185Sminshall 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
7126002Sroot 			netflush();
7136002Sroot 		if (ncc > 0)
7146002Sroot 			telrcv();
71527185Sminshall 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
7166002Sroot 			ptyflush();
7176002Sroot 	}
7186002Sroot 	cleanup();
719*38904Sborman }  /* end of telnet */
7206002Sroot 
721*38904Sborman #ifndef	TCSIG
722*38904Sborman # ifdef	TIOCSIG
723*38904Sborman #  define TCSIG TIOCSIG
724*38904Sborman # endif
725*38904Sborman #endif
7266002Sroot 
72737212Sminshall /*
7286002Sroot  * Send interrupt to process on other side of pty.
7296002Sroot  * If it is in raw mode, just write NULL;
7306002Sroot  * otherwise, write intr char.
7316002Sroot  */
7326002Sroot interrupt()
7336002Sroot {
734*38904Sborman 	ptyflush();	/* half-hearted */
7356002Sroot 
736*38904Sborman #ifdef	TCSIG
737*38904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
738*38904Sborman #else	/* TCSIG */
739*38904Sborman 	init_termbuf();
740*38904Sborman 	*pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177';
741*38904Sborman #endif	/* TCSIG */
7426002Sroot }
7436002Sroot 
74427229Sminshall /*
74527229Sminshall  * Send quit to process on other side of pty.
74627229Sminshall  * If it is in raw mode, just write NULL;
74727229Sminshall  * otherwise, write quit char.
74827229Sminshall  */
74927229Sminshall sendbrk()
75027229Sminshall {
75127229Sminshall 	ptyflush();	/* half-hearted */
752*38904Sborman #ifdef	TCSIG
753*38904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
754*38904Sborman #else	/* TCSIG */
755*38904Sborman 	init_termbuf();
756*38904Sborman 	*pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034';
757*38904Sborman #endif	/* TCSIG */
75827229Sminshall }
75927229Sminshall 
760*38904Sborman sendsusp()
7616002Sroot {
762*38904Sborman #ifdef	SIGTSTP
763*38904Sborman 	ptyflush();	/* half-hearted */
764*38904Sborman # ifdef	TCSIG
765*38904Sborman 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
766*38904Sborman # else	/* TCSIG */
767*38904Sborman 	*pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032';
768*38904Sborman # endif	/* TCSIG */
769*38904Sborman #endif	/* SIGTSTP */
7706002Sroot }
7716002Sroot 
772*38904Sborman doeof()
7736002Sroot {
774*38904Sborman 	init_termbuf();
7756002Sroot 
776*38904Sborman 	*pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004';
7776002Sroot }
778