1 /*
2  * Copyright (c) 1988, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "telnet_locl.h"
35 
36 RCSID("$Id$");
37 
38 /*
39  * The following routines try to encapsulate what is system dependent
40  * (at least between 4.x and dos) which is used in telnet.c.
41  */
42 
43 int
44 	tout,			/* Output file descriptor */
45 	tin,			/* Input file descriptor */
46 	net;
47 
48 struct	termios old_tc = { 0 };
49 extern struct termios new_tc;
50 
51 # ifndef	TCSANOW
52 #  ifdef TCSETS
53 #   define	TCSANOW		TCSETS
54 #   define	TCSADRAIN	TCSETSW
55 #   define	tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
56 #  else
57 #   ifdef TCSETA
58 #    define	TCSANOW		TCSETA
59 #    define	TCSADRAIN	TCSETAW
60 #    define	tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
61 #   else
62 #    define	TCSANOW		TIOCSETA
63 #    define	TCSADRAIN	TIOCSETAW
64 #    define	tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
65 #   endif
66 #  endif
67 #  define	tcsetattr(f, a, t) ioctl(f, a, (char *)t)
68 #  define	cfgetospeed(ptr)	((ptr)->c_cflag&CBAUD)
69 #  ifdef CIBAUD
70 #   define	cfgetispeed(ptr)	(((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
71 #  else
72 #   define	cfgetispeed(ptr)	cfgetospeed(ptr)
73 #  endif
74 # endif /* TCSANOW */
75 
76 static fd_set ibits, obits, xbits;
77 
78 
79 void
80 init_sys(void)
81 {
82     tout = fileno(stdout);
83     tin = fileno(stdin);
84     FD_ZERO(&ibits);
85     FD_ZERO(&obits);
86     FD_ZERO(&xbits);
87 
88     errno = 0;
89 }
90 
91 
92 int
93 TerminalWrite(char *buf, int n)
94 {
95     return write(tout, buf, n);
96 }
97 
98 int
99 TerminalRead(unsigned char *buf, int n)
100 {
101     return read(tin, buf, n);
102 }
103 
104 /*
105  *
106  */
107 
108 int
109 TerminalAutoFlush(void)
110 {
111 #if	defined(LNOFLSH)
112     int flush;
113 
114     ioctl(0, TIOCLGET, (char *)&flush);
115     return !(flush&LNOFLSH);	/* if LNOFLSH, no autoflush */
116 #else	/* LNOFLSH */
117     return 1;
118 #endif	/* LNOFLSH */
119 }
120 
121 /*
122  * TerminalSpecialChars()
123  *
124  * Look at an input character to see if it is a special character
125  * and decide what to do.
126  *
127  * Output:
128  *
129  *	0	Don't add this character.
130  *	1	Do add this character
131  */
132 
133 int
134 TerminalSpecialChars(int c)
135 {
136     if (c == termIntChar) {
137 	intp();
138 	return 0;
139     } else if (c == termQuitChar) {
140 #ifdef	KLUDGELINEMODE
141 	if (kludgelinemode)
142 	    sendbrk();
143 	else
144 #endif
145 	    sendabort();
146 	return 0;
147     } else if (c == termEofChar) {
148 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
149 	    sendeof();
150 	    return 0;
151 	}
152 	return 1;
153     } else if (c == termSuspChar) {
154 	sendsusp();
155 	return(0);
156     } else if (c == termFlushChar) {
157 	xmitAO();		/* Transmit Abort Output */
158 	return 0;
159     } else if (!MODE_LOCAL_CHARS(globalmode)) {
160 	if (c == termKillChar) {
161 	    xmitEL();
162 	    return 0;
163 	} else if (c == termEraseChar) {
164 	    xmitEC();		/* Transmit Erase Character */
165 	    return 0;
166 	}
167     }
168     return 1;
169 }
170 
171 
172 /*
173  * Flush output to the terminal
174  */
175 
176 void
177 TerminalFlushOutput(void)
178 {
179 #ifdef	TIOCFLUSH
180     ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
181 #else
182     ioctl(fileno(stdout), TCFLSH, (char *) 0);
183 #endif
184 }
185 
186 void
187 TerminalSaveState(void)
188 {
189     tcgetattr(0, &old_tc);
190 
191     new_tc = old_tc;
192 
193 #ifndef	VDISCARD
194     termFlushChar = CONTROL('O');
195 #endif
196 #ifndef	VWERASE
197     termWerasChar = CONTROL('W');
198 #endif
199 #ifndef	VREPRINT
200     termRprntChar = CONTROL('R');
201 #endif
202 #ifndef	VLNEXT
203     termLiteralNextChar = CONTROL('V');
204 #endif
205 #ifndef	VSTART
206     termStartChar = CONTROL('Q');
207 #endif
208 #ifndef	VSTOP
209     termStopChar = CONTROL('S');
210 #endif
211 #ifndef	VSTATUS
212     termAytChar = CONTROL('T');
213 #endif
214 }
215 
216 cc_t*
217 tcval(int func)
218 {
219     switch(func) {
220     case SLC_IP:	return(&termIntChar);
221     case SLC_ABORT:	return(&termQuitChar);
222     case SLC_EOF:	return(&termEofChar);
223     case SLC_EC:	return(&termEraseChar);
224     case SLC_EL:	return(&termKillChar);
225     case SLC_XON:	return(&termStartChar);
226     case SLC_XOFF:	return(&termStopChar);
227     case SLC_FORW1:	return(&termForw1Char);
228     case SLC_FORW2:	return(&termForw2Char);
229 # ifdef	VDISCARD
230     case SLC_AO:	return(&termFlushChar);
231 # endif
232 # ifdef	VSUSP
233     case SLC_SUSP:	return(&termSuspChar);
234 # endif
235 # ifdef	VWERASE
236     case SLC_EW:	return(&termWerasChar);
237 # endif
238 # ifdef	VREPRINT
239     case SLC_RP:	return(&termRprntChar);
240 # endif
241 # ifdef	VLNEXT
242     case SLC_LNEXT:	return(&termLiteralNextChar);
243 # endif
244 # ifdef	VSTATUS
245     case SLC_AYT:	return(&termAytChar);
246 # endif
247 
248     case SLC_SYNCH:
249     case SLC_BRK:
250     case SLC_EOR:
251     default:
252 	return((cc_t *)0);
253     }
254 }
255 
256 void
257 TerminalDefaultChars(void)
258 {
259     memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
260 # ifndef	VDISCARD
261     termFlushChar = CONTROL('O');
262 # endif
263 # ifndef	VWERASE
264     termWerasChar = CONTROL('W');
265 # endif
266 # ifndef	VREPRINT
267     termRprntChar = CONTROL('R');
268 # endif
269 # ifndef	VLNEXT
270     termLiteralNextChar = CONTROL('V');
271 # endif
272 # ifndef	VSTART
273     termStartChar = CONTROL('Q');
274 # endif
275 # ifndef	VSTOP
276     termStopChar = CONTROL('S');
277 # endif
278 # ifndef	VSTATUS
279     termAytChar = CONTROL('T');
280 # endif
281 }
282 
283 #ifdef notdef
284 void
285 TerminalRestoreState()
286 {
287 }
288 #endif
289 
290 /*
291  * TerminalNewMode - set up terminal to a specific mode.
292  *	MODE_ECHO: do local terminal echo
293  *	MODE_FLOW: do local flow control
294  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
295  *	MODE_EDIT: do local line editing
296  *
297  *	Command mode:
298  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
299  *		local echo
300  *		local editing
301  *		local xon/xoff
302  *		local signal mapping
303  *
304  *	Linemode:
305  *		local/no editing
306  *	Both Linemode and Single Character mode:
307  *		local/remote echo
308  *		local/no xon/xoff
309  *		local/no signal mapping
310  */
311 
312 
313 #ifdef	SIGTSTP
314 static RETSIGTYPE susp(int);
315 #endif	/* SIGTSTP */
316 #ifdef	SIGINFO
317 static RETSIGTYPE ayt(int);
318 #endif
319 
320 void
321 TerminalNewMode(int f)
322 {
323     static int prevmode = 0;
324     struct termios tmp_tc;
325     int onoff;
326     int old;
327     cc_t esc;
328 
329     globalmode = f&~MODE_FORCE;
330     if (prevmode == f)
331 	return;
332 
333     /*
334      * Write any outstanding data before switching modes
335      * ttyflush() returns 0 only when there is no more data
336      * left to write out, it returns -1 if it couldn't do
337      * anything at all, otherwise it returns 1 + the number
338      * of characters left to write.
339      */
340     old = ttyflush(SYNCHing|flushout);
341     if (old < 0 || old > 1) {
342 	tcgetattr(tin, &tmp_tc);
343 	do {
344 	    /*
345 	     * Wait for data to drain, then flush again.
346 	     */
347 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
348 	    old = ttyflush(SYNCHing|flushout);
349 	} while (old < 0 || old > 1);
350     }
351 
352     old = prevmode;
353     prevmode = f&~MODE_FORCE;
354     tmp_tc = new_tc;
355 
356     if (f&MODE_ECHO) {
357 	tmp_tc.c_lflag |= ECHO;
358 	tmp_tc.c_oflag |= ONLCR;
359 	if (crlf)
360 		tmp_tc.c_iflag |= ICRNL;
361     } else {
362 	tmp_tc.c_lflag &= ~ECHO;
363 	tmp_tc.c_oflag &= ~ONLCR;
364 # ifdef notdef
365 	if (crlf)
366 		tmp_tc.c_iflag &= ~ICRNL;
367 # endif
368     }
369 
370     if ((f&MODE_FLOW) == 0) {
371 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
372     } else {
373 	if (restartany < 0) {
374 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
375 	} else if (restartany > 0) {
376 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
377 	} else {
378 		tmp_tc.c_iflag |= IXOFF|IXON;
379 		tmp_tc.c_iflag &= ~IXANY;
380 	}
381     }
382 
383     if ((f&MODE_TRAPSIG) == 0) {
384 	tmp_tc.c_lflag &= ~ISIG;
385 	localchars = 0;
386     } else {
387 	tmp_tc.c_lflag |= ISIG;
388 	localchars = 1;
389     }
390 
391     if (f&MODE_EDIT) {
392 	tmp_tc.c_lflag |= ICANON;
393     } else {
394 	tmp_tc.c_lflag &= ~ICANON;
395 	tmp_tc.c_iflag &= ~ICRNL;
396 	tmp_tc.c_cc[VMIN] = 1;
397 	tmp_tc.c_cc[VTIME] = 0;
398     }
399 
400     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
401 # ifdef VLNEXT
402 	tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
403 # endif
404     }
405 
406     if (f&MODE_SOFT_TAB) {
407 # ifdef	OXTABS
408 	tmp_tc.c_oflag |= OXTABS;
409 # endif
410 # ifdef	TABDLY
411 	tmp_tc.c_oflag &= ~TABDLY;
412 	tmp_tc.c_oflag |= TAB3;
413 # endif
414     } else {
415 # ifdef	OXTABS
416 	tmp_tc.c_oflag &= ~OXTABS;
417 # endif
418 # ifdef	TABDLY
419 	tmp_tc.c_oflag &= ~TABDLY;
420 # endif
421     }
422 
423     if (f&MODE_LIT_ECHO) {
424 # ifdef	ECHOCTL
425 	tmp_tc.c_lflag &= ~ECHOCTL;
426 # endif
427     } else {
428 # ifdef	ECHOCTL
429 	tmp_tc.c_lflag |= ECHOCTL;
430 # endif
431     }
432 
433     if (f == -1) {
434 	onoff = 0;
435     } else {
436 	if (f & MODE_INBIN)
437 		tmp_tc.c_iflag &= ~ISTRIP;
438 	else
439 		tmp_tc.c_iflag |= ISTRIP;
440 	if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
441 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
442 		tmp_tc.c_cflag |= CS8;
443 		if(f & MODE_OUTBIN)
444 		    tmp_tc.c_oflag &= ~OPOST;
445 		else
446 		    tmp_tc.c_oflag |= OPOST;
447 	} else {
448 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
449 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
450 		tmp_tc.c_oflag |= OPOST;
451 	}
452 	onoff = 1;
453     }
454 
455     if (f != -1) {
456 
457 #ifdef	SIGTSTP
458 	signal(SIGTSTP, susp);
459 #endif	/* SIGTSTP */
460 #ifdef	SIGINFO
461 	signal(SIGINFO, ayt);
462 #endif
463 #ifdef NOKERNINFO
464 	tmp_tc.c_lflag |= NOKERNINFO;
465 #endif
466 	/*
467 	 * We don't want to process ^Y here.  It's just another
468 	 * character that we'll pass on to the back end.  It has
469 	 * to process it because it will be processed when the
470 	 * user attempts to read it, not when we send it.
471 	 */
472 # ifdef	VDSUSP
473 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
474 # endif
475 	/*
476 	 * If the VEOL character is already set, then use VEOL2,
477 	 * otherwise use VEOL.
478 	 */
479 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
480 	if ((tmp_tc.c_cc[VEOL] != esc)
481 # ifdef	VEOL2
482 	    && (tmp_tc.c_cc[VEOL2] != esc)
483 # endif
484 	    ) {
485 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
486 		    tmp_tc.c_cc[VEOL] = esc;
487 # ifdef	VEOL2
488 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
489 		    tmp_tc.c_cc[VEOL2] = esc;
490 # endif
491 	}
492     } else {
493         sigset_t sm;
494 
495 #ifdef	SIGINFO
496 	signal(SIGINFO, ayt_status);
497 #endif
498 #ifdef	SIGTSTP
499 	signal(SIGTSTP, SIG_DFL);
500 	sigemptyset(&sm);
501 	sigaddset(&sm, SIGTSTP);
502 	sigprocmask(SIG_UNBLOCK, &sm, NULL);
503 #endif	/* SIGTSTP */
504 	tmp_tc = old_tc;
505     }
506     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
507 	tcsetattr(tin, TCSANOW, &tmp_tc);
508 
509     ioctl(tin, FIONBIO, (char *)&onoff);
510     ioctl(tout, FIONBIO, (char *)&onoff);
511 
512 }
513 
514 /*
515  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
516  */
517 #if B4800 != 4800
518 #define	DECODE_BAUD
519 #endif
520 
521 #ifdef	DECODE_BAUD
522 #ifndef	B7200
523 #define B7200   B4800
524 #endif
525 
526 #ifndef	B14400
527 #define B14400  B9600
528 #endif
529 
530 #ifndef	B19200
531 # define B19200 B14400
532 #endif
533 
534 #ifndef	B28800
535 #define B28800  B19200
536 #endif
537 
538 #ifndef	B38400
539 # define B38400 B28800
540 #endif
541 
542 #ifndef B57600
543 #define B57600  B38400
544 #endif
545 
546 #ifndef B76800
547 #define B76800  B57600
548 #endif
549 
550 #ifndef B115200
551 #define B115200 B76800
552 #endif
553 
554 #ifndef B230400
555 #define B230400 B115200
556 #endif
557 
558 
559 /*
560  * This code assumes that the values B0, B50, B75...
561  * are in ascending order.  They do not have to be
562  * contiguous.
563  */
564 struct termspeeds {
565 	long speed;
566 	long value;
567 } termspeeds[] = {
568 	{ 0,      B0 },      { 50,    B50 },    { 75,     B75 },
569 	{ 110,    B110 },    { 134,   B134 },   { 150,    B150 },
570 	{ 200,    B200 },    { 300,   B300 },   { 600,    B600 },
571 	{ 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
572 	{ 4800,   B4800 },   { 7200,  B7200 },  { 9600,   B9600 },
573 	{ 14400,  B14400 },  { 19200, B19200 }, { 28800,  B28800 },
574 	{ 38400,  B38400 },  { 57600, B57600 }, { 115200, B115200 },
575 	{ 230400, B230400 }, { -1,    B230400 }
576 };
577 #endif	/* DECODE_BAUD */
578 
579 void
580 TerminalSpeeds(long *input_speed, long *output_speed)
581 {
582 #ifdef	DECODE_BAUD
583     struct termspeeds *tp;
584 #endif	/* DECODE_BAUD */
585     long in, out;
586 
587     out = cfgetospeed(&old_tc);
588     in = cfgetispeed(&old_tc);
589     if (in == 0)
590 	in = out;
591 
592 #ifdef	DECODE_BAUD
593     tp = termspeeds;
594     while ((tp->speed != -1) && (tp->value < in))
595 	tp++;
596     *input_speed = tp->speed;
597 
598     tp = termspeeds;
599     while ((tp->speed != -1) && (tp->value < out))
600 	tp++;
601     *output_speed = tp->speed;
602 #else	/* DECODE_BAUD */
603 	*input_speed = in;
604 	*output_speed = out;
605 #endif	/* DECODE_BAUD */
606 }
607 
608 int
609 TerminalWindowSize(long *rows, long *cols)
610 {
611     int irows, icols;
612 
613     if (get_window_size(STDIN_FILENO, &irows, &icols) == 0) {
614 	*rows = irows;
615 	*cols = icols;
616 	return 1;
617     } else
618 	return 0;
619 }
620 
621 int
622 NetClose(int fd)
623 {
624     return close(fd);
625 }
626 
627 
628 void
629 NetNonblockingIO(int fd, int onoff)
630 {
631     ioctl(fd, FIONBIO, (char *)&onoff);
632 }
633 
634 
635 /*
636  * Various signal handling routines.
637  */
638 
639 static RETSIGTYPE deadpeer(int),
640   intr(int), intr2(int), susp(int), sendwin(int);
641 #ifdef SIGINFO
642 static RETSIGTYPE ayt(int);
643 #endif
644 
645 
646     /* ARGSUSED */
647 static RETSIGTYPE
648 deadpeer(int sig)
649 {
650 	setcommandmode();
651 	longjmp(peerdied, -1);
652 }
653 
654 int intr_happened = 0;
655 int intr_waiting = 0;
656 
657     /* ARGSUSED */
658 static RETSIGTYPE
659 intr(int sig)
660 {
661     if (intr_waiting) {
662 	intr_happened = 1;
663 	return;
664     }
665     if (localchars) {
666 	intp();
667 	return;
668     }
669     setcommandmode();
670     longjmp(toplevel, -1);
671 }
672 
673     /* ARGSUSED */
674 static RETSIGTYPE
675 intr2(int sig)
676 {
677     if (localchars) {
678 #ifdef	KLUDGELINEMODE
679 	if (kludgelinemode)
680 	    sendbrk();
681 	else
682 #endif
683 	    sendabort();
684 	return;
685     }
686 }
687 
688 #ifdef	SIGTSTP
689     /* ARGSUSED */
690 static RETSIGTYPE
691 susp(int sig)
692 {
693     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
694 	return;
695     if (localchars)
696 	sendsusp();
697 }
698 #endif
699 
700 #ifdef	SIGWINCH
701     /* ARGSUSED */
702 static RETSIGTYPE
703 sendwin(int sig)
704 {
705     if (connected) {
706 	sendnaws();
707     }
708 }
709 #endif
710 
711 #ifdef	SIGINFO
712     /* ARGSUSED */
713 static RETSIGTYPE
714 ayt(int sig)
715 {
716     if (connected)
717 	sendayt();
718     else
719 	ayt_status(sig);
720 }
721 #endif
722 
723 
724 void
725 sys_telnet_init(void)
726 {
727     signal(SIGINT, intr);
728     signal(SIGQUIT, intr2);
729     signal(SIGPIPE, deadpeer);
730 #ifdef	SIGWINCH
731     signal(SIGWINCH, sendwin);
732 #endif
733 #ifdef	SIGTSTP
734     signal(SIGTSTP, susp);
735 #endif
736 #ifdef	SIGINFO
737     signal(SIGINFO, ayt);
738 #endif
739 
740     setconnmode(0);
741 
742     NetNonblockingIO(net, 1);
743 
744 
745 #if	defined(SO_OOBINLINE)
746     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1)
747 	perror("setsockopt (SO_OOBINLINE) (ignored)");
748 #endif	/* defined(SO_OOBINLINE) */
749 }
750 
751 /*
752  * Process rings -
753  *
754  *	This routine tries to fill up/empty our various rings.
755  *
756  *	The parameter specifies whether this is a poll operation,
757  *	or a block-until-something-happens operation.
758  *
759  *	The return value is 1 if something happened, 0 if not.
760  */
761 
762 int
763 process_rings(int netin,
764 	      int netout,
765 	      int netex,
766 	      int ttyin,
767 	      int ttyout,
768 	      int poll) /* If 0, then block until something to do */
769 {
770     int c;
771 		/* One wants to be a bit careful about setting returnValue
772 		 * to one, since a one implies we did some useful work,
773 		 * and therefore probably won't be called to block next
774 		 * time (TN3270 mode only).
775 		 */
776     int returnValue = 0;
777     static struct timeval TimeValue = { 0 };
778 
779     if (net >= FD_SETSIZE
780 	|| tout >= FD_SETSIZE
781 	|| tin >= FD_SETSIZE)
782 	errx (1, "fd too large");
783 
784     if (netout) {
785 	FD_SET(net, &obits);
786     }
787     if (ttyout) {
788 	FD_SET(tout, &obits);
789     }
790     if (ttyin) {
791 	FD_SET(tin, &ibits);
792     }
793     if (netin) {
794 	FD_SET(net, &ibits);
795     }
796 #if !defined(SO_OOBINLINE)
797     if (netex) {
798 	FD_SET(net, &xbits);
799     }
800 #endif
801     if ((c = select(FD_SETSIZE, &ibits, &obits, &xbits,
802 			(poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
803 	if (c == -1) {
804 		    /*
805 		     * we can get EINTR if we are in line mode,
806 		     * and the user does an escape (TSTP), or
807 		     * some other signal generator.
808 		     */
809 	    if (errno == EINTR) {
810 		return 0;
811 	    }
812 		    /* I don't like this, does it ever happen? */
813 	    printf("sleep(5) from telnet, after select\r\n");
814 	    sleep(5);
815 	}
816 	return 0;
817     }
818 
819     /*
820      * Any urgent data?
821      */
822     if (FD_ISSET(net, &xbits)) {
823 	FD_CLR(net, &xbits);
824 	SYNCHing = 1;
825 	ttyflush(1);	/* flush already enqueued data */
826     }
827 
828     /*
829      * Something to read from the network...
830      */
831     if (FD_ISSET(net, &ibits)) {
832 	int canread;
833 
834 	FD_CLR(net, &ibits);
835 	canread = ring_empty_consecutive(&netiring);
836 #if	!defined(SO_OOBINLINE)
837 	    /*
838 	     * In 4.2 (and some early 4.3) systems, the
839 	     * OOB indication and data handling in the kernel
840 	     * is such that if two separate TCP Urgent requests
841 	     * come in, one byte of TCP data will be overlaid.
842 	     * This is fatal for Telnet, but we try to live
843 	     * with it.
844 	     *
845 	     * In addition, in 4.2 (and...), a special protocol
846 	     * is needed to pick up the TCP Urgent data in
847 	     * the correct sequence.
848 	     *
849 	     * What we do is:  if we think we are in urgent
850 	     * mode, we look to see if we are "at the mark".
851 	     * If we are, we do an OOB receive.  If we run
852 	     * this twice, we will do the OOB receive twice,
853 	     * but the second will fail, since the second
854 	     * time we were "at the mark", but there wasn't
855 	     * any data there (the kernel doesn't reset
856 	     * "at the mark" until we do a normal read).
857 	     * Once we've read the OOB data, we go ahead
858 	     * and do normal reads.
859 	     *
860 	     * There is also another problem, which is that
861 	     * since the OOB byte we read doesn't put us
862 	     * out of OOB state, and since that byte is most
863 	     * likely the TELNET DM (data mark), we would
864 	     * stay in the TELNET SYNCH (SYNCHing) state.
865 	     * So, clocks to the rescue.  If we've "just"
866 	     * received a DM, then we test for the
867 	     * presence of OOB data when the receive OOB
868 	     * fails (and AFTER we did the normal mode read
869 	     * to clear "at the mark").
870 	     */
871 	if (SYNCHing) {
872 	    int atmark;
873 	    static int bogus_oob = 0, first = 1;
874 
875 	    ioctl(net, SIOCATMARK, (char *)&atmark);
876 	    if (atmark) {
877 		c = recv(net, netiring.supply, canread, MSG_OOB);
878 		if ((c == -1) && (errno == EINVAL)) {
879 		    c = recv(net, netiring.supply, canread, 0);
880 		    if (clocks.didnetreceive < clocks.gotDM) {
881 			SYNCHing = stilloob();
882 		    }
883 		} else if (first && c > 0) {
884 		    /*
885 		     * Bogosity check.  Systems based on 4.2BSD
886 		     * do not return an error if you do a second
887 		     * recv(MSG_OOB).  So, we do one.  If it
888 		     * succeeds and returns exactly the same
889 		     * data, then assume that we are running
890 		     * on a broken system and set the bogus_oob
891 		     * flag.  (If the data was different, then
892 		     * we probably got some valid new data, so
893 		     * increment the count...)
894 		     */
895 		    int i;
896 		    i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
897 		    if (i == c &&
898 			 memcmp(netiring.supply, netiring.supply + c, i) == 0) {
899 			bogus_oob = 1;
900 			first = 0;
901 		    } else if (i < 0) {
902 			bogus_oob = 0;
903 			first = 0;
904 		    } else
905 			c += i;
906 		}
907 		if (bogus_oob && c > 0) {
908 		    int i;
909 		    /*
910 		     * Bogosity.  We have to do the read
911 		     * to clear the atmark to get out of
912 		     * an infinate loop.
913 		     */
914 		    i = read(net, netiring.supply + c, canread - c);
915 		    if (i > 0)
916 			c += i;
917 		}
918 	    } else {
919 		c = recv(net, netiring.supply, canread, 0);
920 	    }
921 	} else {
922 	    c = recv(net, netiring.supply, canread, 0);
923 	}
924 	settimer(didnetreceive);
925 #else	/* !defined(SO_OOBINLINE) */
926 	c = recv(net, (char *)netiring.supply, canread, 0);
927 #endif	/* !defined(SO_OOBINLINE) */
928 	if (c < 0 && errno == EWOULDBLOCK) {
929 	    c = 0;
930 	} else if (c <= 0) {
931 	    return -1;
932 	}
933 	if (netdata) {
934 	    Dump('<', netiring.supply, c);
935 	}
936 	if (c)
937 	    ring_supplied(&netiring, c);
938 	returnValue = 1;
939     }
940 
941     /*
942      * Something to read from the tty...
943      */
944     if (FD_ISSET(tin, &ibits)) {
945 	FD_CLR(tin, &ibits);
946 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
947 	if (c < 0 && errno == EIO)
948 	    c = 0;
949 	if (c < 0 && errno == EWOULDBLOCK) {
950 	    c = 0;
951 	} else {
952 	    /* EOF detection for line mode!!!! */
953 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
954 			/* must be an EOF... */
955 		*ttyiring.supply = termEofChar;
956 		c = 1;
957 	    }
958 	    if (c <= 0) {
959 		return -1;
960 	    }
961 	    if (termdata) {
962 		Dump('<', ttyiring.supply, c);
963 	    }
964 	    ring_supplied(&ttyiring, c);
965 	}
966 	returnValue = 1;		/* did something useful */
967     }
968 
969     if (FD_ISSET(net, &obits)) {
970 	FD_CLR(net, &obits);
971 	returnValue |= netflush();
972     }
973     if (FD_ISSET(tout, &obits)) {
974 	FD_CLR(tout, &obits);
975 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
976     }
977 
978     return returnValue;
979 }
980