Annotation of src/usr.bin/telnet/sys_bsd.c, Revision 1.35
1.35 ! deraadt 1: /* $OpenBSD: sys_bsd.c,v 1.34 2017/07/19 12:25:52 deraadt Exp $ */
1.3 niklas 2: /* $NetBSD: sys_bsd.c,v 1.11 1996/02/28 21:04:10 thorpej Exp $ */
3:
1.1 deraadt 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.
1.14 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 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:
1.5 art 33: #include "telnet_locl.h"
1.18 guenther 34:
1.17 guenther 35: #include <sys/ioctl.h>
1.20 guenther 36: #include <sys/socket.h>
37: #include <arpa/telnet.h>
38: #include <errno.h>
1.17 guenther 39: #include <poll.h>
1.20 guenther 40: #include <string.h>
1.18 guenther 41: #include <unistd.h>
1.1 deraadt 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:
1.15 millert 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:
1.5 art 58: struct termios old_tc = { 0 };
1.1 deraadt 59:
1.27 jsg 60: void
61: init_sys(void)
1.1 deraadt 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:
1.27 jsg 82: int
83: TerminalSpecialChars(int c)
1.1 deraadt 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:
1.27 jsg 120: void
121: TerminalSaveState(void)
1.1 deraadt 122: {
123: tcgetattr(0, &old_tc);
124:
125: new_tc = old_tc;
126: }
127:
1.27 jsg 128: cc_t *
129: tcval(int func)
1.1 deraadt 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);
1.22 guenther 141: case SLC_SUSP: return(&termSuspChar);
1.1 deraadt 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:
1.32 krw 151: return(NULL);
1.1 deraadt 152: }
153: }
154:
1.27 jsg 155: void
156: TerminalDefaultChars(void)
1.1 deraadt 157: {
1.23 guenther 158: memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
1.1 deraadt 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:
1.5 art 183: static void susp();
184: static void ayt();
1.1 deraadt 185:
1.27 jsg 186: void
187: TerminalNewMode(int f)
1.1 deraadt 188: {
189: static int prevmode = 0;
1.5 art 190: struct termios tmp_tc;
1.1 deraadt 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: */
1.31 semarie 213: if (isatty(tin))
214: tcsetattr(tin, TCSADRAIN, &tmp_tc);
1.1 deraadt 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;
1.30 sthen 258: tmp_tc.c_iflag &= ~ICRNL;
1.1 deraadt 259: tmp_tc.c_cc[VMIN] = 1;
260: tmp_tc.c_cc[VTIME] = 0;
261: }
262:
263: if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
1.2 niklas 264: tmp_tc.c_lflag &= ~IEXTEN;
1.1 deraadt 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;
1.5 art 301: if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
1.1 deraadt 302: tmp_tc.c_cflag &= ~(CSIZE|PARENB);
303: tmp_tc.c_cflag |= CS8;
1.5 art 304: if(f & MODE_OUTBIN)
305: tmp_tc.c_oflag &= ~OPOST;
306: else
307: tmp_tc.c_oflag |= OPOST;
308:
1.1 deraadt 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);
1.16 guenther 320: #if defined(NOKERNINFO)
1.1 deraadt 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 {
1.9 millert 350: sigset_t mask;
1.26 guenther 351: (void) signal(SIGINFO, ayt_status);
1.1 deraadt 352: (void) signal(SIGTSTP, SIG_DFL);
1.9 millert 353: sigemptyset(&mask);
354: sigaddset(&mask, SIGTSTP);
355: sigprocmask(SIG_UNBLOCK, &mask, NULL);
1.1 deraadt 356: tmp_tc = old_tc;
357: }
1.35 ! deraadt 358: if (isatty(tin) && tcsetattr(tin, TCSADRAIN, &tmp_tc) == -1)
1.1 deraadt 359: tcsetattr(tin, TCSANOW, &tmp_tc);
360:
1.28 guenther 361: ioctl(tin, FIONBIO, &onoff);
362: ioctl(tout, FIONBIO, &onoff);
1.1 deraadt 363: }
364:
1.27 jsg 365: void
366: TerminalSpeeds(long *ispeed, long *ospeed)
1.1 deraadt 367: {
1.10 mpech 368: long in, out;
1.1 deraadt 369:
370: out = cfgetospeed(&old_tc);
371: in = cfgetispeed(&old_tc);
372: if (in == 0)
373: in = out;
374:
1.34 deraadt 375: *ispeed = in;
376: *ospeed = out;
1.1 deraadt 377: }
378:
1.27 jsg 379: int
380: TerminalWindowSize(long *rows, long *cols)
1.1 deraadt 381: {
382: struct winsize ws;
383:
1.35 ! deraadt 384: if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) == 0) {
1.1 deraadt 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:
1.27 jsg 396: void
397: deadpeer(int sig)
1.1 deraadt 398: {
399: setcommandmode();
400: longjmp(peerdied, -1);
401: }
402:
1.27 jsg 403: void
404: intr(int sig)
1.1 deraadt 405: {
406: if (localchars) {
407: intp();
408: return;
409: }
410: setcommandmode();
411: longjmp(toplevel, -1);
412: }
413:
1.27 jsg 414: void
415: intr2(int sig)
1.1 deraadt 416: {
417: if (localchars) {
418: #ifdef KLUDGELINEMODE
419: if (kludgelinemode)
420: sendbrk();
421: else
422: #endif
423: sendabort();
424: return;
425: }
426: }
427:
1.27 jsg 428: void
429: susp(int sig)
1.1 deraadt 430: {
431: if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
432: return;
433: if (localchars)
434: sendsusp();
435: }
436:
1.27 jsg 437: void
438: sendwin(int sig)
1.1 deraadt 439: {
440: if (connected) {
441: sendnaws();
442: }
443: }
444:
1.27 jsg 445: void
446: ayt(int sig)
1.1 deraadt 447: {
448: if (connected)
449: sendayt();
450: else
1.26 guenther 451: ayt_status(sig);
1.1 deraadt 452: }
453:
454:
1.27 jsg 455: void
456: sys_telnet_init(void)
1.1 deraadt 457: {
1.16 guenther 458: int one = 1;
459:
1.1 deraadt 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:
1.25 guenther 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);
1.16 guenther 475: if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) {
476: perror("setsockopt");
1.1 deraadt 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:
1.27 jsg 491: int
492: process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
493: int dopoll) /* If 0, then block until something to do */
1.1 deraadt 494: {
1.10 mpech 495: int c;
1.1 deraadt 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;
1.15 millert 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: }
1.1 deraadt 528:
1.35 ! deraadt 529: if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) == -1) {
1.1 deraadt 530: return 0;
531: }
532:
533: /*
534: * Any urgent data?
535: */
1.15 millert 536: if (pfd[TELNET_FD_NET].revents & POLLRDBAND) {
1.1 deraadt 537: SYNCHing = 1;
538: (void) ttyflush(1); /* flush already enqueued data */
539: }
540:
541: /*
542: * Something to read from the network...
543: */
1.15 millert 544: if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) {
1.1 deraadt 545: int canread;
546:
547: canread = ring_empty_consecutive(&netiring);
1.28 guenther 548: c = recv(net, netiring.supply, canread, 0);
1.35 ! deraadt 549: if (c == -1 && errno == EWOULDBLOCK) {
1.1 deraadt 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: */
1.15 millert 565: if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) {
1.19 guenther 566: c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring));
1.35 ! deraadt 567: if (c == -1 && errno == EIO)
1.3 niklas 568: c = 0;
1.35 ! deraadt 569: if (c == -1 && errno == EWOULDBLOCK) {
1.1 deraadt 570: c = 0;
571: } else {
1.5 art 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) {
1.1 deraadt 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:
1.15 millert 589: if (pfd[TELNET_FD_NET].revents & POLLOUT) {
1.1 deraadt 590: returnValue |= netflush();
591: }
1.15 millert 592: if (pfd[TELNET_FD_TOUT].revents & POLLOUT) {
1.1 deraadt 593: returnValue |= (ttyflush(SYNCHing|flushout) > 0);
594: }
595:
596: return returnValue;
597: }