Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / appl / telnet / telnet / sys_bsd.c
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: sys_bsd.c,v 1.23.18.2 2000/10/19 21:21:21 assar Exp $");
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 #ifdef  KLUDGELINEMODE
122 extern int kludgelinemode;
123 #endif
124 /*
125  * TerminalSpecialChars()
126  *
127  * Look at an input character to see if it is a special character
128  * and decide what to do.
129  *
130  * Output:
131  *
132  *      0       Don't add this character.
133  *      1       Do add this character
134  */
135
136 int
137 TerminalSpecialChars(int c)
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(void)
181 {
182 #ifdef  TIOCFLUSH
183     ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
184 #else
185     ioctl(fileno(stdout), TCFLSH, (char *) 0);
186 #endif
187 }
188
189 void
190 TerminalSaveState(void)
191 {
192     tcgetattr(0, &old_tc);
193
194     new_tc = old_tc;
195
196 #ifndef VDISCARD
197     termFlushChar = CONTROL('O');
198 #endif
199 #ifndef VWERASE
200     termWerasChar = CONTROL('W');
201 #endif
202 #ifndef VREPRINT
203     termRprntChar = CONTROL('R');
204 #endif
205 #ifndef VLNEXT
206     termLiteralNextChar = CONTROL('V');
207 #endif
208 #ifndef VSTART
209     termStartChar = CONTROL('Q');
210 #endif
211 #ifndef VSTOP
212     termStopChar = CONTROL('S');
213 #endif
214 #ifndef VSTATUS
215     termAytChar = CONTROL('T');
216 #endif
217 }
218
219 cc_t*
220 tcval(int func)
221 {
222     switch(func) {
223     case SLC_IP:        return(&termIntChar);
224     case SLC_ABORT:     return(&termQuitChar);
225     case SLC_EOF:       return(&termEofChar);
226     case SLC_EC:        return(&termEraseChar);
227     case SLC_EL:        return(&termKillChar);
228     case SLC_XON:       return(&termStartChar);
229     case SLC_XOFF:      return(&termStopChar);
230     case SLC_FORW1:     return(&termForw1Char);
231     case SLC_FORW2:     return(&termForw2Char);
232 # ifdef VDISCARD
233     case SLC_AO:        return(&termFlushChar);
234 # endif
235 # ifdef VSUSP
236     case SLC_SUSP:      return(&termSuspChar);
237 # endif
238 # ifdef VWERASE
239     case SLC_EW:        return(&termWerasChar);
240 # endif
241 # ifdef VREPRINT
242     case SLC_RP:        return(&termRprntChar);
243 # endif
244 # ifdef VLNEXT
245     case SLC_LNEXT:     return(&termLiteralNextChar);
246 # endif
247 # ifdef VSTATUS
248     case SLC_AYT:       return(&termAytChar);
249 # endif
250
251     case SLC_SYNCH:
252     case SLC_BRK:
253     case SLC_EOR:
254     default:
255         return((cc_t *)0);
256     }
257 }
258
259 void
260 TerminalDefaultChars(void)
261 {
262     memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
263 # ifndef        VDISCARD
264     termFlushChar = CONTROL('O');
265 # endif
266 # ifndef        VWERASE
267     termWerasChar = CONTROL('W');
268 # endif
269 # ifndef        VREPRINT
270     termRprntChar = CONTROL('R');
271 # endif
272 # ifndef        VLNEXT
273     termLiteralNextChar = CONTROL('V');
274 # endif
275 # ifndef        VSTART
276     termStartChar = CONTROL('Q');
277 # endif
278 # ifndef        VSTOP
279     termStopChar = CONTROL('S');
280 # endif
281 # ifndef        VSTATUS
282     termAytChar = CONTROL('T');
283 # endif
284 }
285
286 #ifdef notdef
287 void
288 TerminalRestoreState()
289 {
290 }
291 #endif
292
293 /*
294  * TerminalNewMode - set up terminal to a specific mode.
295  *      MODE_ECHO: do local terminal echo
296  *      MODE_FLOW: do local flow control
297  *      MODE_TRAPSIG: do local mapping to TELNET IAC sequences
298  *      MODE_EDIT: do local line editing
299  *
300  *      Command mode:
301  *              MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
302  *              local echo
303  *              local editing
304  *              local xon/xoff
305  *              local signal mapping
306  *
307  *      Linemode:
308  *              local/no editing
309  *      Both Linemode and Single Character mode:
310  *              local/remote echo
311  *              local/no xon/xoff
312  *              local/no signal mapping
313  */
314
315
316 #ifdef  SIGTSTP
317 static RETSIGTYPE susp();
318 #endif  /* SIGTSTP */
319 #ifdef  SIGINFO
320 static RETSIGTYPE ayt();
321 #endif
322
323 void
324 TerminalNewMode(int f)
325 {
326     static int prevmode = 0;
327     struct termios tmp_tc;
328     int onoff;
329     int old;
330     cc_t esc;
331
332     globalmode = f&~MODE_FORCE;
333     if (prevmode == f)
334         return;
335
336     /*
337      * Write any outstanding data before switching modes
338      * ttyflush() returns 0 only when there is no more data
339      * left to write out, it returns -1 if it couldn't do
340      * anything at all, otherwise it returns 1 + the number
341      * of characters left to write.
342      */
343     old = ttyflush(SYNCHing|flushout);
344     if (old < 0 || old > 1) {
345         tcgetattr(tin, &tmp_tc);
346         do {
347             /*
348              * Wait for data to drain, then flush again.
349              */
350             tcsetattr(tin, TCSADRAIN, &tmp_tc);
351             old = ttyflush(SYNCHing|flushout);
352         } while (old < 0 || old > 1);
353     }
354
355     old = prevmode;
356     prevmode = f&~MODE_FORCE;
357     tmp_tc = new_tc;
358
359     if (f&MODE_ECHO) {
360         tmp_tc.c_lflag |= ECHO;
361         tmp_tc.c_oflag |= ONLCR;
362         if (crlf)
363                 tmp_tc.c_iflag |= ICRNL;
364     } else {
365         tmp_tc.c_lflag &= ~ECHO;
366         tmp_tc.c_oflag &= ~ONLCR;
367 # ifdef notdef
368         if (crlf)
369                 tmp_tc.c_iflag &= ~ICRNL;
370 # endif
371     }
372
373     if ((f&MODE_FLOW) == 0) {
374         tmp_tc.c_iflag &= ~(IXOFF|IXON);        /* Leave the IXANY bit alone */
375     } else {
376         if (restartany < 0) {
377                 tmp_tc.c_iflag |= IXOFF|IXON;   /* Leave the IXANY bit alone */
378         } else if (restartany > 0) {
379                 tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
380         } else {
381                 tmp_tc.c_iflag |= IXOFF|IXON;
382                 tmp_tc.c_iflag &= ~IXANY;
383         }
384     }
385
386     if ((f&MODE_TRAPSIG) == 0) {
387         tmp_tc.c_lflag &= ~ISIG;
388         localchars = 0;
389     } else {
390         tmp_tc.c_lflag |= ISIG;
391         localchars = 1;
392     }
393
394     if (f&MODE_EDIT) {
395         tmp_tc.c_lflag |= ICANON;
396     } else {
397         tmp_tc.c_lflag &= ~ICANON;
398         tmp_tc.c_iflag &= ~ICRNL;
399         tmp_tc.c_cc[VMIN] = 1;
400         tmp_tc.c_cc[VTIME] = 0;
401     }
402
403     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
404 # ifdef VLNEXT
405         tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
406 # endif
407     }
408
409     if (f&MODE_SOFT_TAB) {
410 # ifdef OXTABS
411         tmp_tc.c_oflag |= OXTABS;
412 # endif
413 # ifdef TABDLY
414         tmp_tc.c_oflag &= ~TABDLY;
415         tmp_tc.c_oflag |= TAB3;
416 # endif
417     } else {
418 # ifdef OXTABS
419         tmp_tc.c_oflag &= ~OXTABS;
420 # endif
421 # ifdef TABDLY
422         tmp_tc.c_oflag &= ~TABDLY;
423 # endif
424     }
425
426     if (f&MODE_LIT_ECHO) {
427 # ifdef ECHOCTL
428         tmp_tc.c_lflag &= ~ECHOCTL;
429 # endif
430     } else {
431 # ifdef ECHOCTL
432         tmp_tc.c_lflag |= ECHOCTL;
433 # endif
434     }
435
436     if (f == -1) {
437         onoff = 0;
438     } else {
439         if (f & MODE_INBIN)
440                 tmp_tc.c_iflag &= ~ISTRIP;
441         else
442                 tmp_tc.c_iflag |= ISTRIP;
443         if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
444                 tmp_tc.c_cflag &= ~(CSIZE|PARENB);
445                 tmp_tc.c_cflag |= CS8;
446                 if(f & MODE_OUTBIN)
447                     tmp_tc.c_oflag &= ~OPOST;
448                 else
449                     tmp_tc.c_oflag |= OPOST;
450         } else {
451                 tmp_tc.c_cflag &= ~(CSIZE|PARENB);
452                 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
453                 tmp_tc.c_oflag |= OPOST;
454         }
455         onoff = 1;
456     }
457
458     if (f != -1) {
459
460 #ifdef  SIGTSTP
461         signal(SIGTSTP, susp);
462 #endif  /* SIGTSTP */
463 #ifdef  SIGINFO
464         signal(SIGINFO, ayt);
465 #endif
466 #ifdef NOKERNINFO
467         tmp_tc.c_lflag |= NOKERNINFO;
468 #endif
469         /*
470          * We don't want to process ^Y here.  It's just another
471          * character that we'll pass on to the back end.  It has
472          * to process it because it will be processed when the
473          * user attempts to read it, not when we send it.
474          */
475 # ifdef VDSUSP
476         tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
477 # endif
478         /*
479          * If the VEOL character is already set, then use VEOL2,
480          * otherwise use VEOL.
481          */
482         esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
483         if ((tmp_tc.c_cc[VEOL] != esc)
484 # ifdef VEOL2
485             && (tmp_tc.c_cc[VEOL2] != esc)
486 # endif
487             ) {
488                 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
489                     tmp_tc.c_cc[VEOL] = esc;
490 # ifdef VEOL2
491                 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
492                     tmp_tc.c_cc[VEOL2] = esc;
493 # endif
494         }
495     } else {
496         sigset_t sm;
497 #ifdef  SIGINFO
498         RETSIGTYPE ayt_status();
499
500         signal(SIGINFO, ayt_status);
501 #endif
502 #ifdef  SIGTSTP
503         signal(SIGTSTP, SIG_DFL);
504         sigemptyset(&sm);
505         sigaddset(&sm, SIGTSTP);
506         sigprocmask(SIG_UNBLOCK, &sm, NULL);
507 #endif  /* SIGTSTP */
508         tmp_tc = old_tc;
509     }
510     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
511         tcsetattr(tin, TCSANOW, &tmp_tc);
512
513     ioctl(tin, FIONBIO, (char *)&onoff);
514     ioctl(tout, FIONBIO, (char *)&onoff);
515
516 }
517
518 /*
519  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
520  */
521 #if B4800 != 4800
522 #define DECODE_BAUD
523 #endif
524
525 #ifdef  DECODE_BAUD
526 #ifndef B7200
527 #define B7200   B4800
528 #endif
529
530 #ifndef B14400
531 #define B14400  B9600
532 #endif
533
534 #ifndef B19200
535 # define B19200 B14400
536 #endif
537
538 #ifndef B28800
539 #define B28800  B19200
540 #endif
541
542 #ifndef B38400
543 # define B38400 B28800
544 #endif
545
546 #ifndef B57600
547 #define B57600  B38400
548 #endif
549
550 #ifndef B76800
551 #define B76800  B57600
552 #endif
553
554 #ifndef B115200
555 #define B115200 B76800
556 #endif
557
558 #ifndef B230400
559 #define B230400 B115200
560 #endif
561
562
563 /*
564  * This code assumes that the values B0, B50, B75...
565  * are in ascending order.  They do not have to be
566  * contiguous.
567  */
568 struct termspeeds {
569         long speed;
570         long value;
571 } termspeeds[] = {
572         { 0,      B0 },      { 50,    B50 },    { 75,     B75 },
573         { 110,    B110 },    { 134,   B134 },   { 150,    B150 },
574         { 200,    B200 },    { 300,   B300 },   { 600,    B600 },
575         { 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
576         { 4800,   B4800 },   { 7200,  B7200 },  { 9600,   B9600 },
577         { 14400,  B14400 },  { 19200, B19200 }, { 28800,  B28800 },
578         { 38400,  B38400 },  { 57600, B57600 }, { 115200, B115200 },
579         { 230400, B230400 }, { -1,    B230400 }
580 };
581 #endif  /* DECODE_BAUD */
582
583 void
584 TerminalSpeeds(long *input_speed, long *output_speed)
585 {
586 #ifdef  DECODE_BAUD
587     struct termspeeds *tp;
588 #endif  /* DECODE_BAUD */
589     long in, out;
590
591     out = cfgetospeed(&old_tc);
592     in = cfgetispeed(&old_tc);
593     if (in == 0)
594         in = out;
595
596 #ifdef  DECODE_BAUD
597     tp = termspeeds;
598     while ((tp->speed != -1) && (tp->value < in))
599         tp++;
600     *input_speed = tp->speed;
601
602     tp = termspeeds;
603     while ((tp->speed != -1) && (tp->value < out))
604         tp++;
605     *output_speed = tp->speed;
606 #else   /* DECODE_BAUD */
607         *input_speed = in;
608         *output_speed = out;
609 #endif  /* DECODE_BAUD */
610 }
611
612 int
613 TerminalWindowSize(long *rows, long *cols)
614 {
615     struct winsize ws;
616
617     if (get_window_size (STDIN_FILENO, &ws) == 0) {
618         *rows = ws.ws_row;
619         *cols = ws.ws_col;
620         return 1;
621     } else
622         return 0;
623 }
624
625 int
626 NetClose(int fd)
627 {
628     return close(fd);
629 }
630
631
632 void
633 NetNonblockingIO(int fd, int onoff)
634 {
635     ioctl(fd, FIONBIO, (char *)&onoff);
636 }
637
638 \f
639 /*
640  * Various signal handling routines.
641  */
642
643 static RETSIGTYPE deadpeer(int),
644   intr(int), intr2(int), susp(int), sendwin(int);
645 #ifdef SIGINFO
646 static RETSIGTYPE ayt(int);
647 #endif
648   
649
650     /* ARGSUSED */
651 static RETSIGTYPE
652 deadpeer(int sig)
653 {
654         setcommandmode();
655         longjmp(peerdied, -1);
656 }
657
658     /* ARGSUSED */
659 static RETSIGTYPE
660 intr(int sig)
661 {
662     if (localchars) {
663         intp();
664         return;
665     }
666     setcommandmode();
667     longjmp(toplevel, -1);
668 }
669
670     /* ARGSUSED */
671 static RETSIGTYPE
672 intr2(int sig)
673 {
674     if (localchars) {
675 #ifdef  KLUDGELINEMODE
676         if (kludgelinemode)
677             sendbrk();
678         else
679 #endif
680             sendabort();
681         return;
682     }
683 }
684
685 #ifdef  SIGTSTP
686     /* ARGSUSED */
687 static RETSIGTYPE
688 susp(int sig)
689 {
690     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
691         return;
692     if (localchars)
693         sendsusp();
694 }
695 #endif
696
697 #ifdef  SIGWINCH
698     /* ARGSUSED */
699 static RETSIGTYPE
700 sendwin(int sig)
701 {
702     if (connected) {
703         sendnaws();
704     }
705 }
706 #endif
707
708 #ifdef  SIGINFO
709     /* ARGSUSED */
710 static RETSIGTYPE
711 ayt(int sig)
712 {
713     if (connected)
714         sendayt();
715     else
716         ayt_status(sig);
717 }
718 #endif
719
720 \f
721 void
722 sys_telnet_init(void)
723 {
724     signal(SIGINT, intr);
725     signal(SIGQUIT, intr2);
726     signal(SIGPIPE, deadpeer);
727 #ifdef  SIGWINCH
728     signal(SIGWINCH, sendwin);
729 #endif
730 #ifdef  SIGTSTP
731     signal(SIGTSTP, susp);
732 #endif
733 #ifdef  SIGINFO
734     signal(SIGINFO, ayt);
735 #endif
736
737     setconnmode(0);
738
739     NetNonblockingIO(net, 1);
740
741
742 #if     defined(SO_OOBINLINE)
743     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
744         perror("SetSockOpt");
745     }
746 #endif  /* defined(SO_OOBINLINE) */
747 }
748
749 /*
750  * Process rings -
751  *
752  *      This routine tries to fill up/empty our various rings.
753  *
754  *      The parameter specifies whether this is a poll operation,
755  *      or a block-until-something-happens operation.
756  *
757  *      The return value is 1 if something happened, 0 if not.
758  */
759
760 int
761 process_rings(int netin,
762               int netout,
763               int netex,
764               int ttyin,
765               int ttyout,
766               int poll) /* If 0, then block until something to do */
767 {
768     int c;
769                 /* One wants to be a bit careful about setting returnValue
770                  * to one, since a one implies we did some useful work,
771                  * and therefore probably won't be called to block next
772                  * time (TN3270 mode only).
773                  */
774     int returnValue = 0;
775     static struct timeval TimeValue = { 0 };
776
777     if (net >= FD_SETSIZE
778         || tout >= FD_SETSIZE
779         || tin >= FD_SETSIZE)
780         errx (1, "fd too large");
781
782     if (netout) {
783         FD_SET(net, &obits);
784     }
785     if (ttyout) {
786         FD_SET(tout, &obits);
787     }
788     if (ttyin) {
789         FD_SET(tin, &ibits);
790     }
791     if (netin) {
792         FD_SET(net, &ibits);
793     }
794 #if !defined(SO_OOBINLINE)
795     if (netex) {
796         FD_SET(net, &xbits);
797     }
798 #endif
799     if ((c = select(FD_SETSIZE, &ibits, &obits, &xbits,
800                         (poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
801         if (c == -1) {
802                     /*
803                      * we can get EINTR if we are in line mode,
804                      * and the user does an escape (TSTP), or
805                      * some other signal generator.
806                      */
807             if (errno == EINTR) {
808                 return 0;
809             }
810                     /* I don't like this, does it ever happen? */
811             printf("sleep(5) from telnet, after select\r\n");
812             sleep(5);
813         }
814         return 0;
815     }
816
817     /*
818      * Any urgent data?
819      */
820     if (FD_ISSET(net, &xbits)) {
821         FD_CLR(net, &xbits);
822         SYNCHing = 1;
823         ttyflush(1);    /* flush already enqueued data */
824     }
825
826     /*
827      * Something to read from the network...
828      */
829     if (FD_ISSET(net, &ibits)) {
830         int canread;
831
832         FD_CLR(net, &ibits);
833         canread = ring_empty_consecutive(&netiring);
834 #if     !defined(SO_OOBINLINE)
835             /*
836              * In 4.2 (and some early 4.3) systems, the
837              * OOB indication and data handling in the kernel
838              * is such that if two separate TCP Urgent requests
839              * come in, one byte of TCP data will be overlaid.
840              * This is fatal for Telnet, but we try to live
841              * with it.
842              *
843              * In addition, in 4.2 (and...), a special protocol
844              * is needed to pick up the TCP Urgent data in
845              * the correct sequence.
846              *
847              * What we do is:  if we think we are in urgent
848              * mode, we look to see if we are "at the mark".
849              * If we are, we do an OOB receive.  If we run
850              * this twice, we will do the OOB receive twice,
851              * but the second will fail, since the second
852              * time we were "at the mark", but there wasn't
853              * any data there (the kernel doesn't reset
854              * "at the mark" until we do a normal read).
855              * Once we've read the OOB data, we go ahead
856              * and do normal reads.
857              *
858              * There is also another problem, which is that
859              * since the OOB byte we read doesn't put us
860              * out of OOB state, and since that byte is most
861              * likely the TELNET DM (data mark), we would
862              * stay in the TELNET SYNCH (SYNCHing) state.
863              * So, clocks to the rescue.  If we've "just"
864              * received a DM, then we test for the
865              * presence of OOB data when the receive OOB
866              * fails (and AFTER we did the normal mode read
867              * to clear "at the mark").
868              */
869         if (SYNCHing) {
870             int atmark;
871             static int bogus_oob = 0, first = 1;
872
873             ioctl(net, SIOCATMARK, (char *)&atmark);
874             if (atmark) {
875                 c = recv(net, netiring.supply, canread, MSG_OOB);
876                 if ((c == -1) && (errno == EINVAL)) {
877                     c = recv(net, netiring.supply, canread, 0);
878                     if (clocks.didnetreceive < clocks.gotDM) {
879                         SYNCHing = stilloob();
880                     }
881                 } else if (first && c > 0) {
882                     /*
883                      * Bogosity check.  Systems based on 4.2BSD
884                      * do not return an error if you do a second
885                      * recv(MSG_OOB).  So, we do one.  If it
886                      * succeeds and returns exactly the same
887                      * data, then assume that we are running
888                      * on a broken system and set the bogus_oob
889                      * flag.  (If the data was different, then
890                      * we probably got some valid new data, so
891                      * increment the count...)
892                      */
893                     int i;
894                     i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
895                     if (i == c &&
896                          memcmp(netiring.supply, netiring.supply + c, i) == 0) {
897                         bogus_oob = 1;
898                         first = 0;
899                     } else if (i < 0) {
900                         bogus_oob = 0;
901                         first = 0;
902                     } else
903                         c += i;
904                 }
905                 if (bogus_oob && c > 0) {
906                     int i;
907                     /*
908                      * Bogosity.  We have to do the read
909                      * to clear the atmark to get out of
910                      * an infinate loop.
911                      */
912                     i = read(net, netiring.supply + c, canread - c);
913                     if (i > 0)
914                         c += i;
915                 }
916             } else {
917                 c = recv(net, netiring.supply, canread, 0);
918             }
919         } else {
920             c = recv(net, netiring.supply, canread, 0);
921         }
922         settimer(didnetreceive);
923 #else   /* !defined(SO_OOBINLINE) */
924         c = recv(net, (char *)netiring.supply, canread, 0);
925 #endif  /* !defined(SO_OOBINLINE) */
926         if (c < 0 && errno == EWOULDBLOCK) {
927             c = 0;
928         } else if (c <= 0) {
929             return -1;
930         }
931         if (netdata) {
932             Dump('<', netiring.supply, c);
933         }
934         if (c)
935             ring_supplied(&netiring, c);
936         returnValue = 1;
937     }
938
939     /*
940      * Something to read from the tty...
941      */
942     if (FD_ISSET(tin, &ibits)) {
943         FD_CLR(tin, &ibits);
944         c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
945         if (c < 0 && errno == EIO)
946             c = 0;
947         if (c < 0 && errno == EWOULDBLOCK) {
948             c = 0;
949         } else {
950             /* EOF detection for line mode!!!! */
951             if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
952                         /* must be an EOF... */
953                 *ttyiring.supply = termEofChar;
954                 c = 1;
955             }
956             if (c <= 0) {
957                 return -1;
958             }
959             if (termdata) {
960                 Dump('<', ttyiring.supply, c);
961             }
962             ring_supplied(&ttyiring, c);
963         }
964         returnValue = 1;                /* did something useful */
965     }
966
967     if (FD_ISSET(net, &obits)) {
968         FD_CLR(net, &obits);
969         returnValue |= netflush();
970     }
971     if (FD_ISSET(tout, &obits)) {
972         FD_CLR(tout, &obits);
973         returnValue |= (ttyflush(SYNCHing|flushout) > 0);
974     }
975
976     return returnValue;
977 }