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
48int
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
58struct	termios old_tc = { 0 };
59
60void
61init_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
82int
83TerminalSpecialChars(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
120void
121TerminalSaveState(void)
122{
123    tcgetattr(0, &old_tc);
124
125    new_tc = old_tc;
126}
127
128cc_t *
129tcval(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
155void
156TerminalDefaultChars(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
183static void susp(int);
184static void ayt(int);
185
186void
187TerminalNewMode(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
365void
366TerminalSpeeds(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
379int
380TerminalWindowSize(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
396void
397deadpeer(int sig)
398{
399	setcommandmode();
400	longjmp(peerdied, -1);
401}
402
403void
404intr(int sig)
405{
406    if (localchars) {
407	intp();
408	return;
409    }
410    setcommandmode();
411    longjmp(toplevel, -1);
412}
413
414void
415intr2(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
428void
429susp(int sig)
430{
431    if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
432	return;
433    if (localchars)
434	sendsusp();
435}
436
437void
438sendwin(int sig)
439{
440    if (connected) {
441	sendnaws();
442    }
443}
444
445void
446ayt(int sig)
447{
448    if (connected)
449	sendayt();
450    else
451	ayt_status(sig);
452}
453
454
455void
456sys_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
491int
492process_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