xref: /original-bsd/libexec/telnetd/telnetd.c (revision 2bd07fe6)
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1989 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)telnetd.c	5.42 (Berkeley) 02/28/90";
26 #endif /* not lint */
27 
28 #include "telnetd.h"
29 
30 /*
31  * I/O data buffers,
32  * pointers, and counters.
33  */
34 char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
35 char	ptyibuf2[BUFSIZ];
36 
37 #ifdef	CRAY
38 int	hostinfo = 1;			/* do we print login banner? */
39 #endif
40 
41 #ifdef	CRAY
42 extern int      newmap; /* nonzero if \n maps to ^M^J */
43 int	lowpty = 0, highpty;	/* low, high pty numbers */
44 #endif /* CRAY */
45 
46 int debug = 0;
47 char *progname;
48 
49 #if	defined(IP_TOS) && defined(NEED_GETTOS)
50 struct tosent {
51 	char	*t_name;	/* name */
52 	char	**t_aliases;	/* alias list */
53 	char	*t_proto;	/* protocol */
54 	int	t_tos;		/* Type Of Service bits */
55 };
56 
57 struct tosent *
58 gettosbyname(name, proto)
59 char *name, *proto;
60 {
61 	static struct tosent te;
62 	static char *aliasp = 0;
63 
64 	te.t_name = name;
65 	te.t_aliases = &aliasp;
66 	te.t_proto = proto;
67 	te.t_tos = 020;	/* Low Delay bit */
68 	return(&te);
69 }
70 #endif
71 
72 main(argc, argv)
73 	char *argv[];
74 {
75 	struct sockaddr_in from;
76 	int on = 1, fromlen;
77 #ifdef IP_TOS
78 	struct tosent *tp;
79 #endif /* IP_TOS */
80 
81 	pfrontp = pbackp = ptyobuf;
82 	netip = netibuf;
83 	nfrontp = nbackp = netobuf;
84 
85 	progname = *argv;
86 
87 #ifdef CRAY
88 	/*
89 	 * Get number of pty's before trying to process options,
90 	 * which may include changing pty range.
91 	 */
92 	highpty = getnpty();
93 #endif /* CRAY */
94 
95 top:
96 	argc--, argv++;
97 
98 	if (argc > 0 && strcmp(*argv, "-debug") == 0) {
99 		debug++;
100 		goto top;
101 	}
102 
103 #ifdef	LINEMODE
104 	if (argc > 0 && !strcmp(*argv, "-l")) {
105 		alwayslinemode = 1;
106 		goto top;
107 	}
108 #endif	/* LINEMODE */
109 
110 #ifdef CRAY
111 	if (argc > 0 && !strcmp(*argv, "-h")) {
112 		hostinfo = 0;
113 		goto top;
114 	}
115 
116 	if (argc > 0 && !strncmp(*argv, "-r", 2)) {
117 		char *strchr();
118 		char *c;
119 
120 		/*
121 		 * Allow the specification of alterations to the pty search
122 		 * range.  It is legal to specify only one, and not change the
123 		 * other from its default.
124 		 */
125 		*argv += 2;
126 		if (**argv == '\0' && argc)
127 			argv++, argc--;
128 		c = strchr(*argv, '-');
129 		if (c) {
130 			*c++ = '\0';
131 			highpty = atoi(c);
132 		}
133 		if (**argv != '\0')
134 			lowpty = atoi(*argv);
135 		if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) {
136 	usage:
137 			fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
138 # ifdef	NEWINIT
139 			fprintf(stderr, "[-Iinitid] ");
140 # endif	/* NEWINIT */
141 			fprintf(stderr, "[-l] [-r[lowpty]-[highpty]] [port]\n");
142 			exit(1);
143 		}
144 		goto top;
145 	}
146 # ifdef	NEWINIT
147 	if (argc > 0 && !strncmp(*argv, "-I", 2)) {
148 		extern char *gen_id;
149 
150 		*argv += 2;
151 		if (**argv == '\0') {
152 			if (argc < 2)
153 				goto usage;
154 			argv++, argc--;
155 			if (**argv == '\0')
156 				goto usage;
157 		}
158 		gen_id = *argv;
159 		goto top;
160 	}
161 # endif	/* NEWINIT */
162 #endif	/* CRAY */
163 
164 	if (debug) {
165 	    int s, ns, foo;
166 	    struct servent *sp;
167 	    static struct sockaddr_in sin = { AF_INET };
168 
169 	    if (argc > 0) {
170 		    if (sp = getservbyname(*argv, "tcp")) {
171 			sin.sin_port = sp->s_port;
172 		    } else {
173 			sin.sin_port = atoi(*argv);
174 			if ((int)sin.sin_port <= 0) {
175 			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
176 			    exit(1);
177 			}
178 			sin.sin_port = htons((u_short)sin.sin_port);
179 		   }
180 	    } else {
181 		sp = getservbyname("telnet", "tcp");
182 		if (sp == 0) {
183 			fprintf(stderr,
184 				"telnetd: tcp/telnet: unknown service\n");
185 		    exit(1);
186 		}
187 		sin.sin_port = sp->s_port;
188 	    }
189 
190 	    s = socket(AF_INET, SOCK_STREAM, 0);
191 	    if (s < 0) {
192 		    perror("telnetd: socket");;
193 		    exit(1);
194 	    }
195 	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
196 	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
197 		perror("bind");
198 		exit(1);
199 	    }
200 	    if (listen(s, 1) < 0) {
201 		perror("listen");
202 		exit(1);
203 	    }
204 	    foo = sizeof sin;
205 	    ns = accept(s, (struct sockaddr *)&sin, &foo);
206 	    if (ns < 0) {
207 		perror("accept");
208 		exit(1);
209 	    }
210 	    (void) dup2(ns, 0);
211 	    (void) close(ns);
212 	    (void) close(s);
213 	}
214 
215 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
216 	fromlen = sizeof (from);
217 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
218 		fprintf(stderr, "%s: ", progname);
219 		perror("getpeername");
220 		_exit(1);
221 	}
222 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
223 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
224 	}
225 
226 #ifdef IP_TOS
227 	if ((tp = gettosbyname("telnet", "tcp")) &&
228 	    (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
229 		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
230 #endif /* IP_TOS */
231 	net = 0;
232 	doit(&from);
233 	/* NOTREACHED */
234 }  /* end of main */
235 
236 void	cleanup();
237 
238 /*
239  * getterminaltype
240  *
241  *	Ask the other end to send along its terminal type and speed.
242  * Output is the variable terminaltype filled in.
243  */
244 static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
245 void
246 getterminaltype()
247 {
248     void ttloop();
249 
250     settimer(baseline);
251     send_do(TELOPT_TTYPE, 1);
252     send_do(TELOPT_TSPEED, 1);
253     while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
254 	   (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
255 	ttloop();
256     }
257     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
258 	static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
259 
260 	bcopy(sbbuf, nfrontp, sizeof sbbuf);
261 	nfrontp += sizeof sbbuf;
262     }
263     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
264 
265 	bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
266 	nfrontp += sizeof ttytype_sbbuf;
267     }
268     if (hisopts[TELOPT_TSPEED] == OPT_YES) {
269 	while (sequenceIs(tspeedsubopt, baseline))
270 	    ttloop();
271     }
272     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
273 	char first[256], last[256];
274 
275 	while (sequenceIs(ttypesubopt, baseline))
276 	    ttloop();
277 
278 	if (!terminaltypeok(&terminaltype[5])) {
279 	    (void) strncpy(first, terminaltype, sizeof(first));
280 	    for(;;) {
281 		/*
282 		 * Save the unknown name, and request the next name.
283 		 */
284 		(void) strncpy(last, terminaltype, sizeof(last));
285 		_gettermname();
286 		if (terminaltypeok(&terminaltype[5]))
287 		    break;
288 		if (strncmp(last, terminaltype, sizeof(last)) == 0) {
289 		    /*
290 		     * We've hit the end.  If this is the same as
291 		     * the first name, just go with it.
292 		     */
293 		    if (strncmp(first, terminaltype, sizeof(first) == 0))
294 			break;
295 		    /*
296 		     * Get the terminal name one more type, so that
297 		     * RFC1091 compliant telnets will cycle back to
298 		     * the start of the list.
299 		     */
300 		    _gettermname();
301 		    if (strncmp(first, terminaltype, sizeof(first) != 0))
302 			(void) strncpy(terminaltype, first, sizeof(first));
303 		    break;
304 		}
305 	    }
306 	}
307     }
308 }  /* end of getterminaltype */
309 
310 _gettermname()
311 {
312     settimer(baseline);
313     bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
314     nfrontp += sizeof ttytype_sbbuf;
315     while (sequenceIs(ttypesubopt, baseline))
316 	ttloop();
317 }
318 
319 terminaltypeok(s)
320 char *s;
321 {
322     char buf[1024];
323 
324     if (terminaltype == NULL)
325 	return(1);
326 
327     /*
328      * tgetent() will return 1 if the type is known, and
329      * 0 if it is not known.  If it returns -1, it couldn't
330      * open the database.  But if we can't open the database,
331      * it won't help to say we failed, because we won't be
332      * able to verify anything else.  So, we treat -1 like 1.
333      */
334     if (tgetent(buf, s) == 0)
335 	return(0);
336     return(1);
337 }
338 
339 /*
340  * Get a pty, scan input lines.
341  */
342 doit(who)
343 	struct sockaddr_in *who;
344 {
345 	char *host, *inet_ntoa();
346 	int t;
347 	struct hostent *hp;
348 
349 	/*
350 	 * Find an available pty to use.
351 	 */
352 	pty = getpty();
353 	if (pty < 0)
354 		fatal(net, "All network ports in use");
355 
356 	t = getptyslave();
357 
358 	/* get name of connected client */
359 	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
360 		who->sin_family);
361 	if (hp)
362 		host = hp->h_name;
363 	else
364 		host = inet_ntoa(who->sin_addr);
365 
366 	/*
367 	 * get terminal type.
368 	 */
369 	getterminaltype();
370 	if (terminaltype == NULL)
371 		terminaltype = "TERM=network";
372 
373 	/*
374 	 * Start up the login process on the slave side of the terminal
375 	 */
376 	startslave(t, host);
377 
378 	telnet(net, pty);  /* begin server processing */
379 	/*NOTREACHED*/
380 }  /* end of doit */
381 
382 #ifndef	MAXHOSTNAMELEN
383 #define	MAXHOSTNAMELEN 64
384 #endif	MAXHOSTNAMELEN
385 /*
386  * Main loop.  Select from pty and network, and
387  * hand data to telnet receiver finite state machine.
388  */
389 telnet(f, p)
390 int f, p;
391 {
392 	int on = 1;
393 	char hostname[MAXHOSTNAMELEN];
394 #if	defined(CRAY2) && defined(UNICOS5)
395 	int termstat();
396 	int interrupt(), sendbrk();
397 #endif
398 #define	TABBUFSIZ	512
399 	char	defent[TABBUFSIZ];
400 	char	defstrs[TABBUFSIZ];
401 #undef	TABBUFSIZ
402 	char *HE;
403 	char *HN;
404 	char *IM;
405 	void netflush();
406 
407 	/*
408 	 * Initialize the slc mapping table.
409 	 */
410 	get_slc_defaults();
411 
412 	/*
413 	 * Do some tests where it is desireable to wait for a response.
414 	 * Rather than doing them slowly, one at a time, do them all
415 	 * at once.
416 	 */
417 	if (!myopts[TELOPT_SGA])
418 		send_will(TELOPT_SGA, 1);
419 	/*
420 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
421 	 * because 4.2 clients are unable to deal with TCP urgent data.
422 	 *
423 	 * To find out, we send out a "DO ECHO".  If the remote system
424 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
425 	 * that fact ("WILL ECHO" ==> that the client will echo what
426 	 * WE, the server, sends it; it does NOT mean that the client will
427 	 * echo the terminal input).
428 	 */
429 	send_do(TELOPT_ECHO, 1);
430 
431 #ifdef	LINEMODE
432 	if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
433 		/* Query the peer for linemode support by trying to negotiate
434 		 * the linemode option.
435 		 */
436 		linemode = 1;
437 		editmode = 0;
438 		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
439 	}
440 #endif	/* LINEMODE */
441 
442 	/*
443 	 * Send along a couple of other options that we wish to negotiate.
444 	 */
445 	send_do(TELOPT_NAWS, 1);
446 	send_will(TELOPT_STATUS, 1);
447 	flowmode = 1;  /* default flow control state */
448 	send_do(TELOPT_LFLOW, 1);
449 
450 	/*
451 	 * Spin, waiting for a response from the DO ECHO.  However,
452 	 * some REALLY DUMB telnets out there might not respond
453 	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
454 	 * telnets so far seem to respond with WONT for a DO that
455 	 * they don't understand...) because by the time we get the
456 	 * response, it will already have processed the DO ECHO.
457 	 * Kludge upon kludge.
458 	 */
459 	while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
460 		ttloop();
461 
462 	/*
463 	 * On the off chance that the telnet client is broken and does not
464 	 * respond to the DO ECHO we sent, (after all, we did send the
465 	 * DO NAWS negotiation after the DO ECHO, and we won't get here
466 	 * until a response to the DO NAWS comes back) simulate the
467 	 * receipt of a will echo.  This will also send a WONT ECHO
468 	 * to the client, since we assume that the client failed to
469 	 * respond because it believes that it is already in DO ECHO
470 	 * mode, which we do not want.
471 	 */
472 	if (hiswants[TELOPT_ECHO] == OPT_YES) {
473 		willoption(TELOPT_ECHO);
474 	}
475 
476 	/*
477 	 * Finally, to clean things up, we turn on our echo.  This
478 	 * will break stupid 4.2 telnets out of local terminal echo.
479 	 */
480 
481 	if (!myopts[TELOPT_ECHO])
482 		send_will(TELOPT_ECHO, 1);
483 
484 	/*
485 	 * Turn on packet mode, and default to line at at time mode.
486 	 */
487 	(void) ioctl(p, TIOCPKT, (char *)&on);
488 #ifdef	LINEMODE
489 	tty_setlinemode(1);
490 
491 # ifdef	KLUDGELINEMODE
492 	/*
493 	 * Continuing line mode support.  If client does not support
494 	 * real linemode, attempt to negotiate kludge linemode by sending
495 	 * the do timing mark sequence.
496 	 */
497 	if (lmodetype < REAL_LINEMODE)
498 		send_do(TELOPT_TM, 1);
499 # endif	/* KLUDGELINEMODE */
500 #endif	/* LINEMODE */
501 
502 	/*
503 	 * Call telrcv() once to pick up anything received during
504 	 * terminal type negotiation, 4.2/4.3 determination, and
505 	 * linemode negotiation.
506 	 */
507 	telrcv();
508 
509 	(void) ioctl(f, FIONBIO, (char *)&on);
510 	(void) ioctl(p, FIONBIO, (char *)&on);
511 #if	defined(CRAY2) && defined(UNICOS5)
512 	init_termdriver(f, p, interrupt, sendbrk);
513 #endif
514 
515 #if	defined(SO_OOBINLINE)
516 	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
517 #endif	/* defined(SO_OOBINLINE) */
518 
519 #ifdef	SIGTSTP
520 	(void) signal(SIGTSTP, SIG_IGN);
521 #endif
522 #ifdef	SIGTTOU
523 	/*
524 	 * Ignoring SIGTTOU keeps the kernel from blocking us
525 	 * in ttioct() in /sys/tty.c.
526 	 */
527 	(void) signal(SIGTTOU, SIG_IGN);
528 #endif
529 
530 	(void) signal(SIGCHLD, cleanup);
531 
532 #if	defined(CRAY2) && defined(UNICOS5)
533 	/*
534 	 * Cray-2 will send a signal when pty modes are changed by slave
535 	 * side.  Set up signal handler now.
536 	 */
537 	if ((int)signal(SIGUSR1, termstat) < 0)
538 		perror("signal");
539 	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
540 		perror("ioctl:TCSIGME");
541 	/*
542 	 * Make processing loop check terminal characteristics early on.
543 	 */
544 	termstat();
545 #endif
546 
547 	(void) setpgrp(0, 0);
548 #ifdef	TCSETCTTY
549 	ioctl(p, TCSETCTTY, 0);
550 #endif
551 
552 	/*
553 	 * Show banner that getty never gave.
554 	 *
555 	 * We put the banner in the pty input buffer.  This way, it
556 	 * gets carriage return null processing, etc., just like all
557 	 * other pty --> client data.
558 	 */
559 
560 	(void) gethostname(hostname, sizeof (hostname));
561 
562 	if (getent(defent, "default") == 1) {
563 		char *getstr();
564 		char *cp=defstrs;
565 
566 		HE = getstr("he", &cp);
567 		HN = getstr("hn", &cp);
568 		IM = getstr("im", &cp);
569 		if (HN && *HN)
570 			(void) strcpy(hostname, HN);
571 		if (IM == 0)
572 			IM = "";
573 	} else {
574 #ifdef	CRAY
575 		if (hostinfo == 0)
576 			IM = 0;
577 		else
578 #endif
579 			IM = DEFAULT_IM;
580 		HE = 0;
581 	}
582 	edithost(HE, hostname);
583 	if (IM && *IM)
584 		putf(IM, ptyibuf2);
585 
586 	if (pcc)
587 		(void) strncat(ptyibuf2, ptyip, pcc+1);
588 	ptyip = ptyibuf2;
589 	pcc = strlen(ptyip);
590 #ifdef	LINEMODE
591 	/*
592 	 * Last check to make sure all our states are correct.
593 	 */
594 	init_termbuf();
595 	localstat();
596 #endif	/* LINEMODE */
597 
598 	for (;;) {
599 		fd_set ibits, obits, xbits;
600 		register int c;
601 
602 		if (ncc < 0 && pcc < 0)
603 			break;
604 
605 #if	defined(CRAY2) && defined(UNICOS5)
606 		if (needtermstat)
607 			_termstat();
608 #endif	/* defined(CRAY2) && defined(UNICOS5) */
609 		FD_ZERO(&ibits);
610 		FD_ZERO(&obits);
611 		FD_ZERO(&xbits);
612 		/*
613 		 * Never look for input if there's still
614 		 * stuff in the corresponding output buffer
615 		 */
616 		if (nfrontp - nbackp || pcc > 0) {
617 			FD_SET(f, &obits);
618 		} else {
619 			FD_SET(p, &ibits);
620 		}
621 		if (pfrontp - pbackp || ncc > 0) {
622 			FD_SET(p, &obits);
623 		} else {
624 			FD_SET(f, &ibits);
625 		}
626 		if (!SYNCHing) {
627 			FD_SET(f, &xbits);
628 		}
629 		if ((c = select(16, &ibits, &obits, &xbits,
630 						(struct timeval *)0)) < 1) {
631 			if (c == -1) {
632 				if (errno == EINTR) {
633 					continue;
634 				}
635 			}
636 			sleep(5);
637 			continue;
638 		}
639 
640 		/*
641 		 * Any urgent data?
642 		 */
643 		if (FD_ISSET(net, &xbits)) {
644 		    SYNCHing = 1;
645 		}
646 
647 		/*
648 		 * Something to read from the network...
649 		 */
650 		if (FD_ISSET(net, &ibits)) {
651 #if	!defined(SO_OOBINLINE)
652 			/*
653 			 * In 4.2 (and 4.3 beta) systems, the
654 			 * OOB indication and data handling in the kernel
655 			 * is such that if two separate TCP Urgent requests
656 			 * come in, one byte of TCP data will be overlaid.
657 			 * This is fatal for Telnet, but we try to live
658 			 * with it.
659 			 *
660 			 * In addition, in 4.2 (and...), a special protocol
661 			 * is needed to pick up the TCP Urgent data in
662 			 * the correct sequence.
663 			 *
664 			 * What we do is:  if we think we are in urgent
665 			 * mode, we look to see if we are "at the mark".
666 			 * If we are, we do an OOB receive.  If we run
667 			 * this twice, we will do the OOB receive twice,
668 			 * but the second will fail, since the second
669 			 * time we were "at the mark", but there wasn't
670 			 * any data there (the kernel doesn't reset
671 			 * "at the mark" until we do a normal read).
672 			 * Once we've read the OOB data, we go ahead
673 			 * and do normal reads.
674 			 *
675 			 * There is also another problem, which is that
676 			 * since the OOB byte we read doesn't put us
677 			 * out of OOB state, and since that byte is most
678 			 * likely the TELNET DM (data mark), we would
679 			 * stay in the TELNET SYNCH (SYNCHing) state.
680 			 * So, clocks to the rescue.  If we've "just"
681 			 * received a DM, then we test for the
682 			 * presence of OOB data when the receive OOB
683 			 * fails (and AFTER we did the normal mode read
684 			 * to clear "at the mark").
685 			 */
686 		    if (SYNCHing) {
687 			int atmark;
688 
689 			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
690 			if (atmark) {
691 			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
692 			    if ((ncc == -1) && (errno == EINVAL)) {
693 				ncc = read(net, netibuf, sizeof (netibuf));
694 				if (sequenceIs(didnetreceive, gotDM)) {
695 				    SYNCHing = stilloob(net);
696 				}
697 			    }
698 			} else {
699 			    ncc = read(net, netibuf, sizeof (netibuf));
700 			}
701 		    } else {
702 			ncc = read(net, netibuf, sizeof (netibuf));
703 		    }
704 		    settimer(didnetreceive);
705 #else	/* !defined(SO_OOBINLINE)) */
706 		    ncc = read(net, netibuf, sizeof (netibuf));
707 #endif	/* !defined(SO_OOBINLINE)) */
708 		    if (ncc < 0 && errno == EWOULDBLOCK)
709 			ncc = 0;
710 		    else {
711 			if (ncc <= 0) {
712 			    break;
713 			}
714 			netip = netibuf;
715 		    }
716 		}
717 
718 		/*
719 		 * Something to read from the pty...
720 		 */
721 		if (FD_ISSET(p, &ibits)) {
722 			pcc = read(p, ptyibuf, BUFSIZ);
723 			if (pcc < 0 && errno == EWOULDBLOCK)
724 				pcc = 0;
725 			else {
726 				if (pcc <= 0)
727 					break;
728 #if	!defined(CRAY2) || !defined(UNICOS5)
729 #ifdef	LINEMODE
730 				/*
731 				 * If ioctl from pty, pass it through net
732 				 */
733 				if (ptyibuf[0] & TIOCPKT_IOCTL) {
734 					copy_termbuf(ptyibuf+1, pcc-1);
735 					localstat();
736 					pcc = 1;
737 				}
738 #endif	LINEMODE
739 				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
740 					netclear();	/* clear buffer back */
741 #ifdef	notdef
742 					/*
743 					 * We really should have this in, but
744 					 * there are client telnets on some
745 					 * operating systems get screwed up
746 					 * royally if we send them urgent
747 					 * mode data.  So, for now, we'll not
748 					 * do this...
749 					 */
750 					*nfrontp++ = IAC;
751 					*nfrontp++ = DM;
752 					neturg = nfrontp-1; /* off by one XXX */
753 #endif
754 				}
755 				if (hisopts[TELOPT_LFLOW] &&
756 				    (ptyibuf[0] &
757 				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
758 					(void) sprintf(nfrontp, "%c%c%c%c%c%c",
759 					    IAC, SB, TELOPT_LFLOW,
760 					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
761 					    IAC, SE);
762 					nfrontp += 6;
763 				}
764 				pcc--;
765 				ptyip = ptyibuf+1;
766 #else	/* defined(CRAY2) && defined(UNICOS5) */
767 				if (!uselinemode) {
768 					unpcc = pcc;
769 					unptyip = ptyibuf;
770 					pcc = term_output(&unptyip, ptyibuf2,
771 								&unpcc, BUFSIZ);
772 					ptyip = ptyibuf2;
773 				} else
774 					ptyip = ptyibuf;
775 #endif	/* defined(CRAY2) && defined(UNICOS5) */
776 			}
777 		}
778 
779 		while (pcc > 0) {
780 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
781 				break;
782 			c = *ptyip++ & 0377, pcc--;
783 			if (c == IAC)
784 				*nfrontp++ = c;
785 #if	defined(CRAY2) && defined(UNICOS5)
786 			else if (c == '\n' &&
787 				     myopts[TELOPT_BINARY] == OPT_NO && newmap)
788 				*nfrontp++ = '\r';
789 #endif	/* defined(CRAY2) && defined(UNICOS5) */
790 			*nfrontp++ = c;
791 			if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
792 				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
793 					*nfrontp++ = *ptyip++ & 0377;
794 					pcc--;
795 				} else
796 					*nfrontp++ = '\0';
797 			}
798 		}
799 #if	defined(CRAY2) && defined(UNICOS5)
800 		/*
801 		 * If chars were left over from the terminal driver,
802 		 * note their existence.
803 		 */
804 		 if (!uselinemode && unpcc) {
805 			pcc = unpcc;
806 			unpcc = 0;
807 			ptyip = unptyip;
808 		}
809 #endif	/* defined(CRAY2) && defined(UNICOS5) */
810 
811 		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
812 			netflush();
813 		if (ncc > 0)
814 			telrcv();
815 		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
816 			ptyflush();
817 	}
818 	cleanup();
819 }  /* end of telnet */
820 
821 #ifndef	TCSIG
822 # ifdef	TIOCSIG
823 #  define TCSIG TIOCSIG
824 # endif
825 #endif
826 
827 /*
828  * Send interrupt to process on other side of pty.
829  * If it is in raw mode, just write NULL;
830  * otherwise, write intr char.
831  */
832 interrupt()
833 {
834 	ptyflush();	/* half-hearted */
835 
836 #ifdef	TCSIG
837 	(void) ioctl(pty, TCSIG, (char *)SIGINT);
838 #else	/* TCSIG */
839 	init_termbuf();
840 	*pfrontp++ = slctab[SLC_IP].sptr ?
841 			(unsigned char)*slctab[SLC_IP].sptr : '\177';
842 #endif	/* TCSIG */
843 }
844 
845 /*
846  * Send quit to process on other side of pty.
847  * If it is in raw mode, just write NULL;
848  * otherwise, write quit char.
849  */
850 sendbrk()
851 {
852 	ptyflush();	/* half-hearted */
853 #ifdef	TCSIG
854 	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
855 #else	/* TCSIG */
856 	init_termbuf();
857 	*pfrontp++ = slctab[SLC_ABORT].sptr ?
858 			(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
859 #endif	/* TCSIG */
860 }
861 
862 sendsusp()
863 {
864 #ifdef	SIGTSTP
865 	ptyflush();	/* half-hearted */
866 # ifdef	TCSIG
867 	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
868 # else	/* TCSIG */
869 	*pfrontp++ = slctab[SLC_SUSP].sptr ?
870 			(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
871 # endif	/* TCSIG */
872 #endif	/* SIGTSTP */
873 }
874 
875 doeof()
876 {
877 #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
878 	extern char oldeofc;
879 #endif
880 	init_termbuf();
881 
882 #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
883 	if (!tty_isediting()) {
884 		*pfrontp++ = oldeofc;
885 		return;
886 	}
887 #endif
888 	*pfrontp++ = slctab[SLC_EOF].sptr ?
889 			(unsigned char)*slctab[SLC_EOF].sptr : '\004';
890 }
891