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
35from: 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
63void susp(int);
64void ayt(int);
65
66void intr(int);
67void intr2(int);
68void sendwin(int);
69
70
71int
72	tout,			/* Output file descriptor */
73	tin,			/* Input file descriptor */
74	net;
75
76struct	termios old_tc = { .c_iflag = 0 };
77extern struct termios new_tc;
78
79void
80init_sys(void)
81{
82    tout = fileno(stdout);
83    tin = fileno(stdin);
84
85    errno = 0;
86}
87
88
89int
90TerminalWrite(char *buf, int  n)
91{
92    return write(tout, buf, n);
93}
94
95int
96TerminalRead(unsigned char *buf, int  n)
97{
98    return read(tin, buf, n);
99}
100
101/*
102 *
103 */
104
105int
106TerminalAutoFlush(void)
107{
108    return 1;
109}
110
111#ifdef	KLUDGELINEMODE
112extern 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
126int
127TerminalSpecialChars(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
169void
170TerminalFlushOutput(void)
171{
172    int com = 0;
173    (void) ioctl(fileno(stdout), TIOCFLUSH, &com);
174}
175
176void
177TerminalSaveState(void)
178{
179    tcgetattr(0, &old_tc);
180
181    new_tc = old_tc;
182}
183
184cc_t *
185tcval(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
212void
213TerminalDefaultChars(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
241void
242TerminalNewMode(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
424void
425TerminalSpeeds(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
439int
440TerminalWindowSize(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
452int
453NetClose(int fd)
454{
455    return close(fd);
456}
457
458/*
459 * Various signal handling routines.
460 */
461
462/* ARGSUSED */
463void
464intr(int sig)
465{
466    if (localchars) {
467	intp();
468	return;
469    }
470    setcommandmode();
471    longjmp(toplevel, -1);
472}
473
474/* ARGSUSED */
475void
476intr2(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 */
490void
491susp(int sig)
492{
493    if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
494	return;
495    if (localchars)
496	sendsusp();
497}
498
499/* ARGSUSED */
500void
501sendwin(int sig)
502{
503    if (connected) {
504	sendnaws();
505    }
506}
507
508/* ARGSUSED */
509void
510ayt(int sig)
511{
512    if (connected)
513	sendayt();
514    else
515	ayt_status();
516}
517
518
519void
520sys_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
551int
552process_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