2 * Copyright (c) 1988, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "telnet_locl.h"
36 RCSID("$Id: sys_bsd.c,v 1.23.18.2 2000/10/19 21:21:21 assar Exp $");
39 * The following routines try to encapsulate what is system dependent
40 * (at least between 4.x and dos) which is used in telnet.c.
44 tout, /* Output file descriptor */
45 tin, /* Input file descriptor */
48 struct termios old_tc = { 0 };
49 extern struct termios new_tc;
53 # define TCSANOW TCSETS
54 # define TCSADRAIN TCSETSW
55 # define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
58 # define TCSANOW TCSETA
59 # define TCSADRAIN TCSETAW
60 # define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
62 # define TCSANOW TIOCSETA
63 # define TCSADRAIN TIOCSETAW
64 # define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
67 # define tcsetattr(f, a, t) ioctl(f, a, (char *)t)
68 # define cfgetospeed(ptr) ((ptr)->c_cflag&CBAUD)
70 # define cfgetispeed(ptr) (((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
72 # define cfgetispeed(ptr) cfgetospeed(ptr)
76 static fd_set ibits, obits, xbits;
82 tout = fileno(stdout);
93 TerminalWrite(char *buf, int n)
95 return write(tout, buf, n);
99 TerminalRead(unsigned char *buf, int n)
101 return read(tin, buf, n);
109 TerminalAutoFlush(void)
114 ioctl(0, TIOCLGET, (char *)&flush);
115 return !(flush&LNOFLSH); /* if LNOFLSH, no autoflush */
121 #ifdef KLUDGELINEMODE
122 extern int kludgelinemode;
125 * TerminalSpecialChars()
127 * Look at an input character to see if it is a special character
128 * and decide what to do.
132 * 0 Don't add this character.
133 * 1 Do add this character
137 TerminalSpecialChars(int c)
139 if (c == termIntChar) {
142 } else if (c == termQuitChar) {
143 #ifdef KLUDGELINEMODE
150 } else if (c == termEofChar) {
151 if (my_want_state_is_will(TELOPT_LINEMODE)) {
156 } else if (c == termSuspChar) {
159 } else if (c == termFlushChar) {
160 xmitAO(); /* Transmit Abort Output */
162 } else if (!MODE_LOCAL_CHARS(globalmode)) {
163 if (c == termKillChar) {
166 } else if (c == termEraseChar) {
167 xmitEC(); /* Transmit Erase Character */
176 * Flush output to the terminal
180 TerminalFlushOutput(void)
183 ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
185 ioctl(fileno(stdout), TCFLSH, (char *) 0);
190 TerminalSaveState(void)
192 tcgetattr(0, &old_tc);
197 termFlushChar = CONTROL('O');
200 termWerasChar = CONTROL('W');
203 termRprntChar = CONTROL('R');
206 termLiteralNextChar = CONTROL('V');
209 termStartChar = CONTROL('Q');
212 termStopChar = CONTROL('S');
215 termAytChar = CONTROL('T');
223 case SLC_IP: return(&termIntChar);
224 case SLC_ABORT: return(&termQuitChar);
225 case SLC_EOF: return(&termEofChar);
226 case SLC_EC: return(&termEraseChar);
227 case SLC_EL: return(&termKillChar);
228 case SLC_XON: return(&termStartChar);
229 case SLC_XOFF: return(&termStopChar);
230 case SLC_FORW1: return(&termForw1Char);
231 case SLC_FORW2: return(&termForw2Char);
233 case SLC_AO: return(&termFlushChar);
236 case SLC_SUSP: return(&termSuspChar);
239 case SLC_EW: return(&termWerasChar);
242 case SLC_RP: return(&termRprntChar);
245 case SLC_LNEXT: return(&termLiteralNextChar);
248 case SLC_AYT: return(&termAytChar);
260 TerminalDefaultChars(void)
262 memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
264 termFlushChar = CONTROL('O');
267 termWerasChar = CONTROL('W');
270 termRprntChar = CONTROL('R');
273 termLiteralNextChar = CONTROL('V');
276 termStartChar = CONTROL('Q');
279 termStopChar = CONTROL('S');
282 termAytChar = CONTROL('T');
288 TerminalRestoreState()
294 * TerminalNewMode - set up terminal to a specific mode.
295 * MODE_ECHO: do local terminal echo
296 * MODE_FLOW: do local flow control
297 * MODE_TRAPSIG: do local mapping to TELNET IAC sequences
298 * MODE_EDIT: do local line editing
301 * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
305 * local signal mapping
309 * Both Linemode and Single Character mode:
312 * local/no signal mapping
317 static RETSIGTYPE susp();
320 static RETSIGTYPE ayt();
324 TerminalNewMode(int f)
326 static int prevmode = 0;
327 struct termios tmp_tc;
332 globalmode = f&~MODE_FORCE;
337 * Write any outstanding data before switching modes
338 * ttyflush() returns 0 only when there is no more data
339 * left to write out, it returns -1 if it couldn't do
340 * anything at all, otherwise it returns 1 + the number
341 * of characters left to write.
343 old = ttyflush(SYNCHing|flushout);
344 if (old < 0 || old > 1) {
345 tcgetattr(tin, &tmp_tc);
348 * Wait for data to drain, then flush again.
350 tcsetattr(tin, TCSADRAIN, &tmp_tc);
351 old = ttyflush(SYNCHing|flushout);
352 } while (old < 0 || old > 1);
356 prevmode = f&~MODE_FORCE;
360 tmp_tc.c_lflag |= ECHO;
361 tmp_tc.c_oflag |= ONLCR;
363 tmp_tc.c_iflag |= ICRNL;
365 tmp_tc.c_lflag &= ~ECHO;
366 tmp_tc.c_oflag &= ~ONLCR;
369 tmp_tc.c_iflag &= ~ICRNL;
373 if ((f&MODE_FLOW) == 0) {
374 tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */
376 if (restartany < 0) {
377 tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */
378 } else if (restartany > 0) {
379 tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
381 tmp_tc.c_iflag |= IXOFF|IXON;
382 tmp_tc.c_iflag &= ~IXANY;
386 if ((f&MODE_TRAPSIG) == 0) {
387 tmp_tc.c_lflag &= ~ISIG;
390 tmp_tc.c_lflag |= ISIG;
395 tmp_tc.c_lflag |= ICANON;
397 tmp_tc.c_lflag &= ~ICANON;
398 tmp_tc.c_iflag &= ~ICRNL;
399 tmp_tc.c_cc[VMIN] = 1;
400 tmp_tc.c_cc[VTIME] = 0;
403 if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
405 tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
409 if (f&MODE_SOFT_TAB) {
411 tmp_tc.c_oflag |= OXTABS;
414 tmp_tc.c_oflag &= ~TABDLY;
415 tmp_tc.c_oflag |= TAB3;
419 tmp_tc.c_oflag &= ~OXTABS;
422 tmp_tc.c_oflag &= ~TABDLY;
426 if (f&MODE_LIT_ECHO) {
428 tmp_tc.c_lflag &= ~ECHOCTL;
432 tmp_tc.c_lflag |= ECHOCTL;
440 tmp_tc.c_iflag &= ~ISTRIP;
442 tmp_tc.c_iflag |= ISTRIP;
443 if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
444 tmp_tc.c_cflag &= ~(CSIZE|PARENB);
445 tmp_tc.c_cflag |= CS8;
447 tmp_tc.c_oflag &= ~OPOST;
449 tmp_tc.c_oflag |= OPOST;
451 tmp_tc.c_cflag &= ~(CSIZE|PARENB);
452 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
453 tmp_tc.c_oflag |= OPOST;
461 signal(SIGTSTP, susp);
464 signal(SIGINFO, ayt);
467 tmp_tc.c_lflag |= NOKERNINFO;
470 * We don't want to process ^Y here. It's just another
471 * character that we'll pass on to the back end. It has
472 * to process it because it will be processed when the
473 * user attempts to read it, not when we send it.
476 tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
479 * If the VEOL character is already set, then use VEOL2,
480 * otherwise use VEOL.
482 esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
483 if ((tmp_tc.c_cc[VEOL] != esc)
485 && (tmp_tc.c_cc[VEOL2] != esc)
488 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
489 tmp_tc.c_cc[VEOL] = esc;
491 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
492 tmp_tc.c_cc[VEOL2] = esc;
498 RETSIGTYPE ayt_status();
500 signal(SIGINFO, ayt_status);
503 signal(SIGTSTP, SIG_DFL);
505 sigaddset(&sm, SIGTSTP);
506 sigprocmask(SIG_UNBLOCK, &sm, NULL);
510 if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
511 tcsetattr(tin, TCSANOW, &tmp_tc);
513 ioctl(tin, FIONBIO, (char *)&onoff);
514 ioctl(tout, FIONBIO, (char *)&onoff);
519 * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
535 # define B19200 B14400
539 #define B28800 B19200
543 # define B38400 B28800
547 #define B57600 B38400
551 #define B76800 B57600
555 #define B115200 B76800
559 #define B230400 B115200
564 * This code assumes that the values B0, B50, B75...
565 * are in ascending order. They do not have to be
572 { 0, B0 }, { 50, B50 }, { 75, B75 },
573 { 110, B110 }, { 134, B134 }, { 150, B150 },
574 { 200, B200 }, { 300, B300 }, { 600, B600 },
575 { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 },
576 { 4800, B4800 }, { 7200, B7200 }, { 9600, B9600 },
577 { 14400, B14400 }, { 19200, B19200 }, { 28800, B28800 },
578 { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 },
579 { 230400, B230400 }, { -1, B230400 }
581 #endif /* DECODE_BAUD */
584 TerminalSpeeds(long *input_speed, long *output_speed)
587 struct termspeeds *tp;
588 #endif /* DECODE_BAUD */
591 out = cfgetospeed(&old_tc);
592 in = cfgetispeed(&old_tc);
598 while ((tp->speed != -1) && (tp->value < in))
600 *input_speed = tp->speed;
603 while ((tp->speed != -1) && (tp->value < out))
605 *output_speed = tp->speed;
606 #else /* DECODE_BAUD */
609 #endif /* DECODE_BAUD */
613 TerminalWindowSize(long *rows, long *cols)
617 if (get_window_size (STDIN_FILENO, &ws) == 0) {
633 NetNonblockingIO(int fd, int onoff)
635 ioctl(fd, FIONBIO, (char *)&onoff);
640 * Various signal handling routines.
643 static RETSIGTYPE deadpeer(int),
644 intr(int), intr2(int), susp(int), sendwin(int);
646 static RETSIGTYPE ayt(int);
655 longjmp(peerdied, -1);
667 longjmp(toplevel, -1);
675 #ifdef KLUDGELINEMODE
690 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
722 sys_telnet_init(void)
724 signal(SIGINT, intr);
725 signal(SIGQUIT, intr2);
726 signal(SIGPIPE, deadpeer);
728 signal(SIGWINCH, sendwin);
731 signal(SIGTSTP, susp);
734 signal(SIGINFO, ayt);
739 NetNonblockingIO(net, 1);
742 #if defined(SO_OOBINLINE)
743 if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
744 perror("SetSockOpt");
746 #endif /* defined(SO_OOBINLINE) */
752 * This routine tries to fill up/empty our various rings.
754 * The parameter specifies whether this is a poll operation,
755 * or a block-until-something-happens operation.
757 * The return value is 1 if something happened, 0 if not.
761 process_rings(int netin,
766 int poll) /* If 0, then block until something to do */
769 /* One wants to be a bit careful about setting returnValue
770 * to one, since a one implies we did some useful work,
771 * and therefore probably won't be called to block next
772 * time (TN3270 mode only).
775 static struct timeval TimeValue = { 0 };
777 if (net >= FD_SETSIZE
778 || tout >= FD_SETSIZE
779 || tin >= FD_SETSIZE)
780 errx (1, "fd too large");
786 FD_SET(tout, &obits);
794 #if !defined(SO_OOBINLINE)
799 if ((c = select(FD_SETSIZE, &ibits, &obits, &xbits,
800 (poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
803 * we can get EINTR if we are in line mode,
804 * and the user does an escape (TSTP), or
805 * some other signal generator.
807 if (errno == EINTR) {
810 /* I don't like this, does it ever happen? */
811 printf("sleep(5) from telnet, after select\r\n");
820 if (FD_ISSET(net, &xbits)) {
823 ttyflush(1); /* flush already enqueued data */
827 * Something to read from the network...
829 if (FD_ISSET(net, &ibits)) {
833 canread = ring_empty_consecutive(&netiring);
834 #if !defined(SO_OOBINLINE)
836 * In 4.2 (and some early 4.3) systems, the
837 * OOB indication and data handling in the kernel
838 * is such that if two separate TCP Urgent requests
839 * come in, one byte of TCP data will be overlaid.
840 * This is fatal for Telnet, but we try to live
843 * In addition, in 4.2 (and...), a special protocol
844 * is needed to pick up the TCP Urgent data in
845 * the correct sequence.
847 * What we do is: if we think we are in urgent
848 * mode, we look to see if we are "at the mark".
849 * If we are, we do an OOB receive. If we run
850 * this twice, we will do the OOB receive twice,
851 * but the second will fail, since the second
852 * time we were "at the mark", but there wasn't
853 * any data there (the kernel doesn't reset
854 * "at the mark" until we do a normal read).
855 * Once we've read the OOB data, we go ahead
856 * and do normal reads.
858 * There is also another problem, which is that
859 * since the OOB byte we read doesn't put us
860 * out of OOB state, and since that byte is most
861 * likely the TELNET DM (data mark), we would
862 * stay in the TELNET SYNCH (SYNCHing) state.
863 * So, clocks to the rescue. If we've "just"
864 * received a DM, then we test for the
865 * presence of OOB data when the receive OOB
866 * fails (and AFTER we did the normal mode read
867 * to clear "at the mark").
871 static int bogus_oob = 0, first = 1;
873 ioctl(net, SIOCATMARK, (char *)&atmark);
875 c = recv(net, netiring.supply, canread, MSG_OOB);
876 if ((c == -1) && (errno == EINVAL)) {
877 c = recv(net, netiring.supply, canread, 0);
878 if (clocks.didnetreceive < clocks.gotDM) {
879 SYNCHing = stilloob();
881 } else if (first && c > 0) {
883 * Bogosity check. Systems based on 4.2BSD
884 * do not return an error if you do a second
885 * recv(MSG_OOB). So, we do one. If it
886 * succeeds and returns exactly the same
887 * data, then assume that we are running
888 * on a broken system and set the bogus_oob
889 * flag. (If the data was different, then
890 * we probably got some valid new data, so
891 * increment the count...)
894 i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
896 memcmp(netiring.supply, netiring.supply + c, i) == 0) {
905 if (bogus_oob && c > 0) {
908 * Bogosity. We have to do the read
909 * to clear the atmark to get out of
912 i = read(net, netiring.supply + c, canread - c);
917 c = recv(net, netiring.supply, canread, 0);
920 c = recv(net, netiring.supply, canread, 0);
922 settimer(didnetreceive);
923 #else /* !defined(SO_OOBINLINE) */
924 c = recv(net, (char *)netiring.supply, canread, 0);
925 #endif /* !defined(SO_OOBINLINE) */
926 if (c < 0 && errno == EWOULDBLOCK) {
932 Dump('<', netiring.supply, c);
935 ring_supplied(&netiring, c);
940 * Something to read from the tty...
942 if (FD_ISSET(tin, &ibits)) {
944 c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
945 if (c < 0 && errno == EIO)
947 if (c < 0 && errno == EWOULDBLOCK) {
950 /* EOF detection for line mode!!!! */
951 if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
952 /* must be an EOF... */
953 *ttyiring.supply = termEofChar;
960 Dump('<', ttyiring.supply, c);
962 ring_supplied(&ttyiring, c);
964 returnValue = 1; /* did something useful */
967 if (FD_ISSET(net, &obits)) {
969 returnValue |= netflush();
971 if (FD_ISSET(tout, &obits)) {
972 FD_CLR(tout, &obits);
973 returnValue |= (ttyflush(SYNCHing|flushout) > 0);