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