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