1 /* $OpenBSD: sys_bsd.c,v 1.28 2014/09/09 03:41:08 guenther 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((cc_t *)0); 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 tcsetattr(tin, TCSADRAIN, &tmp_tc); 270 old = ttyflush(SYNCHing|flushout); 271 } while (old < 0 || old > 1); 272 } 273 274 old = prevmode; 275 prevmode = f&~MODE_FORCE; 276 tmp_tc = new_tc; 277 278 if (f&MODE_ECHO) { 279 tmp_tc.c_lflag |= ECHO; 280 tmp_tc.c_oflag |= ONLCR; 281 if (crlf) 282 tmp_tc.c_iflag |= ICRNL; 283 } else { 284 tmp_tc.c_lflag &= ~ECHO; 285 tmp_tc.c_oflag &= ~ONLCR; 286 } 287 288 if ((f&MODE_FLOW) == 0) { 289 tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ 290 } else { 291 if (restartany < 0) { 292 tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */ 293 } else if (restartany > 0) { 294 tmp_tc.c_iflag |= IXOFF|IXON|IXANY; 295 } else { 296 tmp_tc.c_iflag |= IXOFF|IXON; 297 tmp_tc.c_iflag &= ~IXANY; 298 } 299 } 300 301 if ((f&MODE_TRAPSIG) == 0) { 302 tmp_tc.c_lflag &= ~ISIG; 303 localchars = 0; 304 } else { 305 tmp_tc.c_lflag |= ISIG; 306 localchars = 1; 307 } 308 309 if (f&MODE_EDIT) { 310 tmp_tc.c_lflag |= ICANON; 311 } else { 312 tmp_tc.c_lflag &= ~ICANON; 313 tmp_tc.c_iflag &= ~ICRNL; 314 tmp_tc.c_cc[VMIN] = 1; 315 tmp_tc.c_cc[VTIME] = 0; 316 } 317 318 if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { 319 tmp_tc.c_lflag &= ~IEXTEN; 320 } 321 322 if (f&MODE_SOFT_TAB) { 323 # ifdef OXTABS 324 tmp_tc.c_oflag |= OXTABS; 325 # endif 326 # ifdef TABDLY 327 tmp_tc.c_oflag &= ~TABDLY; 328 tmp_tc.c_oflag |= TAB3; 329 # endif 330 } else { 331 # ifdef OXTABS 332 tmp_tc.c_oflag &= ~OXTABS; 333 # endif 334 # ifdef TABDLY 335 tmp_tc.c_oflag &= ~TABDLY; 336 # endif 337 } 338 339 if (f&MODE_LIT_ECHO) { 340 # ifdef ECHOCTL 341 tmp_tc.c_lflag &= ~ECHOCTL; 342 # endif 343 } else { 344 # ifdef ECHOCTL 345 tmp_tc.c_lflag |= ECHOCTL; 346 # endif 347 } 348 349 if (f == -1) { 350 onoff = 0; 351 } else { 352 if (f & MODE_INBIN) 353 tmp_tc.c_iflag &= ~ISTRIP; 354 else 355 tmp_tc.c_iflag |= ISTRIP; 356 if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) { 357 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 358 tmp_tc.c_cflag |= CS8; 359 if(f & MODE_OUTBIN) 360 tmp_tc.c_oflag &= ~OPOST; 361 else 362 tmp_tc.c_oflag |= OPOST; 363 364 } else { 365 tmp_tc.c_cflag &= ~(CSIZE|PARENB); 366 tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); 367 tmp_tc.c_oflag |= OPOST; 368 } 369 onoff = 1; 370 } 371 372 if (f != -1) { 373 (void) signal(SIGTSTP, susp); 374 #ifdef SIGINFO 375 (void) signal(SIGINFO, ayt); 376 #endif 377 #if defined(NOKERNINFO) 378 tmp_tc.c_lflag |= NOKERNINFO; 379 #endif 380 /* 381 * We don't want to process ^Y here. It's just another 382 * character that we'll pass on to the back end. It has 383 * to process it because it will be processed when the 384 * user attempts to read it, not when we send it. 385 */ 386 # ifdef VDSUSP 387 tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); 388 # endif 389 /* 390 * If the VEOL character is already set, then use VEOL2, 391 * otherwise use VEOL. 392 */ 393 esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; 394 if ((tmp_tc.c_cc[VEOL] != esc) 395 # ifdef VEOL2 396 && (tmp_tc.c_cc[VEOL2] != esc) 397 # endif 398 ) { 399 if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) 400 tmp_tc.c_cc[VEOL] = esc; 401 # ifdef VEOL2 402 else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) 403 tmp_tc.c_cc[VEOL2] = esc; 404 # endif 405 } 406 } else { 407 sigset_t mask; 408 #ifdef SIGINFO 409 (void) signal(SIGINFO, ayt_status); 410 #endif 411 (void) signal(SIGTSTP, SIG_DFL); 412 sigemptyset(&mask); 413 sigaddset(&mask, SIGTSTP); 414 sigprocmask(SIG_UNBLOCK, &mask, NULL); 415 tmp_tc = old_tc; 416 } 417 if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) 418 tcsetattr(tin, TCSANOW, &tmp_tc); 419 420 ioctl(tin, FIONBIO, &onoff); 421 ioctl(tout, FIONBIO, &onoff); 422 } 423 424 /* 425 * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD). 426 */ 427 #if B4800 != 4800 428 #define DECODE_BAUD 429 #endif 430 431 #ifdef DECODE_BAUD 432 #ifndef B7200 433 #define B7200 B4800 434 #endif 435 436 #ifndef B14400 437 #define B14400 B9600 438 #endif 439 440 #ifndef B19200 441 # define B19200 B14400 442 #endif 443 444 #ifndef B28800 445 #define B28800 B19200 446 #endif 447 448 #ifndef B38400 449 # define B38400 B28800 450 #endif 451 452 #ifndef B57600 453 #define B57600 B38400 454 #endif 455 456 #ifndef B76800 457 #define B76800 B57600 458 #endif 459 460 #ifndef B115200 461 #define B115200 B76800 462 #endif 463 464 #ifndef B230400 465 #define B230400 B115200 466 #endif 467 468 469 /* 470 * This code assumes that the values B0, B50, B75... 471 * are in ascending order. They do not have to be 472 * contiguous. 473 */ 474 struct termspeeds { 475 long speed; 476 long value; 477 } termspeeds[] = { 478 { 0, B0 }, { 50, B50 }, { 75, B75 }, 479 { 110, B110 }, { 134, B134 }, { 150, B150 }, 480 { 200, B200 }, { 300, B300 }, { 600, B600 }, 481 { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, 482 { 4800, B4800 }, { 7200, B7200 }, { 9600, B9600 }, 483 { 14400, B14400 }, { 19200, B19200 }, { 28800, B28800 }, 484 { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, 485 { 230400, B230400 }, { -1, B230400 } 486 }; 487 #endif /* DECODE_BAUD */ 488 489 void 490 TerminalSpeeds(long *ispeed, long *ospeed) 491 { 492 #ifdef DECODE_BAUD 493 struct termspeeds *tp; 494 #endif /* DECODE_BAUD */ 495 long in, out; 496 497 out = cfgetospeed(&old_tc); 498 in = cfgetispeed(&old_tc); 499 if (in == 0) 500 in = out; 501 502 #ifdef DECODE_BAUD 503 tp = termspeeds; 504 while ((tp->speed != -1) && (tp->value < in)) 505 tp++; 506 *ispeed = tp->speed; 507 508 tp = termspeeds; 509 while ((tp->speed != -1) && (tp->value < out)) 510 tp++; 511 *ospeed = tp->speed; 512 #else /* DECODE_BAUD */ 513 *ispeed = in; 514 *ospeed = out; 515 #endif /* DECODE_BAUD */ 516 } 517 518 int 519 TerminalWindowSize(long *rows, long *cols) 520 { 521 #ifdef TIOCGWINSZ 522 struct winsize ws; 523 524 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { 525 *rows = ws.ws_row; 526 *cols = ws.ws_col; 527 return 1; 528 } 529 #endif /* TIOCGWINSZ */ 530 return 0; 531 } 532 533 /* 534 * Various signal handling routines. 535 */ 536 537 void 538 deadpeer(int sig) 539 { 540 setcommandmode(); 541 longjmp(peerdied, -1); 542 } 543 544 void 545 intr(int sig) 546 { 547 if (localchars) { 548 intp(); 549 return; 550 } 551 setcommandmode(); 552 longjmp(toplevel, -1); 553 } 554 555 void 556 intr2(int sig) 557 { 558 if (localchars) { 559 #ifdef KLUDGELINEMODE 560 if (kludgelinemode) 561 sendbrk(); 562 else 563 #endif 564 sendabort(); 565 return; 566 } 567 } 568 569 void 570 susp(int sig) 571 { 572 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) 573 return; 574 if (localchars) 575 sendsusp(); 576 } 577 578 #ifdef SIGWINCH 579 void 580 sendwin(int sig) 581 { 582 if (connected) { 583 sendnaws(); 584 } 585 } 586 #endif 587 588 #ifdef SIGINFO 589 void 590 ayt(int sig) 591 { 592 if (connected) 593 sendayt(); 594 else 595 ayt_status(sig); 596 } 597 #endif 598 599 600 void 601 sys_telnet_init(void) 602 { 603 int one = 1; 604 605 (void) signal(SIGINT, intr); 606 (void) signal(SIGQUIT, intr2); 607 (void) signal(SIGPIPE, deadpeer); 608 #ifdef SIGWINCH 609 (void) signal(SIGWINCH, sendwin); 610 #endif 611 (void) signal(SIGTSTP, susp); 612 #ifdef SIGINFO 613 (void) signal(SIGINFO, ayt); 614 #endif 615 616 setconnmode(0); 617 618 /* 619 * Mark the socket as non-blocking and receive urgent data inline. 620 * (The latter is required for correct telnet operation when a 621 * second urgent is sent before telnet can process the first.) 622 */ 623 ioctl(net, FIONBIO, &one); 624 if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) { 625 perror("setsockopt"); 626 } 627 } 628 629 /* 630 * Process rings - 631 * 632 * This routine tries to fill up/empty our various rings. 633 * 634 * The parameter specifies whether this is a poll operation, 635 * or a block-until-something-happens operation. 636 * 637 * The return value is 1 if something happened, 0 if not. 638 */ 639 640 int 641 process_rings(int netin, int netout, int netex, int ttyin, int ttyout, 642 int dopoll) /* If 0, then block until something to do */ 643 { 644 int c; 645 /* One wants to be a bit careful about setting returnValue 646 * to one, since a one implies we did some useful work, 647 * and therefore probably won't be called to block next 648 * time (TN3270 mode only). 649 */ 650 int returnValue = 0; 651 struct pollfd pfd[TELNET_FD_NUM]; 652 653 if (ttyout) { 654 pfd[TELNET_FD_TOUT].fd = tout; 655 pfd[TELNET_FD_TOUT].events = POLLOUT; 656 } else { 657 pfd[TELNET_FD_TOUT].fd = -1; 658 } 659 if (ttyin) { 660 pfd[TELNET_FD_TIN].fd = tin; 661 pfd[TELNET_FD_TIN].events = POLLIN; 662 } else { 663 pfd[TELNET_FD_TIN].fd = -1; 664 } 665 if (netout || netin || netex) { 666 pfd[TELNET_FD_NET].fd = net; 667 pfd[TELNET_FD_NET].events = 0; 668 if (netout) 669 pfd[TELNET_FD_NET].events |= POLLOUT; 670 if (netin) 671 pfd[TELNET_FD_NET].events |= POLLIN; 672 if (netex) 673 pfd[TELNET_FD_NET].events |= POLLRDBAND; 674 } else { 675 pfd[TELNET_FD_NET].fd = -1; 676 } 677 678 if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) < 0) { 679 return 0; 680 } 681 682 /* 683 * Any urgent data? 684 */ 685 if (pfd[TELNET_FD_NET].revents & POLLRDBAND) { 686 SYNCHing = 1; 687 (void) ttyflush(1); /* flush already enqueued data */ 688 } 689 690 /* 691 * Something to read from the network... 692 */ 693 if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) { 694 int canread; 695 696 canread = ring_empty_consecutive(&netiring); 697 c = recv(net, netiring.supply, canread, 0); 698 if (c < 0 && errno == EWOULDBLOCK) { 699 c = 0; 700 } else if (c <= 0) { 701 return -1; 702 } 703 if (netdata) { 704 Dump('<', netiring.supply, c); 705 } 706 if (c) 707 ring_supplied(&netiring, c); 708 returnValue = 1; 709 } 710 711 /* 712 * Something to read from the tty... 713 */ 714 if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) { 715 c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring)); 716 if (c < 0 && errno == EIO) 717 c = 0; 718 if (c < 0 && errno == EWOULDBLOCK) { 719 c = 0; 720 } else { 721 /* EOF detection for line mode!!!! */ 722 if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { 723 /* must be an EOF... */ 724 *ttyiring.supply = termEofChar; 725 c = 1; 726 } 727 if (c <= 0) { 728 return -1; 729 } 730 if (termdata) { 731 Dump('<', ttyiring.supply, c); 732 } 733 ring_supplied(&ttyiring, c); 734 } 735 returnValue = 1; /* did something useful */ 736 } 737 738 if (pfd[TELNET_FD_NET].revents & POLLOUT) { 739 returnValue |= netflush(); 740 } 741 if (pfd[TELNET_FD_TOUT].revents & POLLOUT) { 742 returnValue |= (ttyflush(SYNCHing|flushout) > 0); 743 } 744 745 return returnValue; 746 } 747