1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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. 20 * 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 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 /* from: static char sccsid[] = "@(#)termstat.c 8.1 (Berkeley) 6/4/93"; */ 36 static char *rcsid = "$Id: termstat.c,v 1.3 1994/02/25 03:21:01 cgd Exp $"; 37 #endif /* not lint */ 38 39 #include "telnetd.h" 40 41 /* 42 * local variables 43 */ 44 int def_tspeed = -1, def_rspeed = -1; 45 #ifdef TIOCSWINSZ 46 int def_row = 0, def_col = 0; 47 #endif 48 #ifdef LINEMODE 49 static int _terminit = 0; 50 #endif /* LINEMODE */ 51 52 #if defined(CRAY2) && defined(UNICOS5) 53 int newmap = 1; /* nonzero if \n maps to ^M^J */ 54 #endif 55 56 #ifdef LINEMODE 57 /* 58 * localstat 59 * 60 * This function handles all management of linemode. 61 * 62 * Linemode allows the client to do the local editing of data 63 * and send only complete lines to the server. Linemode state is 64 * based on the state of the pty driver. If the pty is set for 65 * external processing, then we can use linemode. Further, if we 66 * can use real linemode, then we can look at the edit control bits 67 * in the pty to determine what editing the client should do. 68 * 69 * Linemode support uses the following state flags to keep track of 70 * current and desired linemode state. 71 * alwayslinemode : true if -l was specified on the telnetd 72 * command line. It means to have linemode on as much as 73 * possible. 74 * 75 * lmodetype: signifies whether the client can 76 * handle real linemode, or if use of kludgeomatic linemode 77 * is preferred. It will be set to one of the following: 78 * REAL_LINEMODE : use linemode option 79 * NO_KLUDGE : don't initiate kludge linemode. 80 * KLUDGE_LINEMODE : use kludge linemode 81 * NO_LINEMODE : client is ignorant of linemode 82 * 83 * linemode, uselinemode : linemode is true if linemode 84 * is currently on, uselinemode is the state that we wish 85 * to be in. If another function wishes to turn linemode 86 * on or off, it sets or clears uselinemode. 87 * 88 * editmode, useeditmode : like linemode/uselinemode, but 89 * these contain the edit mode states (edit and trapsig). 90 * 91 * The state variables correspond to some of the state information 92 * in the pty. 93 * linemode: 94 * In real linemode, this corresponds to whether the pty 95 * expects external processing of incoming data. 96 * In kludge linemode, this more closely corresponds to the 97 * whether normal processing is on or not. (ICANON in 98 * system V, or COOKED mode in BSD.) 99 * If the -l option was specified (alwayslinemode), then 100 * an attempt is made to force external processing on at 101 * all times. 102 * 103 * The following heuristics are applied to determine linemode 104 * handling within the server. 105 * 1) Early on in starting up the server, an attempt is made 106 * to negotiate the linemode option. If this succeeds 107 * then lmodetype is set to REAL_LINEMODE and all linemode 108 * processing occurs in the context of the linemode option. 109 * 2) If the attempt to negotiate the linemode option failed, 110 * and the "-k" (don't initiate kludge linemode) isn't set, 111 * then we try to use kludge linemode. We test for this 112 * capability by sending "do Timing Mark". If a positive 113 * response comes back, then we assume that the client 114 * understands kludge linemode (ech!) and the 115 * lmodetype flag is set to KLUDGE_LINEMODE. 116 * 3) Otherwise, linemode is not supported at all and 117 * lmodetype remains set to NO_LINEMODE (which happens 118 * to be 0 for convenience). 119 * 4) At any time a command arrives that implies a higher 120 * state of linemode support in the client, we move to that 121 * linemode support. 122 * 123 * A short explanation of kludge linemode is in order here. 124 * 1) The heuristic to determine support for kludge linemode 125 * is to send a do timing mark. We assume that a client 126 * that supports timing marks also supports kludge linemode. 127 * A risky proposition at best. 128 * 2) Further negotiation of linemode is done by changing the 129 * the server's state regarding SGA. If server will SGA, 130 * then linemode is off, if server won't SGA, then linemode 131 * is on. 132 */ 133 void 134 localstat() 135 { 136 void netflush(); 137 int need_will_echo = 0; 138 139 #if defined(CRAY2) && defined(UNICOS5) 140 /* 141 * Keep track of that ol' CR/NL mapping while we're in the 142 * neighborhood. 143 */ 144 newmap = tty_isnewmap(); 145 #endif /* defined(CRAY2) && defined(UNICOS5) */ 146 147 /* 148 * Check for state of BINARY options. 149 */ 150 if (tty_isbinaryin()) { 151 if (his_want_state_is_wont(TELOPT_BINARY)) 152 send_do(TELOPT_BINARY, 1); 153 } else { 154 if (his_want_state_is_will(TELOPT_BINARY)) 155 send_dont(TELOPT_BINARY, 1); 156 } 157 158 if (tty_isbinaryout()) { 159 if (my_want_state_is_wont(TELOPT_BINARY)) 160 send_will(TELOPT_BINARY, 1); 161 } else { 162 if (my_want_state_is_will(TELOPT_BINARY)) 163 send_wont(TELOPT_BINARY, 1); 164 } 165 166 /* 167 * Check for changes to flow control if client supports it. 168 */ 169 flowstat(); 170 171 /* 172 * Check linemode on/off state 173 */ 174 uselinemode = tty_linemode(); 175 176 /* 177 * If alwayslinemode is on, and pty is changing to turn it off, then 178 * force linemode back on. 179 */ 180 if (alwayslinemode && linemode && !uselinemode) { 181 uselinemode = 1; 182 tty_setlinemode(uselinemode); 183 } 184 185 186 /* 187 * Do echo mode handling as soon as we know what the 188 * linemode is going to be. 189 * If the pty has echo turned off, then tell the client that 190 * the server will echo. If echo is on, then the server 191 * will echo if in character mode, but in linemode the 192 * client should do local echoing. The state machine will 193 * not send anything if it is unnecessary, so don't worry 194 * about that here. 195 * 196 * If we need to send the WILL ECHO (because echo is off), 197 * then delay that until after we have changed the MODE. 198 * This way, when the user is turning off both editing 199 * and echo, the client will get editing turned off first. 200 * This keeps the client from going into encryption mode 201 * and then right back out if it is doing auto-encryption 202 * when passwords are being typed. 203 */ 204 if (uselinemode) { 205 if (tty_isecho()) 206 send_wont(TELOPT_ECHO, 1); 207 else 208 need_will_echo = 1; 209 #ifdef KLUDGELINEMODE 210 if (lmodetype == KLUDGE_OK) 211 lmodetype = KLUDGE_LINEMODE; 212 #endif 213 } 214 215 /* 216 * If linemode is being turned off, send appropriate 217 * command and then we're all done. 218 */ 219 if (!uselinemode && linemode) { 220 # ifdef KLUDGELINEMODE 221 if (lmodetype == REAL_LINEMODE) { 222 # endif /* KLUDGELINEMODE */ 223 send_dont(TELOPT_LINEMODE, 1); 224 # ifdef KLUDGELINEMODE 225 } else if (lmodetype == KLUDGE_LINEMODE) 226 send_will(TELOPT_SGA, 1); 227 # endif /* KLUDGELINEMODE */ 228 send_will(TELOPT_ECHO, 1); 229 linemode = uselinemode; 230 goto done; 231 } 232 233 # ifdef KLUDGELINEMODE 234 /* 235 * If using real linemode check edit modes for possible later use. 236 * If we are in kludge linemode, do the SGA negotiation. 237 */ 238 if (lmodetype == REAL_LINEMODE) { 239 # endif /* KLUDGELINEMODE */ 240 useeditmode = 0; 241 if (tty_isediting()) 242 useeditmode |= MODE_EDIT; 243 if (tty_istrapsig()) 244 useeditmode |= MODE_TRAPSIG; 245 if (tty_issofttab()) 246 useeditmode |= MODE_SOFT_TAB; 247 if (tty_islitecho()) 248 useeditmode |= MODE_LIT_ECHO; 249 # ifdef KLUDGELINEMODE 250 } else if (lmodetype == KLUDGE_LINEMODE) { 251 if (tty_isediting() && uselinemode) 252 send_wont(TELOPT_SGA, 1); 253 else 254 send_will(TELOPT_SGA, 1); 255 } 256 # endif /* KLUDGELINEMODE */ 257 258 /* 259 * Negotiate linemode on if pty state has changed to turn it on. 260 * Send appropriate command and send along edit mode, then all done. 261 */ 262 if (uselinemode && !linemode) { 263 # ifdef KLUDGELINEMODE 264 if (lmodetype == KLUDGE_LINEMODE) { 265 send_wont(TELOPT_SGA, 1); 266 } else if (lmodetype == REAL_LINEMODE) { 267 # endif /* KLUDGELINEMODE */ 268 send_do(TELOPT_LINEMODE, 1); 269 /* send along edit modes */ 270 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, 271 TELOPT_LINEMODE, LM_MODE, useeditmode, 272 IAC, SE); 273 nfrontp += 7; 274 editmode = useeditmode; 275 # ifdef KLUDGELINEMODE 276 } 277 # endif /* KLUDGELINEMODE */ 278 linemode = uselinemode; 279 goto done; 280 } 281 282 # ifdef KLUDGELINEMODE 283 /* 284 * None of what follows is of any value if not using 285 * real linemode. 286 */ 287 if (lmodetype < REAL_LINEMODE) 288 goto done; 289 # endif /* KLUDGELINEMODE */ 290 291 if (linemode && his_state_is_will(TELOPT_LINEMODE)) { 292 /* 293 * If edit mode changed, send edit mode. 294 */ 295 if (useeditmode != editmode) { 296 /* 297 * Send along appropriate edit mode mask. 298 */ 299 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, 300 TELOPT_LINEMODE, LM_MODE, useeditmode, 301 IAC, SE); 302 nfrontp += 7; 303 editmode = useeditmode; 304 } 305 306 307 /* 308 * Check for changes to special characters in use. 309 */ 310 start_slc(0); 311 check_slc(); 312 (void) end_slc(0); 313 } 314 315 done: 316 if (need_will_echo) 317 send_will(TELOPT_ECHO, 1); 318 /* 319 * Some things should be deferred until after the pty state has 320 * been set by the local process. Do those things that have been 321 * deferred now. This only happens once. 322 */ 323 if (_terminit == 0) { 324 _terminit = 1; 325 defer_terminit(); 326 } 327 328 netflush(); 329 set_termbuf(); 330 return; 331 332 } /* end of localstat */ 333 #endif /* LINEMODE */ 334 335 /* 336 * flowstat 337 * 338 * Check for changes to flow control 339 */ 340 void 341 flowstat() 342 { 343 if (his_state_is_will(TELOPT_LFLOW)) { 344 if (tty_flowmode() != flowmode) { 345 flowmode = tty_flowmode(); 346 (void) sprintf(nfrontp, "%c%c%c%c%c%c", 347 IAC, SB, TELOPT_LFLOW, 348 flowmode ? LFLOW_ON : LFLOW_OFF, 349 IAC, SE); 350 nfrontp += 6; 351 } 352 if (tty_restartany() != restartany) { 353 restartany = tty_restartany(); 354 (void) sprintf(nfrontp, "%c%c%c%c%c%c", 355 IAC, SB, TELOPT_LFLOW, 356 restartany ? LFLOW_RESTART_ANY 357 : LFLOW_RESTART_XON, 358 IAC, SE); 359 nfrontp += 6; 360 } 361 } 362 } 363 364 /* 365 * clientstat 366 * 367 * Process linemode related requests from the client. 368 * Client can request a change to only one of linemode, editmode or slc's 369 * at a time, and if using kludge linemode, then only linemode may be 370 * affected. 371 */ 372 void 373 clientstat(code, parm1, parm2) 374 register int code, parm1, parm2; 375 { 376 void netflush(); 377 378 /* 379 * Get a copy of terminal characteristics. 380 */ 381 init_termbuf(); 382 383 /* 384 * Process request from client. code tells what it is. 385 */ 386 switch (code) { 387 #ifdef LINEMODE 388 case TELOPT_LINEMODE: 389 /* 390 * Don't do anything unless client is asking us to change 391 * modes. 392 */ 393 uselinemode = (parm1 == WILL); 394 if (uselinemode != linemode) { 395 # ifdef KLUDGELINEMODE 396 /* 397 * If using kludge linemode, make sure that 398 * we can do what the client asks. 399 * We can not turn off linemode if alwayslinemode 400 * and the ICANON bit is set. 401 */ 402 if (lmodetype == KLUDGE_LINEMODE) { 403 if (alwayslinemode && tty_isediting()) { 404 uselinemode = 1; 405 } 406 } 407 408 /* 409 * Quit now if we can't do it. 410 */ 411 if (uselinemode == linemode) 412 return; 413 414 /* 415 * If using real linemode and linemode is being 416 * turned on, send along the edit mode mask. 417 */ 418 if (lmodetype == REAL_LINEMODE && uselinemode) 419 # else /* KLUDGELINEMODE */ 420 if (uselinemode) 421 # endif /* KLUDGELINEMODE */ 422 { 423 useeditmode = 0; 424 if (tty_isediting()) 425 useeditmode |= MODE_EDIT; 426 if (tty_istrapsig) 427 useeditmode |= MODE_TRAPSIG; 428 if (tty_issofttab()) 429 useeditmode |= MODE_SOFT_TAB; 430 if (tty_islitecho()) 431 useeditmode |= MODE_LIT_ECHO; 432 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, 433 SB, TELOPT_LINEMODE, LM_MODE, 434 useeditmode, IAC, SE); 435 nfrontp += 7; 436 editmode = useeditmode; 437 } 438 439 440 tty_setlinemode(uselinemode); 441 442 linemode = uselinemode; 443 444 if (!linemode) 445 send_will(TELOPT_ECHO, 1); 446 } 447 break; 448 449 case LM_MODE: 450 { 451 register int ack, changed; 452 453 /* 454 * Client has sent along a mode mask. If it agrees with 455 * what we are currently doing, ignore it; if not, it could 456 * be viewed as a request to change. Note that the server 457 * will change to the modes in an ack if it is different from 458 * what we currently have, but we will not ack the ack. 459 */ 460 useeditmode &= MODE_MASK; 461 ack = (useeditmode & MODE_ACK); 462 useeditmode &= ~MODE_ACK; 463 464 if (changed = (useeditmode ^ editmode)) { 465 /* 466 * This check is for a timing problem. If the 467 * state of the tty has changed (due to the user 468 * application) we need to process that info 469 * before we write in the state contained in the 470 * ack!!! This gets out the new MODE request, 471 * and when the ack to that command comes back 472 * we'll set it and be in the right mode. 473 */ 474 if (ack) 475 localstat(); 476 if (changed & MODE_EDIT) 477 tty_setedit(useeditmode & MODE_EDIT); 478 479 if (changed & MODE_TRAPSIG) 480 tty_setsig(useeditmode & MODE_TRAPSIG); 481 482 if (changed & MODE_SOFT_TAB) 483 tty_setsofttab(useeditmode & MODE_SOFT_TAB); 484 485 if (changed & MODE_LIT_ECHO) 486 tty_setlitecho(useeditmode & MODE_LIT_ECHO); 487 488 set_termbuf(); 489 490 if (!ack) { 491 (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, 492 SB, TELOPT_LINEMODE, LM_MODE, 493 useeditmode|MODE_ACK, 494 IAC, SE); 495 nfrontp += 7; 496 } 497 498 editmode = useeditmode; 499 } 500 501 break; 502 503 } /* end of case LM_MODE */ 504 #endif /* LINEMODE */ 505 506 case TELOPT_NAWS: 507 #ifdef TIOCSWINSZ 508 { 509 struct winsize ws; 510 511 def_col = parm1; 512 def_row = parm2; 513 #ifdef LINEMODE 514 /* 515 * Defer changing window size until after terminal is 516 * initialized. 517 */ 518 if (terminit() == 0) 519 return; 520 #endif /* LINEMODE */ 521 522 /* 523 * Change window size as requested by client. 524 */ 525 526 ws.ws_col = parm1; 527 ws.ws_row = parm2; 528 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); 529 } 530 #endif /* TIOCSWINSZ */ 531 532 break; 533 534 case TELOPT_TSPEED: 535 { 536 def_tspeed = parm1; 537 def_rspeed = parm2; 538 #ifdef LINEMODE 539 /* 540 * Defer changing the terminal speed. 541 */ 542 if (terminit() == 0) 543 return; 544 #endif /* LINEMODE */ 545 /* 546 * Change terminal speed as requested by client. 547 * We set the receive speed first, so that if we can't 548 * store seperate receive and transmit speeds, the transmit 549 * speed will take precedence. 550 */ 551 tty_rspeed(parm2); 552 tty_tspeed(parm1); 553 set_termbuf(); 554 555 break; 556 557 } /* end of case TELOPT_TSPEED */ 558 559 default: 560 /* What? */ 561 break; 562 } /* end of switch */ 563 564 #if defined(CRAY2) && defined(UNICOS5) 565 /* 566 * Just in case of the likely event that we changed the pty state. 567 */ 568 rcv_ioctl(); 569 #endif /* defined(CRAY2) && defined(UNICOS5) */ 570 571 netflush(); 572 573 } /* end of clientstat */ 574 575 #if defined(CRAY2) && defined(UNICOS5) 576 void 577 termstat() 578 { 579 needtermstat = 1; 580 } 581 582 void 583 _termstat() 584 { 585 needtermstat = 0; 586 init_termbuf(); 587 localstat(); 588 rcv_ioctl(); 589 } 590 #endif /* defined(CRAY2) && defined(UNICOS5) */ 591 592 #ifdef LINEMODE 593 /* 594 * defer_terminit 595 * 596 * Some things should not be done until after the login process has started 597 * and all the pty modes are set to what they are supposed to be. This 598 * function is called when the pty state has been processed for the first time. 599 * It calls other functions that do things that were deferred in each module. 600 */ 601 void 602 defer_terminit() 603 { 604 605 /* 606 * local stuff that got deferred. 607 */ 608 if (def_tspeed != -1) { 609 clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed); 610 def_tspeed = def_rspeed = 0; 611 } 612 613 #ifdef TIOCSWINSZ 614 if (def_col || def_row) { 615 struct winsize ws; 616 617 bzero((char *)&ws, sizeof(ws)); 618 ws.ws_col = def_col; 619 ws.ws_row = def_row; 620 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); 621 } 622 #endif 623 624 /* 625 * The only other module that currently defers anything. 626 */ 627 deferslc(); 628 629 } /* end of defer_terminit */ 630 631 /* 632 * terminit 633 * 634 * Returns true if the pty state has been processed yet. 635 */ 636 int 637 terminit() 638 { 639 return(_terminit); 640 641 } /* end of terminit */ 642 #endif /* LINEMODE */ 643