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