xref: /openbsd-src/usr.bin/telnet/sys_bsd.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: sys_bsd.c,v 1.32 2016/03/16 15:41:11 krw Exp $	*/
2 /*	$NetBSD: sys_bsd.c,v 1.11 1996/02/28 21:04:10 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1988, 1990, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "telnet_locl.h"
34 
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <arpa/telnet.h>
38 #include <errno.h>
39 #include <poll.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 /*
44  * The following routines try to encapsulate what is system dependent
45  * (at least between 4.x and dos) which is used in telnet.c.
46  */
47 
48 int
49 	tout,			/* Output file descriptor */
50 	tin,			/* Input file descriptor */
51 	net;
52 
53 #define TELNET_FD_TOUT	0
54 #define TELNET_FD_TIN	1
55 #define TELNET_FD_NET	2
56 #define TELNET_FD_NUM	3
57 
58 struct	termios old_tc = { 0 };
59 
60 void
61 init_sys(void)
62 {
63     tout = fileno(stdout);
64     tin = fileno(stdin);
65 
66     errno = 0;
67 }
68 
69 
70 /*
71  * TerminalSpecialChars()
72  *
73  * Look at an input character to see if it is a special character
74  * and decide what to do.
75  *
76  * Output:
77  *
78  *	0	Don't add this character.
79  *	1	Do add this character
80  */
81 
82 int
83 TerminalSpecialChars(int c)
84 {
85     if (c == termIntChar) {
86 	intp();
87 	return 0;
88     } else if (c == termQuitChar) {
89 #ifdef	KLUDGELINEMODE
90 	if (kludgelinemode)
91 	    sendbrk();
92 	else
93 #endif
94 	    sendabort();
95 	return 0;
96     } else if (c == termEofChar) {
97 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
98 	    sendeof();
99 	    return 0;
100 	}
101 	return 1;
102     } else if (c == termSuspChar) {
103 	sendsusp();
104 	return(0);
105     } else if (c == termFlushChar) {
106 	xmitAO();		/* Transmit Abort Output */
107 	return 0;
108     } else if (!MODE_LOCAL_CHARS(globalmode)) {
109 	if (c == termKillChar) {
110 	    xmitEL();
111 	    return 0;
112 	} else if (c == termEraseChar) {
113 	    xmitEC();		/* Transmit Erase Character */
114 	    return 0;
115 	}
116     }
117     return 1;
118 }
119 
120 void
121 TerminalSaveState(void)
122 {
123     tcgetattr(0, &old_tc);
124 
125     new_tc = old_tc;
126 
127 #ifndef	VDISCARD
128     termFlushChar = CONTROL('O');
129 #endif
130 #ifndef	VWERASE
131     termWerasChar = CONTROL('W');
132 #endif
133 #ifndef	VREPRINT
134     termRprntChar = CONTROL('R');
135 #endif
136 #ifndef	VLNEXT
137     termLiteralNextChar = CONTROL('V');
138 #endif
139 #ifndef	VSTART
140     termStartChar = CONTROL('Q');
141 #endif
142 #ifndef	VSTOP
143     termStopChar = CONTROL('S');
144 #endif
145 #ifndef	VSTATUS
146     termAytChar = CONTROL('T');
147 #endif
148 }
149 
150 cc_t *
151 tcval(int func)
152 {
153     switch(func) {
154     case SLC_IP:	return(&termIntChar);
155     case SLC_ABORT:	return(&termQuitChar);
156     case SLC_EOF:	return(&termEofChar);
157     case SLC_EC:	return(&termEraseChar);
158     case SLC_EL:	return(&termKillChar);
159     case SLC_XON:	return(&termStartChar);
160     case SLC_XOFF:	return(&termStopChar);
161     case SLC_FORW1:	return(&termForw1Char);
162     case SLC_FORW2:	return(&termForw2Char);
163     case SLC_SUSP:	return(&termSuspChar);
164 # ifdef	VDISCARD
165     case SLC_AO:	return(&termFlushChar);
166 # endif
167 # ifdef	VWERASE
168     case SLC_EW:	return(&termWerasChar);
169 # endif
170 # ifdef	VREPRINT
171     case SLC_RP:	return(&termRprntChar);
172 # endif
173 # ifdef	VLNEXT
174     case SLC_LNEXT:	return(&termLiteralNextChar);
175 # endif
176 # ifdef	VSTATUS
177     case SLC_AYT:	return(&termAytChar);
178 # endif
179 
180     case SLC_SYNCH:
181     case SLC_BRK:
182     case SLC_EOR:
183     default:
184 	return(NULL);
185     }
186 }
187 
188 void
189 TerminalDefaultChars(void)
190 {
191     memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
192 # ifndef	VDISCARD
193     termFlushChar = CONTROL('O');
194 # endif
195 # ifndef	VWERASE
196     termWerasChar = CONTROL('W');
197 # endif
198 # ifndef	VREPRINT
199     termRprntChar = CONTROL('R');
200 # endif
201 # ifndef	VLNEXT
202     termLiteralNextChar = CONTROL('V');
203 # endif
204 # ifndef	VSTART
205     termStartChar = CONTROL('Q');
206 # endif
207 # ifndef	VSTOP
208     termStopChar = CONTROL('S');
209 # endif
210 # ifndef	VSTATUS
211     termAytChar = CONTROL('T');
212 # endif
213 }
214 
215 /*
216  * TerminalNewMode - set up terminal to a specific mode.
217  *	MODE_ECHO: do local terminal echo
218  *	MODE_FLOW: do local flow control
219  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
220  *	MODE_EDIT: do local line editing
221  *
222  *	Command mode:
223  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
224  *		local echo
225  *		local editing
226  *		local xon/xoff
227  *		local signal mapping
228  *
229  *	Linemode:
230  *		local/no editing
231  *	Both Linemode and Single Character mode:
232  *		local/remote echo
233  *		local/no xon/xoff
234  *		local/no signal mapping
235  */
236 
237 static void susp();
238 #ifdef SIGINFO
239 static void ayt();
240 #endif
241 
242 void
243 TerminalNewMode(int f)
244 {
245     static int prevmode = 0;
246     struct termios tmp_tc;
247     int onoff;
248     int old;
249     cc_t esc;
250 
251     globalmode = f&~MODE_FORCE;
252     if (prevmode == f)
253 	return;
254 
255     /*
256      * Write any outstanding data before switching modes
257      * ttyflush() returns 0 only when there is no more data
258      * left to write out, it returns -1 if it couldn't do
259      * anything at all, otherwise it returns 1 + the number
260      * of characters left to write.
261      */
262     old = ttyflush(SYNCHing|flushout);
263     if (old < 0 || old > 1) {
264 	tcgetattr(tin, &tmp_tc);
265 	do {
266 	    /*
267 	     * Wait for data to drain, then flush again.
268 	     */
269 	    if (isatty(tin))
270 	        tcsetattr(tin, TCSADRAIN, &tmp_tc);
271 	    old = ttyflush(SYNCHing|flushout);
272 	} while (old < 0 || old > 1);
273     }
274 
275     old = prevmode;
276     prevmode = f&~MODE_FORCE;
277     tmp_tc = new_tc;
278 
279     if (f&MODE_ECHO) {
280 	tmp_tc.c_lflag |= ECHO;
281 	tmp_tc.c_oflag |= ONLCR;
282 	if (crlf)
283 		tmp_tc.c_iflag |= ICRNL;
284     } else {
285 	tmp_tc.c_lflag &= ~ECHO;
286 	tmp_tc.c_oflag &= ~ONLCR;
287     }
288 
289     if ((f&MODE_FLOW) == 0) {
290 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
291     } else {
292 	if (restartany < 0) {
293 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
294 	} else if (restartany > 0) {
295 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
296 	} else {
297 		tmp_tc.c_iflag |= IXOFF|IXON;
298 		tmp_tc.c_iflag &= ~IXANY;
299 	}
300     }
301 
302     if ((f&MODE_TRAPSIG) == 0) {
303 	tmp_tc.c_lflag &= ~ISIG;
304 	localchars = 0;
305     } else {
306 	tmp_tc.c_lflag |= ISIG;
307 	localchars = 1;
308     }
309 
310     if (f&MODE_EDIT) {
311 	tmp_tc.c_lflag |= ICANON;
312     } else {
313 	tmp_tc.c_lflag &= ~ICANON;
314 	tmp_tc.c_iflag &= ~ICRNL;
315 	tmp_tc.c_cc[VMIN] = 1;
316 	tmp_tc.c_cc[VTIME] = 0;
317     }
318 
319     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
320 	tmp_tc.c_lflag &= ~IEXTEN;
321     }
322 
323     if (f&MODE_SOFT_TAB) {
324 # ifdef	OXTABS
325 	tmp_tc.c_oflag |= OXTABS;
326 # endif
327 # ifdef	TABDLY
328 	tmp_tc.c_oflag &= ~TABDLY;
329 	tmp_tc.c_oflag |= TAB3;
330 # endif
331     } else {
332 # ifdef	OXTABS
333 	tmp_tc.c_oflag &= ~OXTABS;
334 # endif
335 # ifdef	TABDLY
336 	tmp_tc.c_oflag &= ~TABDLY;
337 # endif
338     }
339 
340     if (f&MODE_LIT_ECHO) {
341 # ifdef	ECHOCTL
342 	tmp_tc.c_lflag &= ~ECHOCTL;
343 # endif
344     } else {
345 # ifdef	ECHOCTL
346 	tmp_tc.c_lflag |= ECHOCTL;
347 # endif
348     }
349 
350     if (f == -1) {
351 	onoff = 0;
352     } else {
353 	if (f & MODE_INBIN)
354 		tmp_tc.c_iflag &= ~ISTRIP;
355 	else
356 		tmp_tc.c_iflag |= ISTRIP;
357 	if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
358 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
359 		tmp_tc.c_cflag |= CS8;
360 		if(f & MODE_OUTBIN)
361 			tmp_tc.c_oflag &= ~OPOST;
362 		else
363 			tmp_tc.c_oflag |= OPOST;
364 
365 	} else {
366 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
367 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
368 		tmp_tc.c_oflag |= OPOST;
369 	}
370 	onoff = 1;
371     }
372 
373     if (f != -1) {
374 	(void) signal(SIGTSTP, susp);
375 #ifdef	SIGINFO
376 	(void) signal(SIGINFO, ayt);
377 #endif
378 #if	defined(NOKERNINFO)
379 	tmp_tc.c_lflag |= NOKERNINFO;
380 #endif
381 	/*
382 	 * We don't want to process ^Y here.  It's just another
383 	 * character that we'll pass on to the back end.  It has
384 	 * to process it because it will be processed when the
385 	 * user attempts to read it, not when we send it.
386 	 */
387 # ifdef	VDSUSP
388 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
389 # endif
390 	/*
391 	 * If the VEOL character is already set, then use VEOL2,
392 	 * otherwise use VEOL.
393 	 */
394 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
395 	if ((tmp_tc.c_cc[VEOL] != esc)
396 # ifdef	VEOL2
397 	    && (tmp_tc.c_cc[VEOL2] != esc)
398 # endif
399 	    ) {
400 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
401 		    tmp_tc.c_cc[VEOL] = esc;
402 # ifdef	VEOL2
403 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
404 		    tmp_tc.c_cc[VEOL2] = esc;
405 # endif
406 	}
407     } else {
408 	sigset_t mask;
409 #ifdef	SIGINFO
410 	(void) signal(SIGINFO, ayt_status);
411 #endif
412 	(void) signal(SIGTSTP, SIG_DFL);
413 	sigemptyset(&mask);
414 	sigaddset(&mask, SIGTSTP);
415 	sigprocmask(SIG_UNBLOCK, &mask, NULL);
416 	tmp_tc = old_tc;
417     }
418     if (isatty(tin) && tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
419 	tcsetattr(tin, TCSANOW, &tmp_tc);
420 
421     ioctl(tin, FIONBIO, &onoff);
422     ioctl(tout, FIONBIO, &onoff);
423 }
424 
425 /*
426  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
427  */
428 #if B4800 != 4800
429 #define	DECODE_BAUD
430 #endif
431 
432 #ifdef	DECODE_BAUD
433 #ifndef	B7200
434 #define B7200   B4800
435 #endif
436 
437 #ifndef	B14400
438 #define B14400  B9600
439 #endif
440 
441 #ifndef	B19200
442 # define B19200 B14400
443 #endif
444 
445 #ifndef	B28800
446 #define B28800  B19200
447 #endif
448 
449 #ifndef	B38400
450 # define B38400 B28800
451 #endif
452 
453 #ifndef B57600
454 #define B57600  B38400
455 #endif
456 
457 #ifndef B76800
458 #define B76800  B57600
459 #endif
460 
461 #ifndef B115200
462 #define B115200 B76800
463 #endif
464 
465 #ifndef B230400
466 #define B230400 B115200
467 #endif
468 
469 
470 /*
471  * This code assumes that the values B0, B50, B75...
472  * are in ascending order.  They do not have to be
473  * contiguous.
474  */
475 struct termspeeds {
476 	long speed;
477 	long value;
478 } termspeeds[] = {
479 	{ 0,      B0 },      { 50,    B50 },    { 75,     B75 },
480 	{ 110,    B110 },    { 134,   B134 },   { 150,    B150 },
481 	{ 200,    B200 },    { 300,   B300 },   { 600,    B600 },
482 	{ 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
483 	{ 4800,   B4800 },   { 7200,  B7200 },  { 9600,   B9600 },
484 	{ 14400,  B14400 },  { 19200, B19200 }, { 28800,  B28800 },
485 	{ 38400,  B38400 },  { 57600, B57600 }, { 115200, B115200 },
486 	{ 230400, B230400 }, { -1,    B230400 }
487 };
488 #endif	/* DECODE_BAUD */
489 
490 void
491 TerminalSpeeds(long *ispeed, long *ospeed)
492 {
493 #ifdef	DECODE_BAUD
494     struct termspeeds *tp;
495 #endif	/* DECODE_BAUD */
496     long in, out;
497 
498     out = cfgetospeed(&old_tc);
499     in = cfgetispeed(&old_tc);
500     if (in == 0)
501 	in = out;
502 
503 #ifdef	DECODE_BAUD
504     tp = termspeeds;
505     while ((tp->speed != -1) && (tp->value < in))
506 	tp++;
507     *ispeed = tp->speed;
508 
509     tp = termspeeds;
510     while ((tp->speed != -1) && (tp->value < out))
511 	tp++;
512     *ospeed = tp->speed;
513 #else	/* DECODE_BAUD */
514 	*ispeed = in;
515 	*ospeed = out;
516 #endif	/* DECODE_BAUD */
517 }
518 
519 int
520 TerminalWindowSize(long *rows, long *cols)
521 {
522 #ifdef	TIOCGWINSZ
523     struct winsize ws;
524 
525     if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
526 	*rows = ws.ws_row;
527 	*cols = ws.ws_col;
528 	return 1;
529     }
530 #endif	/* TIOCGWINSZ */
531     return 0;
532 }
533 
534 /*
535  * Various signal handling routines.
536  */
537 
538 void
539 deadpeer(int sig)
540 {
541 	setcommandmode();
542 	longjmp(peerdied, -1);
543 }
544 
545 void
546 intr(int sig)
547 {
548     if (localchars) {
549 	intp();
550 	return;
551     }
552     setcommandmode();
553     longjmp(toplevel, -1);
554 }
555 
556 void
557 intr2(int sig)
558 {
559     if (localchars) {
560 #ifdef	KLUDGELINEMODE
561 	if (kludgelinemode)
562 	    sendbrk();
563 	else
564 #endif
565 	    sendabort();
566 	return;
567     }
568 }
569 
570 void
571 susp(int sig)
572 {
573     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
574 	return;
575     if (localchars)
576 	sendsusp();
577 }
578 
579 #ifdef	SIGWINCH
580 void
581 sendwin(int sig)
582 {
583     if (connected) {
584 	sendnaws();
585     }
586 }
587 #endif
588 
589 #ifdef	SIGINFO
590 void
591 ayt(int sig)
592 {
593     if (connected)
594 	sendayt();
595     else
596 	ayt_status(sig);
597 }
598 #endif
599 
600 
601 void
602 sys_telnet_init(void)
603 {
604     int one = 1;
605 
606     (void) signal(SIGINT, intr);
607     (void) signal(SIGQUIT, intr2);
608     (void) signal(SIGPIPE, deadpeer);
609 #ifdef	SIGWINCH
610     (void) signal(SIGWINCH, sendwin);
611 #endif
612     (void) signal(SIGTSTP, susp);
613 #ifdef	SIGINFO
614     (void) signal(SIGINFO, ayt);
615 #endif
616 
617     setconnmode(0);
618 
619     /*
620      * Mark the socket as non-blocking and receive urgent data inline.
621      * (The latter is required for correct telnet operation when a
622      * second urgent is sent before telnet can process the first.)
623      */
624     ioctl(net, FIONBIO, &one);
625     if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) {
626 	perror("setsockopt");
627     }
628 }
629 
630 /*
631  * Process rings -
632  *
633  *	This routine tries to fill up/empty our various rings.
634  *
635  *	The parameter specifies whether this is a poll operation,
636  *	or a block-until-something-happens operation.
637  *
638  *	The return value is 1 if something happened, 0 if not.
639  */
640 
641 int
642 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
643     int dopoll)		/* If 0, then block until something to do */
644 {
645     int c;
646 		/* One wants to be a bit careful about setting returnValue
647 		 * to one, since a one implies we did some useful work,
648 		 * and therefore probably won't be called to block next
649 		 * time (TN3270 mode only).
650 		 */
651     int returnValue = 0;
652     struct pollfd pfd[TELNET_FD_NUM];
653 
654     if (ttyout) {
655 	pfd[TELNET_FD_TOUT].fd = tout;
656 	pfd[TELNET_FD_TOUT].events = POLLOUT;
657     } else {
658 	pfd[TELNET_FD_TOUT].fd = -1;
659     }
660     if (ttyin) {
661 	pfd[TELNET_FD_TIN].fd = tin;
662 	pfd[TELNET_FD_TIN].events = POLLIN;
663     } else {
664 	pfd[TELNET_FD_TIN].fd = -1;
665     }
666     if (netout || netin || netex) {
667 	pfd[TELNET_FD_NET].fd = net;
668 	pfd[TELNET_FD_NET].events = 0;
669 	if (netout)
670 	    pfd[TELNET_FD_NET].events |= POLLOUT;
671 	if (netin)
672 	    pfd[TELNET_FD_NET].events |= POLLIN;
673 	if (netex)
674 	    pfd[TELNET_FD_NET].events |= POLLRDBAND;
675     } else {
676 	pfd[TELNET_FD_NET].fd = -1;
677     }
678 
679     if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) < 0) {
680 	return 0;
681     }
682 
683     /*
684      * Any urgent data?
685      */
686     if (pfd[TELNET_FD_NET].revents & POLLRDBAND) {
687 	SYNCHing = 1;
688 	(void) ttyflush(1);	/* flush already enqueued data */
689     }
690 
691     /*
692      * Something to read from the network...
693      */
694     if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) {
695 	int canread;
696 
697 	canread = ring_empty_consecutive(&netiring);
698 	c = recv(net, netiring.supply, canread, 0);
699 	if (c < 0 && errno == EWOULDBLOCK) {
700 	    c = 0;
701 	} else if (c <= 0) {
702 	    return -1;
703 	}
704 	if (netdata) {
705 	    Dump('<', netiring.supply, c);
706 	}
707 	if (c)
708 	    ring_supplied(&netiring, c);
709 	returnValue = 1;
710     }
711 
712     /*
713      * Something to read from the tty...
714      */
715     if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) {
716 	c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring));
717 	if (c < 0 && errno == EIO)
718 	    c = 0;
719 	if (c < 0 && errno == EWOULDBLOCK) {
720 	    c = 0;
721 	} else {
722 	    /* EOF detection for line mode!!!! */
723 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
724 		/* must be an EOF... */
725 		*ttyiring.supply = termEofChar;
726 		c = 1;
727 	    }
728 	    if (c <= 0) {
729 		return -1;
730 	    }
731 	    if (termdata) {
732 		Dump('<', ttyiring.supply, c);
733 	    }
734 	    ring_supplied(&ttyiring, c);
735 	}
736 	returnValue = 1;		/* did something useful */
737     }
738 
739     if (pfd[TELNET_FD_NET].revents & POLLOUT) {
740 	returnValue |= netflush();
741     }
742     if (pfd[TELNET_FD_TOUT].revents & POLLOUT) {
743 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
744     }
745 
746     return returnValue;
747 }
748