1 /* $OpenBSD: sys_bsd.c,v 1.32 2016/03/16 15:41:11 krw 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 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 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 121 TerminalSaveState(void) 122 { 123 tcgetattr(0, &old_tc); 124 125 new_tc = old_tc; 126 127 #ifndef VDISCARD 128 termFlushChar = CONTROL('O'); 129 #endif 130 #ifndef VWERASE 131 termWerasChar = CONTROL('W'); 132 #endif 133 #ifndef VREPRINT 134 termRprntChar = CONTROL('R'); 135 #endif 136 #ifndef VLNEXT 137 termLiteralNextChar = CONTROL('V'); 138 #endif 139 #ifndef VSTART 140 termStartChar = CONTROL('Q'); 141 #endif 142 #ifndef VSTOP 143 termStopChar = CONTROL('S'); 144 #endif 145 #ifndef VSTATUS 146 termAytChar = CONTROL('T'); 147 #endif 148 } 149 150 cc_t * 151 tcval(int func) 152 { 153 switch(func) { 154 case SLC_IP: return(&termIntChar); 155 case SLC_ABORT: return(&termQuitChar); 156 case SLC_EOF: return(&termEofChar); 157 case SLC_EC: return(&termEraseChar); 158 case SLC_EL: return(&termKillChar); 159 case SLC_XON: return(&termStartChar); 160 case SLC_XOFF: return(&termStopChar); 161 case SLC_FORW1: return(&termForw1Char); 162 case SLC_FORW2: return(&termForw2Char); 163 case SLC_SUSP: return(&termSuspChar); 164 # ifdef VDISCARD 165 case SLC_AO: return(&termFlushChar); 166 # endif 167 # ifdef VWERASE 168 case SLC_EW: return(&termWerasChar); 169 # endif 170 # ifdef VREPRINT 171 case SLC_RP: return(&termRprntChar); 172 # endif 173 # ifdef VLNEXT 174 case SLC_LNEXT: return(&termLiteralNextChar); 175 # endif 176 # ifdef VSTATUS 177 case SLC_AYT: return(&termAytChar); 178 # endif 179 180 case SLC_SYNCH: 181 case SLC_BRK: 182 case SLC_EOR: 183 default: 184 return(NULL); 185 } 186 } 187 188 void 189 TerminalDefaultChars(void) 190 { 191 memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc)); 192 # ifndef VDISCARD 193 termFlushChar = CONTROL('O'); 194 # endif 195 # ifndef VWERASE 196 termWerasChar = CONTROL('W'); 197 # endif 198 # ifndef VREPRINT 199 termRprntChar = CONTROL('R'); 200 # endif 201 # ifndef VLNEXT 202 termLiteralNextChar = CONTROL('V'); 203 # endif 204 # ifndef VSTART 205 termStartChar = CONTROL('Q'); 206 # endif 207 # ifndef VSTOP 208 termStopChar = CONTROL('S'); 209 # endif 210 # ifndef VSTATUS 211 termAytChar = CONTROL('T'); 212 # endif 213 } 214 215 /* 216 * TerminalNewMode - set up terminal to a specific mode. 217 * MODE_ECHO: do local terminal echo 218 * MODE_FLOW: do local flow control 219 * MODE_TRAPSIG: do local mapping to TELNET IAC sequences 220 * MODE_EDIT: do local line editing 221 * 222 * Command mode: 223 * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG 224 * local echo 225 * local editing 226 * local xon/xoff 227 * local signal mapping 228 * 229 * Linemode: 230 * local/no editing 231 * Both Linemode and Single Character mode: 232 * local/remote echo 233 * local/no xon/xoff 234 * local/no signal mapping 235 */ 236 237 static void susp(); 238 #ifdef SIGINFO 239 static void ayt(); 240 #endif 241 242 void 243 TerminalNewMode(int f) 244 { 245 static int prevmode = 0; 246 struct termios tmp_tc; 247 int onoff; 248 int old; 249 cc_t esc; 250 251 globalmode = f&~MODE_FORCE; 252 if (prevmode == f) 253 return; 254 255 /* 256 * Write any outstanding data before switching modes 257 * ttyflush() returns 0 only when there is no more data 258 * left to write out, it returns -1 if it couldn't do 259 * anything at all, otherwise it returns 1 + the number 260 * of characters left to write. 261 */ 262 old = ttyflush(SYNCHing|flushout); 263 if (old < 0 || old > 1) { 264 tcgetattr(tin, &tmp_tc); 265 do { 266 /* 267 * Wait for data to drain, then flush again. 268 */ 269 if (isatty(tin)) 270 tcsetattr(tin, TCSADRAIN, &tmp_tc); 271 old = ttyflush(SYNCHing|flushout); 272 } while (old < 0 || old > 1); 273 } 274 275 old = prevmode; 276 prevmode = f&~MODE_FORCE; 277 tmp_tc = new_tc; 278 279 if (f&MODE_ECHO) { 280 tmp_tc.c_lflag |= ECHO; 281 tmp_tc.c_oflag |= ONLCR; 282 if (crlf) 283 tmp_tc.c_iflag |= ICRNL; 284 } else { 285 tmp_tc.c_lflag &= ~ECHO; 286 tmp_tc.c_oflag &= ~ONLCR; 287 } 288 289 if ((f&MODE_FLOW) == 0) { 290 tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ 291 } else { 292 if (restartany < 0) { 293 tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */ 294 } else if (restartany > 0) { 295 tmp_tc.c_iflag |= IXOFF|IXON|IXANY; 296 } else { 297 tmp_tc.c_iflag |= IXOFF|IXON; 298 tmp_tc.c_iflag &= ~IXANY; 299 } 300 } 301 302 if ((f&MODE_TRAPSIG) == 0) { 303 tmp_tc.c_lflag &= ~ISIG; 304 localchars = 0; 305 } else { 306 tmp_tc.c_lflag |= ISIG; 307 localchars = 1; 308 } 309 310 if (f&MODE_EDIT) { 311 tmp_tc.c_lflag |= ICANON; 312 } else { 313 tmp_tc.c_lflag &= ~ICANON; 314 tmp_tc.c_iflag &= ~ICRNL; 315 tmp_tc.c_cc[VMIN] = 1; 316 tmp_tc.c_cc[VTIME] = 0; 317 } 318 319 if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { 320 tmp_tc.c_lflag &= ~IEXTEN; 321 } 322 323 if (f&MODE_SOFT_TAB) { 324 # ifdef OXTABS 325 tmp_tc.c_oflag |= OXTABS; 326 # endif 327 # ifdef TABDLY 328 tmp_tc.c_oflag &= ~TABDLY; 329 tmp_tc.c_oflag |= TAB3; 330 # endif 331 } else { 332 # ifdef OXTABS 333 tmp_tc.c_oflag &= ~OXTABS; 334 # endif 335 # ifdef TABDLY 336 tmp_tc.c_oflag &= ~TABDLY; 337 # endif 338 } 339 340 if (f&MODE_LIT_ECHO) { 341 # ifdef ECHOCTL 342 tmp_tc.c_lflag &= ~ECHOCTL; 343 # endif 344 } else { 345 # ifdef ECHOCTL 346 tmp_tc.c_lflag |= ECHOCTL; 347 # endif 348 } 349 350 if (f == -1) { 351 onoff = 0; 352 } else { 353 if (f & MODE_INBIN) 354 tmp_tc.c_iflag &= ~ISTRIP; 355 else 356 tmp_tc.c_iflag |= ISTRIP; 357 if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) { 358 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 359 tmp_tc.c_cflag |= CS8; 360 if(f & MODE_OUTBIN) 361 tmp_tc.c_oflag &= ~OPOST; 362 else 363 tmp_tc.c_oflag |= OPOST; 364 365 } else { 366 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 367 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); 368 tmp_tc.c_oflag |= OPOST; 369 } 370 onoff = 1; 371 } 372 373 if (f != -1) { 374 (void) signal(SIGTSTP, susp); 375 #ifdef SIGINFO 376 (void) signal(SIGINFO, ayt); 377 #endif 378 #if defined(NOKERNINFO) 379 tmp_tc.c_lflag |= NOKERNINFO; 380 #endif 381 /* 382 * We don't want to process ^Y here. It's just another 383 * character that we'll pass on to the back end. It has 384 * to process it because it will be processed when the 385 * user attempts to read it, not when we send it. 386 */ 387 # ifdef VDSUSP 388 tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); 389 # endif 390 /* 391 * If the VEOL character is already set, then use VEOL2, 392 * otherwise use VEOL. 393 */ 394 esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; 395 if ((tmp_tc.c_cc[VEOL] != esc) 396 # ifdef VEOL2 397 && (tmp_tc.c_cc[VEOL2] != esc) 398 # endif 399 ) { 400 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) 401 tmp_tc.c_cc[VEOL] = esc; 402 # ifdef VEOL2 403 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) 404 tmp_tc.c_cc[VEOL2] = esc; 405 # endif 406 } 407 } else { 408 sigset_t mask; 409 #ifdef SIGINFO 410 (void) signal(SIGINFO, ayt_status); 411 #endif 412 (void) signal(SIGTSTP, SIG_DFL); 413 sigemptyset(&mask); 414 sigaddset(&mask, SIGTSTP); 415 sigprocmask(SIG_UNBLOCK, &mask, NULL); 416 tmp_tc = old_tc; 417 } 418 if (isatty(tin) && tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) 419 tcsetattr(tin, TCSANOW, &tmp_tc); 420 421 ioctl(tin, FIONBIO, &onoff); 422 ioctl(tout, FIONBIO, &onoff); 423 } 424 425 /* 426 * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD). 427 */ 428 #if B4800 != 4800 429 #define DECODE_BAUD 430 #endif 431 432 #ifdef DECODE_BAUD 433 #ifndef B7200 434 #define B7200 B4800 435 #endif 436 437 #ifndef B14400 438 #define B14400 B9600 439 #endif 440 441 #ifndef B19200 442 # define B19200 B14400 443 #endif 444 445 #ifndef B28800 446 #define B28800 B19200 447 #endif 448 449 #ifndef B38400 450 # define B38400 B28800 451 #endif 452 453 #ifndef B57600 454 #define B57600 B38400 455 #endif 456 457 #ifndef B76800 458 #define B76800 B57600 459 #endif 460 461 #ifndef B115200 462 #define B115200 B76800 463 #endif 464 465 #ifndef B230400 466 #define B230400 B115200 467 #endif 468 469 470 /* 471 * This code assumes that the values B0, B50, B75... 472 * are in ascending order. They do not have to be 473 * contiguous. 474 */ 475 struct termspeeds { 476 long speed; 477 long value; 478 } termspeeds[] = { 479 { 0, B0 }, { 50, B50 }, { 75, B75 }, 480 { 110, B110 }, { 134, B134 }, { 150, B150 }, 481 { 200, B200 }, { 300, B300 }, { 600, B600 }, 482 { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, 483 { 4800, B4800 }, { 7200, B7200 }, { 9600, B9600 }, 484 { 14400, B14400 }, { 19200, B19200 }, { 28800, B28800 }, 485 { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, 486 { 230400, B230400 }, { -1, B230400 } 487 }; 488 #endif /* DECODE_BAUD */ 489 490 void 491 TerminalSpeeds(long *ispeed, long *ospeed) 492 { 493 #ifdef DECODE_BAUD 494 struct termspeeds *tp; 495 #endif /* DECODE_BAUD */ 496 long in, out; 497 498 out = cfgetospeed(&old_tc); 499 in = cfgetispeed(&old_tc); 500 if (in == 0) 501 in = out; 502 503 #ifdef DECODE_BAUD 504 tp = termspeeds; 505 while ((tp->speed != -1) && (tp->value < in)) 506 tp++; 507 *ispeed = tp->speed; 508 509 tp = termspeeds; 510 while ((tp->speed != -1) && (tp->value < out)) 511 tp++; 512 *ospeed = tp->speed; 513 #else /* DECODE_BAUD */ 514 *ispeed = in; 515 *ospeed = out; 516 #endif /* DECODE_BAUD */ 517 } 518 519 int 520 TerminalWindowSize(long *rows, long *cols) 521 { 522 #ifdef TIOCGWINSZ 523 struct winsize ws; 524 525 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { 526 *rows = ws.ws_row; 527 *cols = ws.ws_col; 528 return 1; 529 } 530 #endif /* TIOCGWINSZ */ 531 return 0; 532 } 533 534 /* 535 * Various signal handling routines. 536 */ 537 538 void 539 deadpeer(int sig) 540 { 541 setcommandmode(); 542 longjmp(peerdied, -1); 543 } 544 545 void 546 intr(int sig) 547 { 548 if (localchars) { 549 intp(); 550 return; 551 } 552 setcommandmode(); 553 longjmp(toplevel, -1); 554 } 555 556 void 557 intr2(int sig) 558 { 559 if (localchars) { 560 #ifdef KLUDGELINEMODE 561 if (kludgelinemode) 562 sendbrk(); 563 else 564 #endif 565 sendabort(); 566 return; 567 } 568 } 569 570 void 571 susp(int sig) 572 { 573 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) 574 return; 575 if (localchars) 576 sendsusp(); 577 } 578 579 #ifdef SIGWINCH 580 void 581 sendwin(int sig) 582 { 583 if (connected) { 584 sendnaws(); 585 } 586 } 587 #endif 588 589 #ifdef SIGINFO 590 void 591 ayt(int sig) 592 { 593 if (connected) 594 sendayt(); 595 else 596 ayt_status(sig); 597 } 598 #endif 599 600 601 void 602 sys_telnet_init(void) 603 { 604 int one = 1; 605 606 (void) signal(SIGINT, intr); 607 (void) signal(SIGQUIT, intr2); 608 (void) signal(SIGPIPE, deadpeer); 609 #ifdef SIGWINCH 610 (void) signal(SIGWINCH, sendwin); 611 #endif 612 (void) signal(SIGTSTP, susp); 613 #ifdef SIGINFO 614 (void) signal(SIGINFO, ayt); 615 #endif 616 617 setconnmode(0); 618 619 /* 620 * Mark the socket as non-blocking and receive urgent data inline. 621 * (The latter is required for correct telnet operation when a 622 * second urgent is sent before telnet can process the first.) 623 */ 624 ioctl(net, FIONBIO, &one); 625 if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) { 626 perror("setsockopt"); 627 } 628 } 629 630 /* 631 * Process rings - 632 * 633 * This routine tries to fill up/empty our various rings. 634 * 635 * The parameter specifies whether this is a poll operation, 636 * or a block-until-something-happens operation. 637 * 638 * The return value is 1 if something happened, 0 if not. 639 */ 640 641 int 642 process_rings(int netin, int netout, int netex, int ttyin, int ttyout, 643 int dopoll) /* If 0, then block until something to do */ 644 { 645 int c; 646 /* One wants to be a bit careful about setting returnValue 647 * to one, since a one implies we did some useful work, 648 * and therefore probably won't be called to block next 649 * time (TN3270 mode only). 650 */ 651 int returnValue = 0; 652 struct pollfd pfd[TELNET_FD_NUM]; 653 654 if (ttyout) { 655 pfd[TELNET_FD_TOUT].fd = tout; 656 pfd[TELNET_FD_TOUT].events = POLLOUT; 657 } else { 658 pfd[TELNET_FD_TOUT].fd = -1; 659 } 660 if (ttyin) { 661 pfd[TELNET_FD_TIN].fd = tin; 662 pfd[TELNET_FD_TIN].events = POLLIN; 663 } else { 664 pfd[TELNET_FD_TIN].fd = -1; 665 } 666 if (netout || netin || netex) { 667 pfd[TELNET_FD_NET].fd = net; 668 pfd[TELNET_FD_NET].events = 0; 669 if (netout) 670 pfd[TELNET_FD_NET].events |= POLLOUT; 671 if (netin) 672 pfd[TELNET_FD_NET].events |= POLLIN; 673 if (netex) 674 pfd[TELNET_FD_NET].events |= POLLRDBAND; 675 } else { 676 pfd[TELNET_FD_NET].fd = -1; 677 } 678 679 if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) < 0) { 680 return 0; 681 } 682 683 /* 684 * Any urgent data? 685 */ 686 if (pfd[TELNET_FD_NET].revents & POLLRDBAND) { 687 SYNCHing = 1; 688 (void) ttyflush(1); /* flush already enqueued data */ 689 } 690 691 /* 692 * Something to read from the network... 693 */ 694 if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) { 695 int canread; 696 697 canread = ring_empty_consecutive(&netiring); 698 c = recv(net, netiring.supply, canread, 0); 699 if (c < 0 && errno == EWOULDBLOCK) { 700 c = 0; 701 } else if (c <= 0) { 702 return -1; 703 } 704 if (netdata) { 705 Dump('<', netiring.supply, c); 706 } 707 if (c) 708 ring_supplied(&netiring, c); 709 returnValue = 1; 710 } 711 712 /* 713 * Something to read from the tty... 714 */ 715 if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) { 716 c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring)); 717 if (c < 0 && errno == EIO) 718 c = 0; 719 if (c < 0 && errno == EWOULDBLOCK) { 720 c = 0; 721 } else { 722 /* EOF detection for line mode!!!! */ 723 if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { 724 /* must be an EOF... */ 725 *ttyiring.supply = termEofChar; 726 c = 1; 727 } 728 if (c <= 0) { 729 return -1; 730 } 731 if (termdata) { 732 Dump('<', ttyiring.supply, c); 733 } 734 ring_supplied(&ttyiring, c); 735 } 736 returnValue = 1; /* did something useful */ 737 } 738 739 if (pfd[TELNET_FD_NET].revents & POLLOUT) { 740 returnValue |= netflush(); 741 } 742 if (pfd[TELNET_FD_TOUT].revents & POLLOUT) { 743 returnValue |= (ttyflush(SYNCHing|flushout) > 0); 744 } 745 746 return returnValue; 747 } 748