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