xref: /openbsd/usr.bin/telnet/sys_bsd.c (revision e777735b)
1 /*	$OpenBSD: sys_bsd.c,v 1.36 2023/02/08 08:22:44 tb 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
init_sys(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
TerminalSpecialChars(int c)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
TerminalSaveState(void)121 TerminalSaveState(void)
122 {
123     tcgetattr(0, &old_tc);
124 
125     new_tc = old_tc;
126 }
127 
128 cc_t *
tcval(int func)129 tcval(int func)
130 {
131     switch(func) {
132     case SLC_IP:	return(&termIntChar);
133     case SLC_ABORT:	return(&termQuitChar);
134     case SLC_EOF:	return(&termEofChar);
135     case SLC_EC:	return(&termEraseChar);
136     case SLC_EL:	return(&termKillChar);
137     case SLC_XON:	return(&termStartChar);
138     case SLC_XOFF:	return(&termStopChar);
139     case SLC_FORW1:	return(&termForw1Char);
140     case SLC_FORW2:	return(&termForw2Char);
141     case SLC_SUSP:	return(&termSuspChar);
142     case SLC_AO:	return(&termFlushChar);
143     case SLC_EW:	return(&termWerasChar);
144     case SLC_RP:	return(&termRprntChar);
145     case SLC_LNEXT:	return(&termLiteralNextChar);
146     case SLC_AYT:	return(&termAytChar);
147     case SLC_SYNCH:
148     case SLC_BRK:
149     case SLC_EOR:
150     default:
151 	return(NULL);
152     }
153 }
154 
155 void
TerminalDefaultChars(void)156 TerminalDefaultChars(void)
157 {
158     memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
159 }
160 
161 /*
162  * TerminalNewMode - set up terminal to a specific mode.
163  *	MODE_ECHO: do local terminal echo
164  *	MODE_FLOW: do local flow control
165  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
166  *	MODE_EDIT: do local line editing
167  *
168  *	Command mode:
169  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
170  *		local echo
171  *		local editing
172  *		local xon/xoff
173  *		local signal mapping
174  *
175  *	Linemode:
176  *		local/no editing
177  *	Both Linemode and Single Character mode:
178  *		local/remote echo
179  *		local/no xon/xoff
180  *		local/no signal mapping
181  */
182 
183 static void susp(int);
184 static void ayt(int);
185 
186 void
TerminalNewMode(int f)187 TerminalNewMode(int f)
188 {
189     static int prevmode = 0;
190     struct termios tmp_tc;
191     int onoff;
192     int old;
193     cc_t esc;
194 
195     globalmode = f&~MODE_FORCE;
196     if (prevmode == f)
197 	return;
198 
199     /*
200      * Write any outstanding data before switching modes
201      * ttyflush() returns 0 only when there is no more data
202      * left to write out, it returns -1 if it couldn't do
203      * anything at all, otherwise it returns 1 + the number
204      * of characters left to write.
205      */
206     old = ttyflush(SYNCHing|flushout);
207     if (old < 0 || old > 1) {
208 	tcgetattr(tin, &tmp_tc);
209 	do {
210 	    /*
211 	     * Wait for data to drain, then flush again.
212 	     */
213 	    if (isatty(tin))
214 	        tcsetattr(tin, TCSADRAIN, &tmp_tc);
215 	    old = ttyflush(SYNCHing|flushout);
216 	} while (old < 0 || old > 1);
217     }
218 
219     old = prevmode;
220     prevmode = f&~MODE_FORCE;
221     tmp_tc = new_tc;
222 
223     if (f&MODE_ECHO) {
224 	tmp_tc.c_lflag |= ECHO;
225 	tmp_tc.c_oflag |= ONLCR;
226 	if (crlf)
227 		tmp_tc.c_iflag |= ICRNL;
228     } else {
229 	tmp_tc.c_lflag &= ~ECHO;
230 	tmp_tc.c_oflag &= ~ONLCR;
231     }
232 
233     if ((f&MODE_FLOW) == 0) {
234 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
235     } else {
236 	if (restartany < 0) {
237 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
238 	} else if (restartany > 0) {
239 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
240 	} else {
241 		tmp_tc.c_iflag |= IXOFF|IXON;
242 		tmp_tc.c_iflag &= ~IXANY;
243 	}
244     }
245 
246     if ((f&MODE_TRAPSIG) == 0) {
247 	tmp_tc.c_lflag &= ~ISIG;
248 	localchars = 0;
249     } else {
250 	tmp_tc.c_lflag |= ISIG;
251 	localchars = 1;
252     }
253 
254     if (f&MODE_EDIT) {
255 	tmp_tc.c_lflag |= ICANON;
256     } else {
257 	tmp_tc.c_lflag &= ~ICANON;
258 	tmp_tc.c_iflag &= ~ICRNL;
259 	tmp_tc.c_cc[VMIN] = 1;
260 	tmp_tc.c_cc[VTIME] = 0;
261     }
262 
263     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
264 	tmp_tc.c_lflag &= ~IEXTEN;
265     }
266 
267     if (f&MODE_SOFT_TAB) {
268 # ifdef	OXTABS
269 	tmp_tc.c_oflag |= OXTABS;
270 # endif
271 # ifdef	TABDLY
272 	tmp_tc.c_oflag &= ~TABDLY;
273 	tmp_tc.c_oflag |= TAB3;
274 # endif
275     } else {
276 # ifdef	OXTABS
277 	tmp_tc.c_oflag &= ~OXTABS;
278 # endif
279 # ifdef	TABDLY
280 	tmp_tc.c_oflag &= ~TABDLY;
281 # endif
282     }
283 
284     if (f&MODE_LIT_ECHO) {
285 # ifdef	ECHOCTL
286 	tmp_tc.c_lflag &= ~ECHOCTL;
287 # endif
288     } else {
289 # ifdef	ECHOCTL
290 	tmp_tc.c_lflag |= ECHOCTL;
291 # endif
292     }
293 
294     if (f == -1) {
295 	onoff = 0;
296     } else {
297 	if (f & MODE_INBIN)
298 		tmp_tc.c_iflag &= ~ISTRIP;
299 	else
300 		tmp_tc.c_iflag |= ISTRIP;
301 	if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
302 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
303 		tmp_tc.c_cflag |= CS8;
304 		if(f & MODE_OUTBIN)
305 			tmp_tc.c_oflag &= ~OPOST;
306 		else
307 			tmp_tc.c_oflag |= OPOST;
308 
309 	} else {
310 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
311 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
312 		tmp_tc.c_oflag |= OPOST;
313 	}
314 	onoff = 1;
315     }
316 
317     if (f != -1) {
318 	(void) signal(SIGTSTP, susp);
319 	(void) signal(SIGINFO, ayt);
320 #if	defined(NOKERNINFO)
321 	tmp_tc.c_lflag |= NOKERNINFO;
322 #endif
323 	/*
324 	 * We don't want to process ^Y here.  It's just another
325 	 * character that we'll pass on to the back end.  It has
326 	 * to process it because it will be processed when the
327 	 * user attempts to read it, not when we send it.
328 	 */
329 # ifdef	VDSUSP
330 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
331 # endif
332 	/*
333 	 * If the VEOL character is already set, then use VEOL2,
334 	 * otherwise use VEOL.
335 	 */
336 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
337 	if ((tmp_tc.c_cc[VEOL] != esc)
338 # ifdef	VEOL2
339 	    && (tmp_tc.c_cc[VEOL2] != esc)
340 # endif
341 	    ) {
342 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
343 		    tmp_tc.c_cc[VEOL] = esc;
344 # ifdef	VEOL2
345 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
346 		    tmp_tc.c_cc[VEOL2] = esc;
347 # endif
348 	}
349     } else {
350 	sigset_t mask;
351 	(void) signal(SIGINFO, ayt_status);
352 	(void) signal(SIGTSTP, SIG_DFL);
353 	sigemptyset(&mask);
354 	sigaddset(&mask, SIGTSTP);
355 	sigprocmask(SIG_UNBLOCK, &mask, NULL);
356 	tmp_tc = old_tc;
357     }
358     if (isatty(tin) && tcsetattr(tin, TCSADRAIN, &tmp_tc) == -1)
359 	tcsetattr(tin, TCSANOW, &tmp_tc);
360 
361     ioctl(tin, FIONBIO, &onoff);
362     ioctl(tout, FIONBIO, &onoff);
363 }
364 
365 void
TerminalSpeeds(long * ispeed,long * ospeed)366 TerminalSpeeds(long *ispeed, long *ospeed)
367 {
368     long in, out;
369 
370     out = cfgetospeed(&old_tc);
371     in = cfgetispeed(&old_tc);
372     if (in == 0)
373 	in = out;
374 
375     *ispeed = in;
376     *ospeed = out;
377 }
378 
379 int
TerminalWindowSize(long * rows,long * cols)380 TerminalWindowSize(long *rows, long *cols)
381 {
382     struct winsize ws;
383 
384     if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) == 0) {
385 	*rows = ws.ws_row;
386 	*cols = ws.ws_col;
387 	return 1;
388     }
389     return 0;
390 }
391 
392 /*
393  * Various signal handling routines.
394  */
395 
396 void
deadpeer(int sig)397 deadpeer(int sig)
398 {
399 	setcommandmode();
400 	longjmp(peerdied, -1);
401 }
402 
403 void
intr(int sig)404 intr(int sig)
405 {
406     if (localchars) {
407 	intp();
408 	return;
409     }
410     setcommandmode();
411     longjmp(toplevel, -1);
412 }
413 
414 void
intr2(int sig)415 intr2(int sig)
416 {
417     if (localchars) {
418 #ifdef	KLUDGELINEMODE
419 	if (kludgelinemode)
420 	    sendbrk();
421 	else
422 #endif
423 	    sendabort();
424 	return;
425     }
426 }
427 
428 void
susp(int sig)429 susp(int sig)
430 {
431     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
432 	return;
433     if (localchars)
434 	sendsusp();
435 }
436 
437 void
sendwin(int sig)438 sendwin(int sig)
439 {
440     if (connected) {
441 	sendnaws();
442     }
443 }
444 
445 void
ayt(int sig)446 ayt(int sig)
447 {
448     if (connected)
449 	sendayt();
450     else
451 	ayt_status(sig);
452 }
453 
454 
455 void
sys_telnet_init(void)456 sys_telnet_init(void)
457 {
458     int one = 1;
459 
460     (void) signal(SIGINT, intr);
461     (void) signal(SIGQUIT, intr2);
462     (void) signal(SIGPIPE, deadpeer);
463     (void) signal(SIGWINCH, sendwin);
464     (void) signal(SIGTSTP, susp);
465     (void) signal(SIGINFO, ayt);
466 
467     setconnmode(0);
468 
469     /*
470      * Mark the socket as non-blocking and receive urgent data inline.
471      * (The latter is required for correct telnet operation when a
472      * second urgent is sent before telnet can process the first.)
473      */
474     ioctl(net, FIONBIO, &one);
475     if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) {
476 	perror("setsockopt");
477     }
478 }
479 
480 /*
481  * Process rings -
482  *
483  *	This routine tries to fill up/empty our various rings.
484  *
485  *	The parameter specifies whether this is a poll operation,
486  *	or a block-until-something-happens operation.
487  *
488  *	The return value is 1 if something happened, 0 if not.
489  */
490 
491 int
process_rings(int netin,int netout,int netex,int ttyin,int ttyout,int dopoll)492 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
493     int dopoll)		/* If 0, then block until something to do */
494 {
495     int c;
496 		/* One wants to be a bit careful about setting returnValue
497 		 * to one, since a one implies we did some useful work,
498 		 * and therefore probably won't be called to block next
499 		 * time (TN3270 mode only).
500 		 */
501     int returnValue = 0;
502     struct pollfd pfd[TELNET_FD_NUM];
503 
504     if (ttyout) {
505 	pfd[TELNET_FD_TOUT].fd = tout;
506 	pfd[TELNET_FD_TOUT].events = POLLOUT;
507     } else {
508 	pfd[TELNET_FD_TOUT].fd = -1;
509     }
510     if (ttyin) {
511 	pfd[TELNET_FD_TIN].fd = tin;
512 	pfd[TELNET_FD_TIN].events = POLLIN;
513     } else {
514 	pfd[TELNET_FD_TIN].fd = -1;
515     }
516     if (netout || netin || netex) {
517 	pfd[TELNET_FD_NET].fd = net;
518 	pfd[TELNET_FD_NET].events = 0;
519 	if (netout)
520 	    pfd[TELNET_FD_NET].events |= POLLOUT;
521 	if (netin)
522 	    pfd[TELNET_FD_NET].events |= POLLIN;
523 	if (netex)
524 	    pfd[TELNET_FD_NET].events |= POLLRDBAND;
525     } else {
526 	pfd[TELNET_FD_NET].fd = -1;
527     }
528 
529     if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) == -1) {
530 	return 0;
531     }
532 
533     /*
534      * Any urgent data?
535      */
536     if (pfd[TELNET_FD_NET].revents & POLLRDBAND) {
537 	SYNCHing = 1;
538 	(void) ttyflush(1);	/* flush already enqueued data */
539     }
540 
541     /*
542      * Something to read from the network...
543      */
544     if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) {
545 	int canread;
546 
547 	canread = ring_empty_consecutive(&netiring);
548 	c = recv(net, netiring.supply, canread, 0);
549 	if (c == -1 && errno == EWOULDBLOCK) {
550 	    c = 0;
551 	} else if (c <= 0) {
552 	    return -1;
553 	}
554 	if (netdata) {
555 	    Dump('<', netiring.supply, c);
556 	}
557 	if (c)
558 	    ring_supplied(&netiring, c);
559 	returnValue = 1;
560     }
561 
562     /*
563      * Something to read from the tty...
564      */
565     if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) {
566 	c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring));
567 	if (c == -1 && errno == EIO)
568 	    c = 0;
569 	if (c == -1 && errno == EWOULDBLOCK) {
570 	    c = 0;
571 	} else {
572 	    /* EOF detection for line mode!!!! */
573 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
574 		/* must be an EOF... */
575 		*ttyiring.supply = termEofChar;
576 		c = 1;
577 	    }
578 	    if (c <= 0) {
579 		return -1;
580 	    }
581 	    if (termdata) {
582 		Dump('<', ttyiring.supply, c);
583 	    }
584 	    ring_supplied(&ttyiring, c);
585 	}
586 	returnValue = 1;		/* did something useful */
587     }
588 
589     if (pfd[TELNET_FD_NET].revents & POLLOUT) {
590 	returnValue |= netflush();
591     }
592     if (pfd[TELNET_FD_TOUT].revents & POLLOUT) {
593 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
594     }
595 
596     return returnValue;
597 }
598