xref: /original-bsd/usr.bin/telnet/sys_bsd.c (revision 2bd07fe6)
1 /*
2  * Copyright (c) 1988 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 static char sccsid[] = "@(#)sys_bsd.c	1.24 (Berkeley) 03/01/90";
20 #endif /* not lint */
21 
22 /*
23  * The following routines try to encapsulate what is system dependent
24  * (at least between 4.x and dos) which is used in telnet.c.
25  */
26 
27 
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <sys/socket.h>
32 #include <signal.h>
33 #include <errno.h>
34 #include <arpa/telnet.h>
35 
36 #include "ring.h"
37 
38 #include "fdset.h"
39 
40 #include "defines.h"
41 #include "externs.h"
42 #include "types.h"
43 
44 #if	defined(CRAY)
45 #define	SIG_FUNC_RET	void
46 #else
47 #define	SIG_FUNC_RET	int
48 #endif
49 
50 int
51 	tout,			/* Output file descriptor */
52 	tin,			/* Input file descriptor */
53 	net;
54 
55 #ifndef	USE_TERMIO
56 struct	tchars otc = { 0 }, ntc = { 0 };
57 struct	ltchars oltc = { 0 }, nltc = { 0 };
58 struct	sgttyb ottyb = { 0 }, nttyb = { 0 };
59 int	olmode = 0;
60 
61 #else	/* USE_TERMIO */
62 struct	termio old_tc = { 0 };
63 extern struct termio new_tc;
64 
65 #ifndef	TCGETA
66 #define	TCGETA	TIOCGETA
67 #define	TCSETA	TIOCSETA
68 #define	TCSETAW	TIOCSETAW
69 #endif	/* TCGETA */
70 #endif	/* USE_TERMIO */
71 
72 static fd_set ibits, obits, xbits;
73 
74 
75 init_sys()
76 {
77     tout = fileno(stdout);
78     tin = fileno(stdin);
79     FD_ZERO(&ibits);
80     FD_ZERO(&obits);
81     FD_ZERO(&xbits);
82 
83     errno = 0;
84 }
85 
86 
87 TerminalWrite(buf, n)
88 char	*buf;
89 int	n;
90 {
91     return write(tout, buf, n);
92 }
93 
94 TerminalRead(buf, n)
95 char	*buf;
96 int	n;
97 {
98     return read(tin, buf, n);
99 }
100 
101 /*
102  *
103  */
104 
105 int
106 TerminalAutoFlush()
107 {
108 #if	defined(LNOFLSH)
109     int flush;
110 
111     ioctl(0, TIOCLGET, (char *)&flush);
112     return !(flush&LNOFLSH);	/* if LNOFLSH, no autoflush */
113 #else	/* LNOFLSH */
114     return 1;
115 #endif	/* LNOFLSH */
116 }
117 
118 #ifdef	KLUDGELINEMODE
119 extern int kludgelinemode;
120 #endif
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(c)
135 int	c;
136 {
137     void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
138 
139     if (c == termIntChar) {
140 	intp();
141 	return 0;
142     } else if (c == termQuitChar) {
143 #ifdef	KLUDGELINEMODE
144 	if (kludgelinemode)
145 	    sendbrk();
146 	else
147 #endif
148 	    sendabort();
149 	return 0;
150     } else if (c == termEofChar) {
151 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
152 	    sendeof();
153 	    return 0;
154 	}
155 	return 1;
156     } else if (c == termSuspChar) {
157 	sendsusp();
158 	return(0);
159     } else if (c == termFlushChar) {
160 	xmitAO();		/* Transmit Abort Output */
161 	return 0;
162     } else if (!MODE_LOCAL_CHARS(globalmode)) {
163 	if (c == termKillChar) {
164 	    xmitEL();
165 	    return 0;
166 	} else if (c == termEraseChar) {
167 	    xmitEC();		/* Transmit Erase Character */
168 	    return 0;
169 	}
170     }
171     return 1;
172 }
173 
174 
175 /*
176  * Flush output to the terminal
177  */
178 
179 void
180 TerminalFlushOutput()
181 {
182 #ifdef	TIOCFLUSH
183     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
184 #else
185     (void) ioctl(fileno(stdout), TCFLSH, (char *) 0);
186 #endif
187 }
188 
189 void
190 TerminalSaveState()
191 {
192 #ifndef	USE_TERMIO
193     ioctl(0, TIOCGETP, (char *)&ottyb);
194     ioctl(0, TIOCGETC, (char *)&otc);
195     ioctl(0, TIOCGLTC, (char *)&oltc);
196     ioctl(0, TIOCLGET, (char *)&olmode);
197 
198     ntc = otc;
199     nltc = oltc;
200     nttyb = ottyb;
201 
202 #else	/* USE_TERMIO */
203     ioctl(0, TCGETA, &old_tc);
204 
205     new_tc = old_tc;
206 
207     termFlushChar = 'O'&0x37;
208     termWerasChar = 'W'&0x37;
209     termRprntChar = 'R'&0x37;
210     termLiteralNextChar = 'V'&0x37;
211     termStartChar = 'Q'&0x37;
212     termStopChar = 'S'&0x37;
213 #endif	/* USE_TERMIO */
214 }
215 
216 cc_t *
217 tcval(func)
218 register int func;
219 {
220     switch(func) {
221     case SLC_IP:	return(&termIntChar);
222     case SLC_ABORT:	return(&termQuitChar);
223     case SLC_EOF:	return(&termEofChar);
224     case SLC_EC:	return(&termEraseChar);
225     case SLC_EL:	return(&termKillChar);
226     case SLC_XON:	return(&termStartChar);
227     case SLC_XOFF:	return(&termStopChar);
228 #ifndef	SYSV_TERMIO
229     case SLC_AO:	return(&termFlushChar);
230     case SLC_SUSP:	return(&termSuspChar);
231     case SLC_EW:	return(&termWerasChar);
232     case SLC_RP:	return(&termRprntChar);
233     case SLC_LNEXT:	return(&termLiteralNextChar);
234 #endif	/* SYSV_TERMIO */
235 
236     case SLC_SYNCH:
237     case SLC_BRK:
238     case SLC_AYT:
239     case SLC_EOR:
240     case SLC_FORW1:
241     case SLC_FORW2:
242     default:
243 	return((cc_t *)0);
244     }
245 }
246 
247 void
248 TerminalDefaultChars()
249 {
250 #ifndef	USE_TERMIO
251     ntc = otc;
252     nltc = oltc;
253     nttyb.sg_kill = ottyb.sg_kill;
254     nttyb.sg_erase = ottyb.sg_erase;
255 #else	/* USE_TERMIO */
256     memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
257 # ifndef	VFLUSHO
258     termFlushChar = 'O'&0x37;
259 # endif
260 # ifndef	VWERASE
261     termWerasChar = 'W'&0x37;
262 # endif
263 # ifndef	VREPRINT
264     termRprntChar = 'R'&0x37;
265 # endif
266 # ifndef	VLNEXT
267     termLiteralNextChar = 'V'&0x37;
268 # endif
269 # ifndef	VSTART
270     termStartChar = 'Q'&0x37;
271 # endif
272 # ifndef	VSTOP
273     termStopChar = 'S'&0x37;
274 # endif
275 #endif	/* USE_TERMIO */
276 }
277 
278 void
279 TerminalRestoreState()
280 {
281 }
282 
283 /*
284  * TerminalNewMode - set up terminal to a specific mode.
285  *	MODE_ECHO: do local terminal echo
286  *	MODE_FLOW: do local flow control
287  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
288  *	MODE_EDIT: do local line editing
289  *
290  *	Command mode:
291  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
292  *		local echo
293  *		local editing
294  *		local xon/xoff
295  *		local signal mapping
296  *
297  *	Linemode:
298  *		local/no editing
299  *	Both Linemode and Single Character mode:
300  *		local/remote echo
301  *		local/no xon/xoff
302  *		local/no signal mapping
303  */
304 
305 
306 void
307 TerminalNewMode(f)
308 register int f;
309 {
310     static int prevmode = 0;
311 #ifndef	USE_TERMIO
312     struct tchars tc;
313     struct ltchars ltc;
314     struct sgttyb sb;
315     int lmode;
316 #else	/* USE_TERMIO */
317     struct termio tmp_tc;
318 #endif	/* USE_TERMIO */
319     int onoff;
320     int old;
321 
322     globalmode = f&~MODE_FORCE;
323     if (prevmode == f)
324 	return;
325 
326     /*
327      * Write any outstanding data before switching modes
328      * ttyflush() returns 0 only when there is no more data
329      * left to write out, it returns -1 if it couldn't do
330      * anything at all, otherwise it returns 1 + the number
331      * of characters left to write.
332      */
333     old = ttyflush(SYNCHing|flushout);
334     if (old < 0 || old > 1) {
335 #ifndef	USE_TERMIO
336 	ioctl(tin, TIOCGETP, (char *)&sb);
337 #else	/* USE_TERMIO */
338 	ioctl(tin, TCGETA, (char *)&tmp_tc);
339 #endif	/* USE_TERMIO */
340 	do {
341 	    /*
342 	     * Wait for data to drain, then flush again.
343 	     */
344 #ifndef	USE_TERMIO
345 	    ioctl(tin, TIOCSETP, (char *)&sb);
346 #else	/* USE_TERMIO */
347 	    ioctl(tin, TCSETAW, (char *)&tmp_tc);
348 #endif	/* USE_TERMIO */
349 	    old = ttyflush(SYNCHing|flushout);
350 	} while (old < 0 || old > 1);
351     }
352 
353     old = prevmode;
354     prevmode = f&~MODE_FORCE;
355 #ifndef	USE_TERMIO
356     sb = nttyb;
357     tc = ntc;
358     ltc = nltc;
359     lmode = olmode;
360 #else
361     tmp_tc = new_tc;
362 #endif
363 
364     if (f&MODE_ECHO) {
365 #ifndef	USE_TERMIO
366 	sb.sg_flags |= ECHO;
367 #else
368 	tmp_tc.c_lflag |= ECHO;
369 	tmp_tc.c_oflag |= ONLCR;
370 # ifdef notdef
371 	tmp_tc.c_iflag |= ICRNL;
372 # endif
373 #endif
374     } else {
375 #ifndef	USE_TERMIO
376 	sb.sg_flags &= ~ECHO;
377 #else
378 	tmp_tc.c_lflag &= ~ECHO;
379 	tmp_tc.c_oflag &= ~ONLCR;
380 # ifdef notdef
381 	tmp_tc.c_iflag &= ~ICRNL;
382 # endif
383 #endif
384     }
385 
386     if ((f&MODE_FLOW) == 0) {
387 #ifndef	USE_TERMIO
388 	tc.t_startc = -1;
389 	tc.t_stopc = -1;
390 #else
391 	tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON);
392     } else {
393 	tmp_tc.c_iflag |= IXANY|IXOFF|IXON;
394 #endif
395     }
396 
397     if ((f&MODE_TRAPSIG) == 0) {
398 #ifndef	USE_TERMIO
399 	tc.t_intrc = -1;
400 	tc.t_quitc = -1;
401 	tc.t_eofc = -1;
402 	ltc.t_suspc = -1;
403 	ltc.t_dsuspc = -1;
404 #else
405 	tmp_tc.c_lflag &= ~ISIG;
406 #endif
407 	localchars = 0;
408     } else {
409 #ifdef	USE_TERMIO
410 	tmp_tc.c_lflag |= ISIG;
411 #endif
412 	localchars = 1;
413     }
414 
415     if (f&MODE_EDIT) {
416 #ifndef	USE_TERMIO
417 	sb.sg_flags &= ~CBREAK;
418 	sb.sg_flags |= CRMOD;
419 #else
420 	tmp_tc.c_lflag |= ICANON;
421 #endif
422     } else {
423 #ifndef	USE_TERMIO
424 	sb.sg_flags |= CBREAK;
425 	if (f&MODE_ECHO)
426 	    sb.sg_flags |= CRMOD;
427 	else
428 	    sb.sg_flags &= ~CRMOD;
429 #else
430 	tmp_tc.c_lflag &= ~ICANON;
431 	tmp_tc.c_iflag &= ~ICRNL;
432 	tmp_tc.c_cc[VMIN] = 1;
433 	tmp_tc.c_cc[VTIME] = 0;
434 #endif
435     }
436 
437     if (f == -1) {
438 	onoff = 0;
439     } else {
440 #ifndef	USE_TERMIO
441 	if (f & MODE_OUTBIN)
442 		lmode |= LLITOUT;
443 	else
444 		lmode &= ~LLITOUT;
445 
446 	if (f & MODE_INBIN)
447 		lmode |= LPASS8;
448 	else
449 		lmode &= ~LPASS8;
450 #else
451 	if (f & MODE_OUTBIN)
452 		tmp_tc.c_lflag &= ~ISTRIP;
453 	else
454 		tmp_tc.c_lflag |= ISTRIP;
455 	if (f & MODE_INBIN) {
456 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
457 		tmp_tc.c_cflag |= CS8;
458 		tmp_tc.c_cflag &= ~OPOST;
459 	} else {
460 		tmp_tc.c_cflag &= ~CSIZE;
461 		tmp_tc.c_cflag |= CS7|PARENB;
462 		tmp_tc.c_cflag |= OPOST;
463 	}
464 #endif
465 	onoff = 1;
466     }
467 
468     if (f != -1) {
469 #ifdef	SIGTSTP
470 	void susp();
471 
472 	(void) signal(SIGTSTP, (SIG_FUNC_RET (*)())susp);
473 #endif	/* SIGTSTP */
474 #ifdef	USE_TERMIO
475 #ifdef	VEOL2
476 	/*
477 	 * If the VEOL character is already set, then use VEOL2,
478 	 * otherwise use VEOL.
479 	 */
480 	if (tmp_tc.c_cc[VEOL] != (cc_t)(-1))
481 		tmp_tc.c_cc[VEOL2] = escape;
482 	else
483 #endif
484 		tmp_tc.c_cc[VEOL] = escape;
485 #else
486 	tc.t_brkc = escape;
487 #endif
488     } else {
489 #ifdef	SIGTSTP
490 	(void) signal(SIGTSTP, SIG_DFL);
491 	sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
492 #endif	/* SIGTSTP */
493 #ifndef USE_TERMIO
494 	ltc = oltc;
495 	tc = otc;
496 	sb = ottyb;
497 #endif
498     }
499 #ifndef USE_TERMIO
500     ioctl(tin, TIOCLSET, (char *)&lmode);
501     ioctl(tin, TIOCSLTC, (char *)&ltc);
502     ioctl(tin, TIOCSETC, (char *)&tc);
503     ioctl(tin, TIOCSETP, (char *)&sb);
504 #else
505     if (ioctl(tin, TCSETAW, &tmp_tc) < 0)
506 	ioctl(tin, TCSETA, &tmp_tc);
507 #endif
508 
509 #if	(!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
510     ioctl(tin, FIONBIO, (char *)&onoff);
511     ioctl(tout, FIONBIO, (char *)&onoff);
512 #endif	/* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
513 #if	defined(TN3270)
514     if (noasynchtty == 0) {
515 	ioctl(tin, FIOASYNC, (char *)&onoff);
516     }
517 #endif	/* defined(TN3270) */
518 }
519 
520 #ifndef	B19200
521 # define B19200 B9600
522 #endif
523 
524 #ifndef	B38400
525 # define B38400 B19200
526 #endif
527 
528 /*
529  * This code assumes that the values B0, B50, B75...
530  * are in ascending order.  They do not have to be
531  * contiguous.
532  */
533 struct termspeeds {
534 	long speed;
535 	long value;
536 } termspeeds[] = {
537 	{ 0,     B0 },     { 50,    B50 },   { 75,    B75 },
538 	{ 110,   B110 },   { 134,   B134 },  { 150,   B150 },
539 	{ 200,   B200 },   { 300,   B300 },  { 600,   B600 },
540 	{ 1200,  B1200 },  { 1800,  B1800 }, { 2400,  B2400 },
541 	{ 4800,  B4800 },  { 9600,  B9600 }, { 19200, B19200 },
542 	{ 38400, B38400 }, { -1,    B38400 }
543 };
544 
545 #ifndef	USE_TERMIO
546 # define	ISPEED	ottyb.sg_ispeed
547 # define	OSPEED	ottyb.sg_ospeed
548 #else
549 # ifdef	SYSV_TERMIO
550 #  define	ISPEED	(old_tc.c_cflag&CBAUD)
551 #  define	OSPEED	ISPEED
552 # else
553 #  define	ISPEED	old_tc.c_ispeed
554 #  define OSPEED	old_tc.c_ospeed
555 # endif
556 #endif
557 
558 void
559 TerminalSpeeds(ispeed, ospeed)
560 long *ispeed;
561 long *ospeed;
562 {
563     register struct termspeeds *tp;
564 
565     tp = termspeeds;
566     while ((tp->speed != -1) && (tp->value < ISPEED))
567 	tp++;
568     *ispeed = tp->speed;
569 
570     tp = termspeeds;
571     while ((tp->speed != -1) && (tp->value < OSPEED))
572 	tp++;
573     *ospeed = tp->speed;
574 }
575 
576 int
577 TerminalWindowSize(rows, cols)
578 long *rows, *cols;
579 {
580 #ifdef	TIOCGWINSZ
581     struct winsize ws;
582 
583     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
584 	*rows = ws.ws_row;
585 	*cols = ws.ws_col;
586 	return 1;
587     }
588 #endif	/* TIOCGWINSZ */
589     return 0;
590 }
591 
592 int
593 NetClose(fd)
594 int	fd;
595 {
596     return close(fd);
597 }
598 
599 
600 void
601 NetNonblockingIO(fd, onoff)
602 int
603 	fd,
604 	onoff;
605 {
606     ioctl(fd, FIONBIO, (char *)&onoff);
607 }
608 
609 #if	defined(TN3270)
610 void
611 NetSigIO(fd, onoff)
612 int
613 	fd,
614 	onoff;
615 {
616     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
617 }
618 
619 void
620 NetSetPgrp(fd)
621 int fd;
622 {
623     int myPid;
624 
625     myPid = getpid();
626     fcntl(fd, F_SETOWN, myPid);
627 }
628 #endif	/*defined(TN3270)*/
629 
630 /*
631  * Various signal handling routines.
632  */
633 
634 static void
635 deadpeer()
636 {
637 	setcommandmode();
638 	longjmp(peerdied, -1);
639 }
640 
641 static void
642 intr()
643 {
644     if (localchars) {
645 	intp();
646 	return;
647     }
648     setcommandmode();
649     longjmp(toplevel, -1);
650 }
651 
652 static void
653 intr2()
654 {
655     if (localchars) {
656 #ifdef	KLUDGELINEMODE
657 	if (kludgelinemode)
658 	    sendbrk();
659 	else
660 #endif
661 	    sendabort();
662 	return;
663     }
664 }
665 
666 static void
667 susp()
668 {
669     if (localchars)
670 	sendsusp();
671 }
672 
673 static void
674 sendwin()
675 {
676     if (connected) {
677 	sendnaws();
678     }
679 }
680 
681 static void
682 doescape()
683 {
684     command(0, 0, 0);
685 }
686 
687 void
688 sys_telnet_init()
689 {
690     (void) signal(SIGINT, (SIG_FUNC_RET (*)())intr);
691     (void) signal(SIGQUIT, (SIG_FUNC_RET (*)())intr2);
692     (void) signal(SIGPIPE, (SIG_FUNC_RET (*)())deadpeer);
693 #ifdef	SIGWINCH
694     (void) signal(SIGWINCH, (SIG_FUNC_RET (*)())sendwin);
695 #endif
696 #ifdef	SIGTSTP
697     (void) signal(SIGTSTP, (SIG_FUNC_RET (*)())susp);
698 #endif
699 
700     setconnmode(0);
701 
702     NetNonblockingIO(net, 1);
703 
704 #if	defined(TN3270)
705     if (noasynchnet == 0) {			/* DBX can't handle! */
706 	NetSigIO(net, 1);
707 	NetSetPgrp(net);
708     }
709 #endif	/* defined(TN3270) */
710 
711 #if	defined(SO_OOBINLINE)
712     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
713 	perror("SetSockOpt");
714     }
715 #endif	/* defined(SO_OOBINLINE) */
716 }
717 
718 /*
719  * Process rings -
720  *
721  *	This routine tries to fill up/empty our various rings.
722  *
723  *	The parameter specifies whether this is a poll operation,
724  *	or a block-until-something-happens operation.
725  *
726  *	The return value is 1 if something happened, 0 if not.
727  */
728 
729 int
730 process_rings(netin, netout, netex, ttyin, ttyout, poll)
731 int poll;		/* If 0, then block until something to do */
732 {
733     register int c;
734 		/* One wants to be a bit careful about setting returnValue
735 		 * to one, since a one implies we did some useful work,
736 		 * and therefore probably won't be called to block next
737 		 * time (TN3270 mode only).
738 		 */
739     int returnValue = 0;
740     static struct timeval TimeValue = { 0 };
741 
742     if (netout) {
743 	FD_SET(net, &obits);
744     }
745     if (ttyout) {
746 	FD_SET(tout, &obits);
747     }
748 #if	defined(TN3270)
749     if (ttyin) {
750 	FD_SET(tin, &ibits);
751     }
752 #else	/* defined(TN3270) */
753     if (ttyin) {
754 	FD_SET(tin, &ibits);
755     }
756 #endif	/* defined(TN3270) */
757 #if	defined(TN3270)
758     if (netin) {
759 	FD_SET(net, &ibits);
760     }
761 #   else /* !defined(TN3270) */
762     if (netin) {
763 	FD_SET(net, &ibits);
764     }
765 #   endif /* !defined(TN3270) */
766     if (netex) {
767 	FD_SET(net, &xbits);
768     }
769     if ((c = select(16, &ibits, &obits, &xbits,
770 			(poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
771 	if (c == -1) {
772 		    /*
773 		     * we can get EINTR if we are in line mode,
774 		     * and the user does an escape (TSTP), or
775 		     * some other signal generator.
776 		     */
777 	    if (errno == EINTR) {
778 		return 0;
779 	    }
780 #	    if defined(TN3270)
781 		    /*
782 		     * we can get EBADF if we were in transparent
783 		     * mode, and the transcom process died.
784 		    */
785 	    if (errno == EBADF) {
786 			/*
787 			 * zero the bits (even though kernel does it)
788 			 * to make sure we are selecting on the right
789 			 * ones.
790 			*/
791 		FD_ZERO(&ibits);
792 		FD_ZERO(&obits);
793 		FD_ZERO(&xbits);
794 		return 0;
795 	    }
796 #	    endif /* defined(TN3270) */
797 		    /* I don't like this, does it ever happen? */
798 	    printf("sleep(5) from telnet, after select\r\n");
799 	    sleep(5);
800 	}
801 	return 0;
802     }
803 
804     /*
805      * Any urgent data?
806      */
807     if (FD_ISSET(net, &xbits)) {
808 	FD_CLR(net, &xbits);
809 	SYNCHing = 1;
810 	ttyflush(1);	/* flush already enqueued data */
811     }
812 
813     /*
814      * Something to read from the network...
815      */
816     if (FD_ISSET(net, &ibits)) {
817 	int canread;
818 
819 	FD_CLR(net, &ibits);
820 	canread = ring_empty_consecutive(&netiring);
821 #if	!defined(SO_OOBINLINE)
822 	    /*
823 	     * In 4.2 (and some early 4.3) systems, the
824 	     * OOB indication and data handling in the kernel
825 	     * is such that if two separate TCP Urgent requests
826 	     * come in, one byte of TCP data will be overlaid.
827 	     * This is fatal for Telnet, but we try to live
828 	     * with it.
829 	     *
830 	     * In addition, in 4.2 (and...), a special protocol
831 	     * is needed to pick up the TCP Urgent data in
832 	     * the correct sequence.
833 	     *
834 	     * What we do is:  if we think we are in urgent
835 	     * mode, we look to see if we are "at the mark".
836 	     * If we are, we do an OOB receive.  If we run
837 	     * this twice, we will do the OOB receive twice,
838 	     * but the second will fail, since the second
839 	     * time we were "at the mark", but there wasn't
840 	     * any data there (the kernel doesn't reset
841 	     * "at the mark" until we do a normal read).
842 	     * Once we've read the OOB data, we go ahead
843 	     * and do normal reads.
844 	     *
845 	     * There is also another problem, which is that
846 	     * since the OOB byte we read doesn't put us
847 	     * out of OOB state, and since that byte is most
848 	     * likely the TELNET DM (data mark), we would
849 	     * stay in the TELNET SYNCH (SYNCHing) state.
850 	     * So, clocks to the rescue.  If we've "just"
851 	     * received a DM, then we test for the
852 	     * presence of OOB data when the receive OOB
853 	     * fails (and AFTER we did the normal mode read
854 	     * to clear "at the mark").
855 	     */
856 	if (SYNCHing) {
857 	    int atmark;
858 	    static int bogus_oob = 0, first = 1;
859 
860 	    ioctl(net, SIOCATMARK, (char *)&atmark);
861 	    if (atmark) {
862 		c = recv(net, netiring.supply, canread, MSG_OOB);
863 		if ((c == -1) && (errno == EINVAL)) {
864 		    c = recv(net, netiring.supply, canread, 0);
865 		    if (clocks.didnetreceive < clocks.gotDM) {
866 			SYNCHing = stilloob(net);
867 		    }
868 		} else if (first && c > 0) {
869 		    /*
870 		     * Bogosity check.  Systems based on 4.2BSD
871 		     * do not return an error if you do a second
872 		     * recv(MSG_OOB).  So, we do one.  If it
873 		     * succeeds and returns exactly the same
874 		     * data, then assume that we are running
875 		     * on a broken system and set the bogus_oob
876 		     * flag.  (If the data was different, then
877 		     * we probably got some valid new data, so
878 		     * increment the count...)
879 		     */
880 		    int i;
881 		    i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
882 		    if (i == c &&
883 			  bcmp(netiring.supply, netiring.supply + c, i) == 0) {
884 			bogus_oob = 1;
885 			first = 0;
886 		    } else if (i < 0) {
887 			bogus_oob = 0;
888 			first = 0;
889 		    } else
890 			c += i;
891 		}
892 		if (bogus_oob && c > 0) {
893 		    int i;
894 		    /*
895 		     * Bogosity.  We have to do the read
896 		     * to clear the atmark to get out of
897 		     * an infinate loop.
898 		     */
899 		    i = read(net, netiring.supply + c, canread - c);
900 		    if (i > 0)
901 			c += i;
902 		}
903 	    } else {
904 		c = recv(net, netiring.supply, canread, 0);
905 	    }
906 	} else {
907 	    c = recv(net, netiring.supply, canread, 0);
908 	}
909 	settimer(didnetreceive);
910 #else	/* !defined(SO_OOBINLINE) */
911 	c = recv(net, netiring.supply, canread, 0);
912 #endif	/* !defined(SO_OOBINLINE) */
913 	if (c < 0 && errno == EWOULDBLOCK) {
914 	    c = 0;
915 	} else if (c <= 0) {
916 	    return -1;
917 	}
918 	if (netdata) {
919 	    Dump('<', netiring.supply, c);
920 	}
921 	if (c)
922 	    ring_supplied(&netiring, c);
923 	returnValue = 1;
924     }
925 
926     /*
927      * Something to read from the tty...
928      */
929     if (FD_ISSET(tin, &ibits)) {
930 	FD_CLR(tin, &ibits);
931 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
932 	if (c < 0 && errno == EWOULDBLOCK) {
933 	    c = 0;
934 	} else {
935 	    /* EOF detection for line mode!!!! */
936 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
937 			/* must be an EOF... */
938 		*ttyiring.supply = termEofChar;
939 		c = 1;
940 	    }
941 	    if (c <= 0) {
942 		return -1;
943 	    }
944 	    if (termdata) {
945 		Dump('<', ttyiring.supply, c);
946 	    }
947 	    ring_supplied(&ttyiring, c);
948 	}
949 	returnValue = 1;		/* did something useful */
950     }
951 
952     if (FD_ISSET(net, &obits)) {
953 	FD_CLR(net, &obits);
954 	returnValue |= netflush();
955     }
956     if (FD_ISSET(tout, &obits)) {
957 	FD_CLR(tout, &obits);
958 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
959     }
960 
961     return returnValue;
962 }
963