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 48int 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 58struct termios old_tc = { 0 }; 59 60void 61init_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 82int 83TerminalSpecialChars(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 120void 121TerminalSaveState(void) 122{ 123 tcgetattr(0, &old_tc); 124 125 new_tc = old_tc; 126} 127 128cc_t * 129tcval(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 155void 156TerminalDefaultChars(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 183static void susp(int); 184static void ayt(int); 185 186void 187TerminalNewMode(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 365void 366TerminalSpeeds(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 379int 380TerminalWindowSize(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 396void 397deadpeer(int sig) 398{ 399 setcommandmode(); 400 longjmp(peerdied, -1); 401} 402 403void 404intr(int sig) 405{ 406 if (localchars) { 407 intp(); 408 return; 409 } 410 setcommandmode(); 411 longjmp(toplevel, -1); 412} 413 414void 415intr2(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 428void 429susp(int sig) 430{ 431 if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) 432 return; 433 if (localchars) 434 sendsusp(); 435} 436 437void 438sendwin(int sig) 439{ 440 if (connected) { 441 sendnaws(); 442 } 443} 444 445void 446ayt(int sig) 447{ 448 if (connected) 449 sendayt(); 450 else 451 ayt_status(sig); 452} 453 454 455void 456sys_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 491int 492process_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