1 1.35 kre /* $NetBSD: state.c,v 1.35 2024/10/29 13:10:10 kre Exp $ */ 2 1.9 thorpej 3 1.1 cgd /* 4 1.5 cgd * Copyright (c) 1989, 1993 5 1.5 cgd * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.24 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.11 mrg #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.9 thorpej #if 0 35 1.9 thorpej static char sccsid[] = "@(#)state.c 8.5 (Berkeley) 5/30/95"; 36 1.9 thorpej #else 37 1.35 kre __RCSID("$NetBSD: state.c,v 1.35 2024/10/29 13:10:10 kre Exp $"); 38 1.9 thorpej #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.27 hubertf #include <ctype.h> 42 1.15 itojun #include <stdarg.h> 43 1.15 itojun 44 1.1 cgd #include "telnetd.h" 45 1.12 thorpej 46 1.25 perry static int envvarok(char *); 47 1.11 mrg 48 1.1 cgd int not42 = 1; 49 1.1 cgd 50 1.1 cgd /* 51 1.1 cgd * Buffer for sub-options, and macros 52 1.1 cgd * for suboptions buffer manipulations 53 1.1 cgd */ 54 1.12 thorpej unsigned char subbuffer[4096], *subpointer= subbuffer, *subend= subbuffer; 55 1.1 cgd 56 1.5 cgd #define SB_CLEAR() subpointer = subbuffer 57 1.1 cgd #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 58 1.1 cgd #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 59 1.1 cgd *subpointer++ = (c); \ 60 1.1 cgd } 61 1.1 cgd #define SB_GET() ((*subpointer++)&0xff) 62 1.1 cgd #define SB_EOF() (subpointer >= subend) 63 1.1 cgd #define SB_LEN() (subend - subpointer) 64 1.1 cgd 65 1.5 cgd #ifdef ENV_HACK 66 1.5 cgd unsigned char *subsave; 67 1.5 cgd #define SB_SAVE() subsave = subpointer; 68 1.5 cgd #define SB_RESTORE() subpointer = subsave; 69 1.5 cgd #endif 70 1.1 cgd 71 1.1 cgd 72 1.1 cgd /* 73 1.1 cgd * State for recv fsm 74 1.1 cgd */ 75 1.1 cgd #define TS_DATA 0 /* base state */ 76 1.1 cgd #define TS_IAC 1 /* look for double IAC's */ 77 1.1 cgd #define TS_CR 2 /* CR-LF ->'s CR */ 78 1.1 cgd #define TS_SB 3 /* throw away begin's... */ 79 1.1 cgd #define TS_SE 4 /* ...end's (suboption negotiation) */ 80 1.1 cgd #define TS_WILL 5 /* will option negotiation */ 81 1.1 cgd #define TS_WONT 6 /* wont " */ 82 1.1 cgd #define TS_DO 7 /* do " */ 83 1.1 cgd #define TS_DONT 8 /* dont " */ 84 1.1 cgd 85 1.23 itojun void 86 1.25 perry telrcv(void) 87 1.1 cgd { 88 1.25 perry int c; 89 1.1 cgd static int state = TS_DATA; 90 1.1 cgd 91 1.1 cgd while (ncc > 0) { 92 1.1 cgd if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 93 1.1 cgd break; 94 1.1 cgd c = *netip++ & 0377, ncc--; 95 1.12 thorpej #ifdef ENCRYPTION 96 1.12 thorpej if (decrypt_input) 97 1.12 thorpej c = (*decrypt_input)(c); 98 1.12 thorpej #endif /* ENCRYPTION */ 99 1.1 cgd switch (state) { 100 1.1 cgd 101 1.1 cgd case TS_CR: 102 1.1 cgd state = TS_DATA; 103 1.33 shm 104 1.33 shm #ifdef LINEMODE 105 1.33 shm /* 106 1.33 shm * If we are operating in linemode, 107 1.33 shm * convert to local end-of-line. 108 1.33 shm */ 109 1.33 shm if (linemode && (ncc > 0) && ((c == '\n') || 110 1.33 shm ((c == 0) && tty_iscrnl())) ) 111 1.33 shm c = '\n'; 112 1.33 shm else 113 1.33 shm #endif 114 1.33 shm { 115 1.33 shm /* 116 1.33 shm * We now map \r\n ==> \r for pragmatic reasons. 117 1.33 shm * Many client implementations send \r\n when 118 1.33 shm * the user hits the CarriageReturn key. 119 1.33 shm * 120 1.33 shm * We USED to map \r\n ==> \n, since \r\n says 121 1.33 shm * that we want to be in column 1 of the next 122 1.33 shm * printable line, and \n is the standard 123 1.33 shm * unix way of saying that (\r is only good 124 1.33 shm * if CRMOD is set, which it normally is). 125 1.33 shm */ 126 1.33 shm 127 1.33 shm /* Strip off \n or \0 after a \r */ 128 1.33 shm if ((c == 0) || (c == '\n')) 129 1.33 shm break; 130 1.1 cgd } 131 1.1 cgd /* FALL THROUGH */ 132 1.1 cgd 133 1.1 cgd case TS_DATA: 134 1.1 cgd if (c == IAC) { 135 1.1 cgd state = TS_IAC; 136 1.1 cgd break; 137 1.1 cgd } 138 1.33 shm 139 1.33 shm if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) 140 1.33 shm state = TS_CR; 141 1.33 shm 142 1.1 cgd *pfrontp++ = c; 143 1.1 cgd break; 144 1.1 cgd 145 1.1 cgd case TS_IAC: 146 1.1 cgd gotiac: switch (c) { 147 1.1 cgd 148 1.1 cgd /* 149 1.1 cgd * Send the process on the pty side an 150 1.1 cgd * interrupt. Do this with a NULL or 151 1.1 cgd * interrupt char; depending on the tty mode. 152 1.1 cgd */ 153 1.1 cgd case IP: 154 1.1 cgd DIAG(TD_OPTIONS, 155 1.1 cgd printoption("td: recv IAC", c)); 156 1.1 cgd interrupt(); 157 1.1 cgd break; 158 1.1 cgd 159 1.1 cgd case BREAK: 160 1.1 cgd DIAG(TD_OPTIONS, 161 1.1 cgd printoption("td: recv IAC", c)); 162 1.1 cgd sendbrk(); 163 1.1 cgd break; 164 1.1 cgd 165 1.1 cgd /* 166 1.1 cgd * Are You There? 167 1.1 cgd */ 168 1.1 cgd case AYT: 169 1.1 cgd DIAG(TD_OPTIONS, 170 1.1 cgd printoption("td: recv IAC", c)); 171 1.1 cgd recv_ayt(); 172 1.1 cgd break; 173 1.1 cgd 174 1.1 cgd /* 175 1.1 cgd * Abort Output 176 1.1 cgd */ 177 1.1 cgd case AO: 178 1.1 cgd { 179 1.1 cgd DIAG(TD_OPTIONS, 180 1.1 cgd printoption("td: recv IAC", c)); 181 1.1 cgd ptyflush(); /* half-hearted */ 182 1.1 cgd init_termbuf(); 183 1.1 cgd 184 1.1 cgd if (slctab[SLC_AO].sptr && 185 1.1 cgd *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { 186 1.1 cgd *pfrontp++ = 187 1.1 cgd (unsigned char)*slctab[SLC_AO].sptr; 188 1.1 cgd } 189 1.1 cgd 190 1.1 cgd netclear(); /* clear buffer back */ 191 1.15 itojun output_data("%c%c", IAC, DM); 192 1.15 itojun neturg = nfrontp - 1; /* off by one XXX */ 193 1.1 cgd DIAG(TD_OPTIONS, 194 1.1 cgd printoption("td: send IAC", DM)); 195 1.1 cgd break; 196 1.1 cgd } 197 1.1 cgd 198 1.1 cgd /* 199 1.1 cgd * Erase Character and 200 1.1 cgd * Erase Line 201 1.1 cgd */ 202 1.1 cgd case EC: 203 1.1 cgd case EL: 204 1.1 cgd { 205 1.1 cgd cc_t ch; 206 1.1 cgd 207 1.1 cgd DIAG(TD_OPTIONS, 208 1.1 cgd printoption("td: recv IAC", c)); 209 1.1 cgd ptyflush(); /* half-hearted */ 210 1.1 cgd init_termbuf(); 211 1.1 cgd if (c == EC) 212 1.1 cgd ch = *slctab[SLC_EC].sptr; 213 1.1 cgd else 214 1.1 cgd ch = *slctab[SLC_EL].sptr; 215 1.1 cgd if (ch != (cc_t)(_POSIX_VDISABLE)) 216 1.1 cgd *pfrontp++ = (unsigned char)ch; 217 1.1 cgd break; 218 1.1 cgd } 219 1.1 cgd 220 1.1 cgd /* 221 1.1 cgd * Check for urgent data... 222 1.1 cgd */ 223 1.1 cgd case DM: 224 1.1 cgd DIAG(TD_OPTIONS, 225 1.1 cgd printoption("td: recv IAC", c)); 226 1.1 cgd SYNCHing = stilloob(net); 227 1.1 cgd settimer(gotDM); 228 1.1 cgd break; 229 1.1 cgd 230 1.1 cgd 231 1.1 cgd /* 232 1.1 cgd * Begin option subnegotiation... 233 1.1 cgd */ 234 1.1 cgd case SB: 235 1.1 cgd state = TS_SB; 236 1.1 cgd SB_CLEAR(); 237 1.1 cgd continue; 238 1.1 cgd 239 1.1 cgd case WILL: 240 1.1 cgd state = TS_WILL; 241 1.1 cgd continue; 242 1.1 cgd 243 1.1 cgd case WONT: 244 1.1 cgd state = TS_WONT; 245 1.1 cgd continue; 246 1.1 cgd 247 1.1 cgd case DO: 248 1.1 cgd state = TS_DO; 249 1.1 cgd continue; 250 1.1 cgd 251 1.1 cgd case DONT: 252 1.1 cgd state = TS_DONT; 253 1.1 cgd continue; 254 1.1 cgd case EOR: 255 1.1 cgd if (his_state_is_will(TELOPT_EOR)) 256 1.1 cgd doeof(); 257 1.1 cgd break; 258 1.1 cgd 259 1.1 cgd /* 260 1.1 cgd * Handle RFC 10xx Telnet linemode option additions 261 1.1 cgd * to command stream (EOF, SUSP, ABORT). 262 1.1 cgd */ 263 1.1 cgd case xEOF: 264 1.1 cgd doeof(); 265 1.1 cgd break; 266 1.1 cgd 267 1.1 cgd case SUSP: 268 1.1 cgd sendsusp(); 269 1.1 cgd break; 270 1.1 cgd 271 1.1 cgd case ABORT: 272 1.1 cgd sendbrk(); 273 1.1 cgd break; 274 1.1 cgd 275 1.1 cgd case IAC: 276 1.1 cgd *pfrontp++ = c; 277 1.1 cgd break; 278 1.1 cgd } 279 1.1 cgd state = TS_DATA; 280 1.1 cgd break; 281 1.1 cgd 282 1.1 cgd case TS_SB: 283 1.1 cgd if (c == IAC) { 284 1.1 cgd state = TS_SE; 285 1.1 cgd } else { 286 1.1 cgd SB_ACCUM(c); 287 1.1 cgd } 288 1.1 cgd break; 289 1.1 cgd 290 1.1 cgd case TS_SE: 291 1.1 cgd if (c != SE) { 292 1.1 cgd if (c != IAC) { 293 1.1 cgd /* 294 1.1 cgd * bad form of suboption negotiation. 295 1.1 cgd * handle it in such a way as to avoid 296 1.1 cgd * damage to local state. Parse 297 1.1 cgd * suboption buffer found so far, 298 1.1 cgd * then treat remaining stream as 299 1.1 cgd * another command sequence. 300 1.1 cgd */ 301 1.1 cgd 302 1.1 cgd /* for DIAGNOSTICS */ 303 1.1 cgd SB_ACCUM(IAC); 304 1.1 cgd SB_ACCUM(c); 305 1.1 cgd subpointer -= 2; 306 1.1 cgd 307 1.1 cgd SB_TERM(); 308 1.1 cgd suboption(); 309 1.1 cgd state = TS_IAC; 310 1.1 cgd goto gotiac; 311 1.1 cgd } 312 1.1 cgd SB_ACCUM(c); 313 1.1 cgd state = TS_SB; 314 1.1 cgd } else { 315 1.1 cgd /* for DIAGNOSTICS */ 316 1.1 cgd SB_ACCUM(IAC); 317 1.1 cgd SB_ACCUM(SE); 318 1.1 cgd subpointer -= 2; 319 1.1 cgd 320 1.1 cgd SB_TERM(); 321 1.1 cgd suboption(); /* handle sub-option */ 322 1.1 cgd state = TS_DATA; 323 1.1 cgd } 324 1.1 cgd break; 325 1.1 cgd 326 1.1 cgd case TS_WILL: 327 1.1 cgd willoption(c); 328 1.1 cgd state = TS_DATA; 329 1.1 cgd continue; 330 1.1 cgd 331 1.1 cgd case TS_WONT: 332 1.1 cgd wontoption(c); 333 1.1 cgd state = TS_DATA; 334 1.1 cgd continue; 335 1.1 cgd 336 1.1 cgd case TS_DO: 337 1.1 cgd dooption(c); 338 1.1 cgd state = TS_DATA; 339 1.1 cgd continue; 340 1.1 cgd 341 1.1 cgd case TS_DONT: 342 1.1 cgd dontoption(c); 343 1.1 cgd state = TS_DATA; 344 1.1 cgd continue; 345 1.1 cgd 346 1.1 cgd default: 347 1.20 wiz syslog(LOG_ERR, "panic state=%d", state); 348 1.1 cgd printf("telnetd: panic state=%d\n", state); 349 1.1 cgd exit(1); 350 1.1 cgd } 351 1.1 cgd } 352 1.1 cgd } /* end of telrcv */ 353 1.1 cgd 354 1.1 cgd /* 355 1.1 cgd * The will/wont/do/dont state machines are based on Dave Borman's 356 1.1 cgd * Telnet option processing state machine. 357 1.1 cgd * 358 1.1 cgd * These correspond to the following states: 359 1.1 cgd * my_state = the last negotiated state 360 1.1 cgd * want_state = what I want the state to go to 361 1.1 cgd * want_resp = how many requests I have sent 362 1.1 cgd * All state defaults are negative, and resp defaults to 0. 363 1.1 cgd * 364 1.1 cgd * When initiating a request to change state to new_state: 365 1.8 jtk * 366 1.1 cgd * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { 367 1.1 cgd * do nothing; 368 1.1 cgd * } else { 369 1.1 cgd * want_state = new_state; 370 1.1 cgd * send new_state; 371 1.1 cgd * want_resp++; 372 1.1 cgd * } 373 1.1 cgd * 374 1.1 cgd * When receiving new_state: 375 1.1 cgd * 376 1.1 cgd * if (want_resp) { 377 1.1 cgd * want_resp--; 378 1.1 cgd * if (want_resp && (new_state == my_state)) 379 1.1 cgd * want_resp--; 380 1.1 cgd * } 381 1.1 cgd * if ((want_resp == 0) && (new_state != want_state)) { 382 1.1 cgd * if (ok_to_switch_to new_state) 383 1.1 cgd * want_state = new_state; 384 1.1 cgd * else 385 1.1 cgd * want_resp++; 386 1.1 cgd * send want_state; 387 1.1 cgd * } 388 1.1 cgd * my_state = new_state; 389 1.1 cgd * 390 1.1 cgd * Note that new_state is implied in these functions by the function itself. 391 1.1 cgd * will and do imply positive new_state, wont and dont imply negative. 392 1.1 cgd * 393 1.1 cgd * Finally, there is one catch. If we send a negative response to a 394 1.1 cgd * positive request, my_state will be the positive while want_state will 395 1.1 cgd * remain negative. my_state will revert to negative when the negative 396 1.30 maya * acknowledgment arrives from the peer. Thus, my_state generally tells 397 1.1 cgd * us not only the last negotiated state, but also tells us what the peer 398 1.1 cgd * wants to be doing as well. It is important to understand this difference 399 1.1 cgd * as we may wish to be processing data streams based on our desired state 400 1.1 cgd * (want_state) or based on what the peer thinks the state is (my_state). 401 1.1 cgd * 402 1.1 cgd * This all works fine because if the peer sends a positive request, the data 403 1.30 maya * that we receive prior to negative acknowledgment will probably be affected 404 1.1 cgd * by the positive state, and we can process it as such (if we can; if we 405 1.1 cgd * can't then it really doesn't matter). If it is that important, then the 406 1.1 cgd * peer probably should be buffering until this option state negotiation 407 1.1 cgd * is complete. 408 1.1 cgd * 409 1.1 cgd */ 410 1.23 itojun void 411 1.25 perry send_do(int option, int init) 412 1.1 cgd { 413 1.1 cgd if (init) { 414 1.1 cgd if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || 415 1.1 cgd his_want_state_is_will(option)) 416 1.1 cgd return; 417 1.1 cgd /* 418 1.1 cgd * Special case for TELOPT_TM: We send a DO, but pretend 419 1.1 cgd * that we sent a DONT, so that we can send more DOs if 420 1.1 cgd * we want to. 421 1.1 cgd */ 422 1.1 cgd if (option == TELOPT_TM) 423 1.1 cgd set_his_want_state_wont(option); 424 1.1 cgd else 425 1.1 cgd set_his_want_state_will(option); 426 1.1 cgd do_dont_resp[option]++; 427 1.1 cgd } 428 1.29 christos (void) output_data("%c%c%c", IAC, DO, option); 429 1.1 cgd 430 1.1 cgd DIAG(TD_OPTIONS, printoption("td: send do", option)); 431 1.1 cgd } 432 1.1 cgd 433 1.14 christos #ifdef LINEMODE 434 1.25 perry extern void doclientstat(void); 435 1.14 christos #endif 436 1.14 christos #if 0 437 1.5 cgd #ifdef AUTHENTICATION 438 1.25 perry extern void auth_request(void); /* libtelnet */ 439 1.1 cgd #endif 440 1.12 thorpej #ifdef ENCRYPTION 441 1.25 perry extern void encrypt_send_support(void); 442 1.12 thorpej #endif /* ENCRYPTION */ 443 1.14 christos #endif 444 1.1 cgd 445 1.23 itojun void 446 1.25 perry willoption(int option) 447 1.1 cgd { 448 1.1 cgd int changeok = 0; 449 1.25 perry void (*func)(void) = 0; 450 1.1 cgd 451 1.1 cgd /* 452 1.1 cgd * process input from peer. 453 1.1 cgd */ 454 1.1 cgd 455 1.1 cgd DIAG(TD_OPTIONS, printoption("td: recv will", option)); 456 1.1 cgd 457 1.1 cgd if (do_dont_resp[option]) { 458 1.1 cgd do_dont_resp[option]--; 459 1.1 cgd if (do_dont_resp[option] && his_state_is_will(option)) 460 1.1 cgd do_dont_resp[option]--; 461 1.1 cgd } 462 1.1 cgd if (do_dont_resp[option] == 0) { 463 1.1 cgd if (his_want_state_is_wont(option)) { 464 1.1 cgd switch (option) { 465 1.1 cgd 466 1.1 cgd case TELOPT_BINARY: 467 1.1 cgd init_termbuf(); 468 1.1 cgd tty_binaryin(1); 469 1.1 cgd set_termbuf(); 470 1.1 cgd changeok++; 471 1.1 cgd break; 472 1.1 cgd 473 1.1 cgd case TELOPT_ECHO: 474 1.1 cgd /* 475 1.1 cgd * See comments below for more info. 476 1.1 cgd */ 477 1.1 cgd not42 = 0; /* looks like a 4.2 system */ 478 1.1 cgd break; 479 1.1 cgd 480 1.1 cgd case TELOPT_TM: 481 1.1 cgd #if defined(LINEMODE) && defined(KLUDGELINEMODE) 482 1.1 cgd /* 483 1.1 cgd * This telnetd implementation does not really 484 1.1 cgd * support timing marks, it just uses them to 485 1.1 cgd * support the kludge linemode stuff. If we 486 1.1 cgd * receive a will or wont TM in response to our 487 1.1 cgd * do TM request that may have been sent to 488 1.1 cgd * determine kludge linemode support, process 489 1.1 cgd * it, otherwise TM should get a negative 490 1.1 cgd * response back. 491 1.1 cgd */ 492 1.1 cgd /* 493 1.1 cgd * Handle the linemode kludge stuff. 494 1.1 cgd * If we are not currently supporting any 495 1.1 cgd * linemode at all, then we assume that this 496 1.1 cgd * is the client telling us to use kludge 497 1.1 cgd * linemode in response to our query. Set the 498 1.1 cgd * linemode type that is to be supported, note 499 1.1 cgd * that the client wishes to use linemode, and 500 1.1 cgd * eat the will TM as though it never arrived. 501 1.1 cgd */ 502 1.1 cgd if (lmodetype < KLUDGE_LINEMODE) { 503 1.1 cgd lmodetype = KLUDGE_LINEMODE; 504 1.1 cgd clientstat(TELOPT_LINEMODE, WILL, 0); 505 1.1 cgd send_wont(TELOPT_SGA, 1); 506 1.5 cgd } else if (lmodetype == NO_AUTOKLUDGE) { 507 1.5 cgd lmodetype = KLUDGE_OK; 508 1.1 cgd } 509 1.1 cgd #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 510 1.1 cgd /* 511 1.1 cgd * We never respond to a WILL TM, and 512 1.1 cgd * we leave the state WONT. 513 1.1 cgd */ 514 1.1 cgd return; 515 1.1 cgd 516 1.1 cgd case TELOPT_LFLOW: 517 1.1 cgd /* 518 1.1 cgd * If we are going to support flow control 519 1.1 cgd * option, then don't worry peer that we can't 520 1.1 cgd * change the flow control characters. 521 1.1 cgd */ 522 1.1 cgd slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 523 1.1 cgd slctab[SLC_XON].defset.flag |= SLC_DEFAULT; 524 1.1 cgd slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 525 1.1 cgd slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; 526 1.31 mrg /* FALLTHROUGH */ 527 1.1 cgd case TELOPT_TTYPE: 528 1.1 cgd case TELOPT_SGA: 529 1.1 cgd case TELOPT_NAWS: 530 1.1 cgd case TELOPT_TSPEED: 531 1.1 cgd case TELOPT_XDISPLOC: 532 1.5 cgd case TELOPT_NEW_ENVIRON: 533 1.5 cgd case TELOPT_OLD_ENVIRON: 534 1.1 cgd changeok++; 535 1.1 cgd break; 536 1.1 cgd 537 1.1 cgd #ifdef LINEMODE 538 1.1 cgd case TELOPT_LINEMODE: 539 1.1 cgd # ifdef KLUDGELINEMODE 540 1.1 cgd /* 541 1.1 cgd * Note client's desire to use linemode. 542 1.1 cgd */ 543 1.1 cgd lmodetype = REAL_LINEMODE; 544 1.1 cgd # endif /* KLUDGELINEMODE */ 545 1.1 cgd func = doclientstat; 546 1.1 cgd changeok++; 547 1.1 cgd break; 548 1.1 cgd #endif /* LINEMODE */ 549 1.1 cgd 550 1.5 cgd #ifdef AUTHENTICATION 551 1.1 cgd case TELOPT_AUTHENTICATION: 552 1.35 kre if (auth_level >= 0) { 553 1.35 kre func = auth_request; 554 1.35 kre changeok++; 555 1.35 kre } 556 1.1 cgd break; 557 1.1 cgd #endif 558 1.1 cgd 559 1.12 thorpej #ifdef ENCRYPTION 560 1.12 thorpej case TELOPT_ENCRYPT: 561 1.12 thorpej func = encrypt_send_support; 562 1.12 thorpej changeok++; 563 1.12 thorpej break; 564 1.12 thorpej #endif /* ENCRYPTION */ 565 1.1 cgd 566 1.1 cgd default: 567 1.1 cgd break; 568 1.1 cgd } 569 1.1 cgd if (changeok) { 570 1.1 cgd set_his_want_state_will(option); 571 1.1 cgd send_do(option, 0); 572 1.1 cgd } else { 573 1.1 cgd do_dont_resp[option]++; 574 1.1 cgd send_dont(option, 0); 575 1.1 cgd } 576 1.1 cgd } else { 577 1.1 cgd /* 578 1.1 cgd * Option processing that should happen when 579 1.1 cgd * we receive conformation of a change in 580 1.1 cgd * state that we had requested. 581 1.1 cgd */ 582 1.1 cgd switch (option) { 583 1.1 cgd case TELOPT_ECHO: 584 1.1 cgd not42 = 0; /* looks like a 4.2 system */ 585 1.1 cgd /* 586 1.1 cgd * Egads, he responded "WILL ECHO". Turn 587 1.1 cgd * it off right now! 588 1.1 cgd */ 589 1.1 cgd send_dont(option, 1); 590 1.1 cgd /* 591 1.1 cgd * "WILL ECHO". Kludge upon kludge! 592 1.1 cgd * A 4.2 client is now echoing user input at 593 1.1 cgd * the tty. This is probably undesireable and 594 1.1 cgd * it should be stopped. The client will 595 1.1 cgd * respond WONT TM to the DO TM that we send to 596 1.1 cgd * check for kludge linemode. When the WONT TM 597 1.1 cgd * arrives, linemode will be turned off and a 598 1.34 andvar * change propagated to the pty. This change 599 1.1 cgd * will cause us to process the new pty state 600 1.1 cgd * in localstat(), which will notice that 601 1.1 cgd * linemode is off and send a WILL ECHO 602 1.1 cgd * so that we are properly in character mode and 603 1.1 cgd * all is well. 604 1.1 cgd */ 605 1.1 cgd break; 606 1.1 cgd #ifdef LINEMODE 607 1.1 cgd case TELOPT_LINEMODE: 608 1.1 cgd # ifdef KLUDGELINEMODE 609 1.1 cgd /* 610 1.1 cgd * Note client's desire to use linemode. 611 1.1 cgd */ 612 1.1 cgd lmodetype = REAL_LINEMODE; 613 1.1 cgd # endif /* KLUDGELINEMODE */ 614 1.1 cgd func = doclientstat; 615 1.1 cgd break; 616 1.1 cgd #endif /* LINEMODE */ 617 1.1 cgd 618 1.5 cgd #ifdef AUTHENTICATION 619 1.1 cgd case TELOPT_AUTHENTICATION: 620 1.1 cgd func = auth_request; 621 1.1 cgd break; 622 1.1 cgd #endif 623 1.1 cgd 624 1.12 thorpej #ifdef ENCRYPTION 625 1.12 thorpej case TELOPT_ENCRYPT: 626 1.12 thorpej func = encrypt_send_support; 627 1.12 thorpej break; 628 1.12 thorpej #endif /* ENCRYPTION */ 629 1.12 thorpej 630 1.5 cgd case TELOPT_LFLOW: 631 1.5 cgd func = flowstat; 632 1.1 cgd break; 633 1.1 cgd } 634 1.1 cgd } 635 1.1 cgd } 636 1.1 cgd set_his_state_will(option); 637 1.1 cgd if (func) 638 1.1 cgd (*func)(); 639 1.1 cgd } /* end of willoption */ 640 1.1 cgd 641 1.23 itojun void 642 1.25 perry send_dont(int option, int init) 643 1.1 cgd { 644 1.1 cgd if (init) { 645 1.1 cgd if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || 646 1.1 cgd his_want_state_is_wont(option)) 647 1.1 cgd return; 648 1.1 cgd set_his_want_state_wont(option); 649 1.1 cgd do_dont_resp[option]++; 650 1.1 cgd } 651 1.29 christos (void) output_data("%c%c%c", IAC, DONT, option); 652 1.1 cgd 653 1.1 cgd DIAG(TD_OPTIONS, printoption("td: send dont", option)); 654 1.1 cgd } 655 1.1 cgd 656 1.23 itojun void 657 1.25 perry wontoption(int option) 658 1.1 cgd { 659 1.1 cgd /* 660 1.1 cgd * Process client input. 661 1.1 cgd */ 662 1.1 cgd 663 1.1 cgd DIAG(TD_OPTIONS, printoption("td: recv wont", option)); 664 1.1 cgd 665 1.1 cgd if (do_dont_resp[option]) { 666 1.1 cgd do_dont_resp[option]--; 667 1.1 cgd if (do_dont_resp[option] && his_state_is_wont(option)) 668 1.1 cgd do_dont_resp[option]--; 669 1.1 cgd } 670 1.1 cgd if (do_dont_resp[option] == 0) { 671 1.1 cgd if (his_want_state_is_will(option)) { 672 1.1 cgd /* it is always ok to change to negative state */ 673 1.1 cgd switch (option) { 674 1.1 cgd case TELOPT_ECHO: 675 1.1 cgd not42 = 1; /* doesn't seem to be a 4.2 system */ 676 1.1 cgd break; 677 1.1 cgd 678 1.1 cgd case TELOPT_BINARY: 679 1.1 cgd init_termbuf(); 680 1.1 cgd tty_binaryin(0); 681 1.1 cgd set_termbuf(); 682 1.1 cgd break; 683 1.1 cgd 684 1.1 cgd #ifdef LINEMODE 685 1.1 cgd case TELOPT_LINEMODE: 686 1.1 cgd # ifdef KLUDGELINEMODE 687 1.1 cgd /* 688 1.1 cgd * If real linemode is supported, then client is 689 1.1 cgd * asking to turn linemode off. 690 1.1 cgd */ 691 1.1 cgd if (lmodetype != REAL_LINEMODE) 692 1.1 cgd break; 693 1.12 thorpej /* XXX double-check this --thorpej */ 694 1.12 thorpej lmodetype = KLUDGE_LINEMODE; 695 1.1 cgd # endif /* KLUDGELINEMODE */ 696 1.1 cgd clientstat(TELOPT_LINEMODE, WONT, 0); 697 1.1 cgd break; 698 1.1 cgd #endif /* LINEMODE */ 699 1.1 cgd 700 1.1 cgd case TELOPT_TM: 701 1.1 cgd /* 702 1.1 cgd * If we get a WONT TM, and had sent a DO TM, 703 1.1 cgd * don't respond with a DONT TM, just leave it 704 1.1 cgd * as is. Short circut the state machine to 705 1.22 wiz * achieve this. 706 1.1 cgd */ 707 1.1 cgd set_his_want_state_wont(TELOPT_TM); 708 1.1 cgd return; 709 1.1 cgd 710 1.1 cgd case TELOPT_LFLOW: 711 1.1 cgd /* 712 1.1 cgd * If we are not going to support flow control 713 1.1 cgd * option, then let peer know that we can't 714 1.1 cgd * change the flow control characters. 715 1.1 cgd */ 716 1.1 cgd slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; 717 1.1 cgd slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; 718 1.1 cgd slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; 719 1.1 cgd slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; 720 1.1 cgd break; 721 1.1 cgd 722 1.23 itojun #ifdef AUTHENTICATION 723 1.1 cgd case TELOPT_AUTHENTICATION: 724 1.1 cgd auth_finished(0, AUTH_REJECT); 725 1.1 cgd break; 726 1.1 cgd #endif 727 1.1 cgd 728 1.1 cgd /* 729 1.1 cgd * For options that we might spin waiting for 730 1.1 cgd * sub-negotiation, if the client turns off the 731 1.1 cgd * option rather than responding to the request, 732 1.1 cgd * we have to treat it here as if we got a response 733 1.1 cgd * to the sub-negotiation, (by updating the timers) 734 1.1 cgd * so that we'll break out of the loop. 735 1.1 cgd */ 736 1.1 cgd case TELOPT_TTYPE: 737 1.1 cgd settimer(ttypesubopt); 738 1.1 cgd break; 739 1.1 cgd 740 1.1 cgd case TELOPT_TSPEED: 741 1.1 cgd settimer(tspeedsubopt); 742 1.1 cgd break; 743 1.1 cgd 744 1.1 cgd case TELOPT_XDISPLOC: 745 1.1 cgd settimer(xdisplocsubopt); 746 1.1 cgd break; 747 1.1 cgd 748 1.5 cgd case TELOPT_OLD_ENVIRON: 749 1.5 cgd settimer(oenvironsubopt); 750 1.5 cgd break; 751 1.5 cgd 752 1.5 cgd case TELOPT_NEW_ENVIRON: 753 1.1 cgd settimer(environsubopt); 754 1.1 cgd break; 755 1.1 cgd 756 1.1 cgd default: 757 1.1 cgd break; 758 1.1 cgd } 759 1.1 cgd set_his_want_state_wont(option); 760 1.1 cgd if (his_state_is_will(option)) 761 1.1 cgd send_dont(option, 0); 762 1.1 cgd } else { 763 1.1 cgd switch (option) { 764 1.1 cgd case TELOPT_TM: 765 1.1 cgd #if defined(LINEMODE) && defined(KLUDGELINEMODE) 766 1.5 cgd if (lmodetype < NO_AUTOKLUDGE) { 767 1.1 cgd lmodetype = NO_LINEMODE; 768 1.1 cgd clientstat(TELOPT_LINEMODE, WONT, 0); 769 1.1 cgd send_will(TELOPT_SGA, 1); 770 1.1 cgd send_will(TELOPT_ECHO, 1); 771 1.1 cgd } 772 1.1 cgd #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 773 1.1 cgd break; 774 1.1 cgd 775 1.23 itojun #ifdef AUTHENTICATION 776 1.1 cgd case TELOPT_AUTHENTICATION: 777 1.1 cgd auth_finished(0, AUTH_REJECT); 778 1.1 cgd break; 779 1.1 cgd #endif 780 1.1 cgd default: 781 1.1 cgd break; 782 1.1 cgd } 783 1.1 cgd } 784 1.1 cgd } 785 1.1 cgd set_his_state_wont(option); 786 1.1 cgd 787 1.1 cgd } /* end of wontoption */ 788 1.1 cgd 789 1.23 itojun void 790 1.25 perry send_will(int option, int init) 791 1.1 cgd { 792 1.1 cgd if (init) { 793 1.1 cgd if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| 794 1.1 cgd my_want_state_is_will(option)) 795 1.1 cgd return; 796 1.1 cgd set_my_want_state_will(option); 797 1.1 cgd will_wont_resp[option]++; 798 1.1 cgd } 799 1.29 christos (void) output_data("%c%c%c", IAC, WILL, option); 800 1.1 cgd 801 1.1 cgd DIAG(TD_OPTIONS, printoption("td: send will", option)); 802 1.1 cgd } 803 1.1 cgd 804 1.1 cgd #if !defined(LINEMODE) || !defined(KLUDGELINEMODE) 805 1.1 cgd /* 806 1.1 cgd * When we get a DONT SGA, we will try once to turn it 807 1.1 cgd * back on. If the other side responds DONT SGA, we 808 1.1 cgd * leave it at that. This is so that when we talk to 809 1.1 cgd * clients that understand KLUDGELINEMODE but not LINEMODE, 810 1.1 cgd * we'll keep them in char-at-a-time mode. 811 1.1 cgd */ 812 1.1 cgd int turn_on_sga = 0; 813 1.1 cgd #endif 814 1.1 cgd 815 1.23 itojun void 816 1.25 perry dooption(int option) 817 1.1 cgd { 818 1.1 cgd int changeok = 0; 819 1.1 cgd 820 1.1 cgd /* 821 1.1 cgd * Process client input. 822 1.1 cgd */ 823 1.1 cgd 824 1.1 cgd DIAG(TD_OPTIONS, printoption("td: recv do", option)); 825 1.1 cgd 826 1.1 cgd if (will_wont_resp[option]) { 827 1.1 cgd will_wont_resp[option]--; 828 1.1 cgd if (will_wont_resp[option] && my_state_is_will(option)) 829 1.1 cgd will_wont_resp[option]--; 830 1.1 cgd } 831 1.1 cgd if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { 832 1.1 cgd switch (option) { 833 1.1 cgd case TELOPT_ECHO: 834 1.1 cgd #ifdef LINEMODE 835 1.1 cgd # ifdef KLUDGELINEMODE 836 1.1 cgd if (lmodetype == NO_LINEMODE) 837 1.1 cgd # else 838 1.1 cgd if (his_state_is_wont(TELOPT_LINEMODE)) 839 1.1 cgd # endif 840 1.1 cgd #endif 841 1.1 cgd { 842 1.1 cgd init_termbuf(); 843 1.1 cgd tty_setecho(1); 844 1.1 cgd set_termbuf(); 845 1.1 cgd } 846 1.1 cgd changeok++; 847 1.1 cgd break; 848 1.1 cgd 849 1.1 cgd case TELOPT_BINARY: 850 1.1 cgd init_termbuf(); 851 1.1 cgd tty_binaryout(1); 852 1.1 cgd set_termbuf(); 853 1.1 cgd changeok++; 854 1.1 cgd break; 855 1.1 cgd 856 1.1 cgd case TELOPT_SGA: 857 1.1 cgd #if defined(LINEMODE) && defined(KLUDGELINEMODE) 858 1.1 cgd /* 859 1.1 cgd * If kludge linemode is in use, then we must 860 1.1 cgd * process an incoming do SGA for linemode 861 1.1 cgd * purposes. 862 1.1 cgd */ 863 1.1 cgd if (lmodetype == KLUDGE_LINEMODE) { 864 1.1 cgd /* 865 1.1 cgd * Receipt of "do SGA" in kludge 866 1.1 cgd * linemode is the peer asking us to 867 1.1 cgd * turn off linemode. Make note of 868 1.1 cgd * the request. 869 1.1 cgd */ 870 1.1 cgd clientstat(TELOPT_LINEMODE, WONT, 0); 871 1.1 cgd /* 872 1.1 cgd * If linemode did not get turned off 873 1.1 cgd * then don't tell peer that we did. 874 1.1 cgd * Breaking here forces a wont SGA to 875 1.1 cgd * be returned. 876 1.1 cgd */ 877 1.1 cgd if (linemode) 878 1.1 cgd break; 879 1.1 cgd } 880 1.1 cgd #else 881 1.1 cgd turn_on_sga = 0; 882 1.1 cgd #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 883 1.1 cgd changeok++; 884 1.1 cgd break; 885 1.1 cgd 886 1.1 cgd case TELOPT_STATUS: 887 1.1 cgd changeok++; 888 1.1 cgd break; 889 1.1 cgd 890 1.1 cgd case TELOPT_TM: 891 1.1 cgd /* 892 1.1 cgd * Special case for TM. We send a WILL, but 893 1.1 cgd * pretend we sent a WONT. 894 1.1 cgd */ 895 1.1 cgd send_will(option, 0); 896 1.1 cgd set_my_want_state_wont(option); 897 1.1 cgd set_my_state_wont(option); 898 1.1 cgd return; 899 1.1 cgd 900 1.1 cgd case TELOPT_LOGOUT: 901 1.1 cgd /* 902 1.1 cgd * When we get a LOGOUT option, respond 903 1.1 cgd * with a WILL LOGOUT, make sure that 904 1.1 cgd * it gets written out to the network, 905 1.1 cgd * and then just go away... 906 1.1 cgd */ 907 1.1 cgd set_my_want_state_will(TELOPT_LOGOUT); 908 1.1 cgd send_will(TELOPT_LOGOUT, 0); 909 1.1 cgd set_my_state_will(TELOPT_LOGOUT); 910 1.1 cgd (void)netflush(); 911 1.1 cgd cleanup(0); 912 1.1 cgd /* NOT REACHED */ 913 1.1 cgd break; 914 1.1 cgd 915 1.12 thorpej #ifdef ENCRYPTION 916 1.12 thorpej case TELOPT_ENCRYPT: 917 1.12 thorpej changeok++; 918 1.12 thorpej break; 919 1.12 thorpej #endif /* ENCRYPTION */ 920 1.12 thorpej 921 1.1 cgd case TELOPT_LINEMODE: 922 1.1 cgd case TELOPT_TTYPE: 923 1.1 cgd case TELOPT_NAWS: 924 1.1 cgd case TELOPT_TSPEED: 925 1.1 cgd case TELOPT_LFLOW: 926 1.1 cgd case TELOPT_XDISPLOC: 927 1.5 cgd case TELOPT_OLD_ENVIRON: 928 1.1 cgd default: 929 1.1 cgd break; 930 1.1 cgd } 931 1.1 cgd if (changeok) { 932 1.1 cgd set_my_want_state_will(option); 933 1.1 cgd send_will(option, 0); 934 1.1 cgd } else { 935 1.1 cgd will_wont_resp[option]++; 936 1.1 cgd send_wont(option, 0); 937 1.1 cgd } 938 1.1 cgd } 939 1.1 cgd set_my_state_will(option); 940 1.1 cgd 941 1.1 cgd } /* end of dooption */ 942 1.1 cgd 943 1.23 itojun void 944 1.25 perry send_wont(int option, int init) 945 1.1 cgd { 946 1.1 cgd if (init) { 947 1.1 cgd if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || 948 1.1 cgd my_want_state_is_wont(option)) 949 1.1 cgd return; 950 1.1 cgd set_my_want_state_wont(option); 951 1.1 cgd will_wont_resp[option]++; 952 1.1 cgd } 953 1.29 christos (void) output_data("%c%c%c", IAC, WONT, option); 954 1.1 cgd 955 1.1 cgd DIAG(TD_OPTIONS, printoption("td: send wont", option)); 956 1.1 cgd } 957 1.1 cgd 958 1.23 itojun void 959 1.25 perry dontoption(int option) 960 1.1 cgd { 961 1.1 cgd /* 962 1.1 cgd * Process client input. 963 1.1 cgd */ 964 1.1 cgd 965 1.1 cgd 966 1.1 cgd DIAG(TD_OPTIONS, printoption("td: recv dont", option)); 967 1.1 cgd 968 1.1 cgd if (will_wont_resp[option]) { 969 1.1 cgd will_wont_resp[option]--; 970 1.1 cgd if (will_wont_resp[option] && my_state_is_wont(option)) 971 1.1 cgd will_wont_resp[option]--; 972 1.1 cgd } 973 1.1 cgd if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { 974 1.1 cgd switch (option) { 975 1.1 cgd case TELOPT_BINARY: 976 1.1 cgd init_termbuf(); 977 1.1 cgd tty_binaryout(0); 978 1.1 cgd set_termbuf(); 979 1.1 cgd break; 980 1.1 cgd 981 1.1 cgd case TELOPT_ECHO: /* we should stop echoing */ 982 1.1 cgd #ifdef LINEMODE 983 1.1 cgd # ifdef KLUDGELINEMODE 984 1.5 cgd if ((lmodetype != REAL_LINEMODE) && 985 1.5 cgd (lmodetype != KLUDGE_LINEMODE)) 986 1.1 cgd # else 987 1.1 cgd if (his_state_is_wont(TELOPT_LINEMODE)) 988 1.1 cgd # endif 989 1.1 cgd #endif 990 1.1 cgd { 991 1.1 cgd init_termbuf(); 992 1.1 cgd tty_setecho(0); 993 1.1 cgd set_termbuf(); 994 1.1 cgd } 995 1.1 cgd break; 996 1.1 cgd 997 1.1 cgd case TELOPT_SGA: 998 1.1 cgd #if defined(LINEMODE) && defined(KLUDGELINEMODE) 999 1.1 cgd /* 1000 1.1 cgd * If kludge linemode is in use, then we 1001 1.1 cgd * must process an incoming do SGA for 1002 1.1 cgd * linemode purposes. 1003 1.1 cgd */ 1004 1.5 cgd if ((lmodetype == KLUDGE_LINEMODE) || 1005 1.5 cgd (lmodetype == KLUDGE_OK)) { 1006 1.1 cgd /* 1007 1.1 cgd * The client is asking us to turn 1008 1.1 cgd * linemode on. 1009 1.1 cgd */ 1010 1.5 cgd lmodetype = KLUDGE_LINEMODE; 1011 1.1 cgd clientstat(TELOPT_LINEMODE, WILL, 0); 1012 1.1 cgd /* 1013 1.1 cgd * If we did not turn line mode on, 1014 1.1 cgd * then what do we say? Will SGA? 1015 1.1 cgd * This violates design of telnet. 1016 1.1 cgd * Gross. Very Gross. 1017 1.1 cgd */ 1018 1.1 cgd } 1019 1.1 cgd break; 1020 1.1 cgd #else 1021 1.1 cgd set_my_want_state_wont(option); 1022 1.1 cgd if (my_state_is_will(option)) 1023 1.1 cgd send_wont(option, 0); 1024 1.1 cgd set_my_state_wont(option); 1025 1.1 cgd if (turn_on_sga ^= 1) 1026 1.5 cgd send_will(option, 1); 1027 1.1 cgd return; 1028 1.1 cgd #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 1029 1.1 cgd 1030 1.1 cgd default: 1031 1.1 cgd break; 1032 1.1 cgd } 1033 1.1 cgd 1034 1.1 cgd set_my_want_state_wont(option); 1035 1.1 cgd if (my_state_is_will(option)) 1036 1.1 cgd send_wont(option, 0); 1037 1.1 cgd } 1038 1.1 cgd set_my_state_wont(option); 1039 1.1 cgd 1040 1.1 cgd } /* end of dontoption */ 1041 1.1 cgd 1042 1.5 cgd #ifdef ENV_HACK 1043 1.5 cgd int env_ovar = -1; 1044 1.5 cgd int env_ovalue = -1; 1045 1.5 cgd #else /* ENV_HACK */ 1046 1.5 cgd # define env_ovar OLD_ENV_VAR 1047 1.5 cgd # define env_ovalue OLD_ENV_VALUE 1048 1.5 cgd #endif /* ENV_HACK */ 1049 1.5 cgd 1050 1.7 ghudson /* envvarok(char*) */ 1051 1.7 ghudson /* check that variable is safe to pass to login or shell */ 1052 1.7 ghudson static int 1053 1.25 perry envvarok(char *varp) 1054 1.7 ghudson { 1055 1.12 thorpej 1056 1.12 thorpej if (strcmp(varp, "TERMCAP") && /* to prevent a security hole */ 1057 1.12 thorpej strcmp(varp, "TERMINFO") && /* with tgetent */ 1058 1.12 thorpej strcmp(varp, "TERMPATH") && 1059 1.12 thorpej strcmp(varp, "HOME") && /* to prevent the tegetent bug */ 1060 1.12 thorpej strncmp(varp, "LD_", strlen("LD_")) && /* most systems */ 1061 1.12 thorpej strncmp(varp, "_RLD_", strlen("_RLD_")) && /* IRIX */ 1062 1.12 thorpej strcmp(varp, "LIBPATH") && /* AIX */ 1063 1.12 thorpej strcmp(varp, "ENV") && 1064 1.12 thorpej strcmp(varp, "BASH_ENV") && 1065 1.12 thorpej strcmp(varp, "IFS") && 1066 1.12 thorpej strncmp(varp, "KRB5", strlen("KRB5")) && /* Krb5 */ 1067 1.12 thorpej /* 1068 1.12 thorpej * The above case is a catch-all for now. Here are some of 1069 1.12 thorpej * the specific ones we must avoid passing, at least until 1070 1.12 thorpej * we can prove it can be done safely. Keep this list 1071 1.12 thorpej * around un case someone wants to remove the catch-all. 1072 1.12 thorpej */ 1073 1.12 thorpej strcmp(varp, "KRB5_CONFIG") && /* Krb5 */ 1074 1.12 thorpej strcmp(varp, "KRB5CCNAME") && /* Krb5 */ 1075 1.12 thorpej strcmp(varp, "KRB5_KTNAME") && /* Krb5 */ 1076 1.12 thorpej strcmp(varp, "KRBTKFILE") && /* Krb4 */ 1077 1.12 thorpej strcmp(varp, "KRB_CONF") && /* CNS 4 */ 1078 1.12 thorpej strcmp(varp, "KRB_REALMS") && /* CNS 4 */ 1079 1.12 thorpej strcmp(varp, "RESOLV_HOST_CONF")) /* Linux */ 1080 1.12 thorpej return (1); 1081 1.12 thorpej else { 1082 1.12 thorpej syslog(LOG_INFO, "Rejected the attempt to modify the " 1083 1.12 thorpej "environment variable \"%s\"", varp); 1084 1.12 thorpej return (0); 1085 1.12 thorpej } 1086 1.7 ghudson } 1087 1.7 ghudson 1088 1.1 cgd /* 1089 1.1 cgd * suboption() 1090 1.1 cgd * 1091 1.1 cgd * Look at the sub-option buffer, and try to be helpful to the other 1092 1.1 cgd * side. 1093 1.1 cgd * 1094 1.1 cgd * Currently we recognize: 1095 1.1 cgd * 1096 1.1 cgd * Terminal type is 1097 1.1 cgd * Linemode 1098 1.1 cgd * Window size 1099 1.1 cgd * Terminal speed 1100 1.1 cgd */ 1101 1.23 itojun void 1102 1.25 perry suboption(void) 1103 1.1 cgd { 1104 1.25 perry int subchar; 1105 1.1 cgd 1106 1.1 cgd DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); 1107 1.1 cgd 1108 1.1 cgd subchar = SB_GET(); 1109 1.1 cgd switch (subchar) { 1110 1.1 cgd case TELOPT_TSPEED: { 1111 1.25 perry int xspeed, rspeed; 1112 1.1 cgd 1113 1.1 cgd if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ 1114 1.1 cgd break; 1115 1.1 cgd 1116 1.1 cgd settimer(tspeedsubopt); 1117 1.1 cgd 1118 1.1 cgd if (SB_EOF() || SB_GET() != TELQUAL_IS) 1119 1.1 cgd return; 1120 1.1 cgd 1121 1.1 cgd xspeed = atoi((char *)subpointer); 1122 1.1 cgd 1123 1.1 cgd while (SB_GET() != ',' && !SB_EOF()); 1124 1.1 cgd if (SB_EOF()) 1125 1.1 cgd return; 1126 1.1 cgd 1127 1.1 cgd rspeed = atoi((char *)subpointer); 1128 1.1 cgd clientstat(TELOPT_TSPEED, xspeed, rspeed); 1129 1.1 cgd 1130 1.1 cgd break; 1131 1.1 cgd 1132 1.1 cgd } /* end of case TELOPT_TSPEED */ 1133 1.1 cgd 1134 1.1 cgd case TELOPT_TTYPE: { /* Yaaaay! */ 1135 1.26 lha char *p; 1136 1.1 cgd 1137 1.1 cgd if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ 1138 1.1 cgd break; 1139 1.1 cgd settimer(ttypesubopt); 1140 1.1 cgd 1141 1.1 cgd if (SB_EOF() || SB_GET() != TELQUAL_IS) { 1142 1.1 cgd return; /* ??? XXX but, this is the most robust */ 1143 1.1 cgd } 1144 1.1 cgd 1145 1.26 lha p = terminaltype; 1146 1.1 cgd 1147 1.26 lha while ((p < (terminaltype + sizeof terminaltype-1)) && 1148 1.1 cgd !SB_EOF()) { 1149 1.25 perry int c; 1150 1.1 cgd 1151 1.1 cgd c = SB_GET(); 1152 1.1 cgd if (isupper(c)) { 1153 1.1 cgd c = tolower(c); 1154 1.1 cgd } 1155 1.26 lha *p++ = c; /* accumulate name */ 1156 1.1 cgd } 1157 1.26 lha *p = 0; 1158 1.1 cgd break; 1159 1.1 cgd } /* end of case TELOPT_TTYPE */ 1160 1.1 cgd 1161 1.1 cgd case TELOPT_NAWS: { 1162 1.25 perry int xwinsize, ywinsize; 1163 1.1 cgd 1164 1.1 cgd if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ 1165 1.1 cgd break; 1166 1.1 cgd 1167 1.1 cgd if (SB_EOF()) 1168 1.1 cgd return; 1169 1.1 cgd xwinsize = SB_GET() << 8; 1170 1.1 cgd if (SB_EOF()) 1171 1.1 cgd return; 1172 1.1 cgd xwinsize |= SB_GET(); 1173 1.1 cgd if (SB_EOF()) 1174 1.1 cgd return; 1175 1.1 cgd ywinsize = SB_GET() << 8; 1176 1.1 cgd if (SB_EOF()) 1177 1.1 cgd return; 1178 1.1 cgd ywinsize |= SB_GET(); 1179 1.1 cgd clientstat(TELOPT_NAWS, xwinsize, ywinsize); 1180 1.1 cgd 1181 1.1 cgd break; 1182 1.1 cgd 1183 1.1 cgd } /* end of case TELOPT_NAWS */ 1184 1.1 cgd 1185 1.1 cgd #ifdef LINEMODE 1186 1.1 cgd case TELOPT_LINEMODE: { 1187 1.25 perry int request; 1188 1.1 cgd 1189 1.1 cgd if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ 1190 1.1 cgd break; 1191 1.1 cgd /* 1192 1.1 cgd * Process linemode suboptions. 1193 1.1 cgd */ 1194 1.1 cgd if (SB_EOF()) 1195 1.1 cgd break; /* garbage was sent */ 1196 1.1 cgd request = SB_GET(); /* get will/wont */ 1197 1.1 cgd 1198 1.1 cgd if (SB_EOF()) 1199 1.1 cgd break; /* another garbage check */ 1200 1.1 cgd 1201 1.20 wiz if (request == LM_SLC) { /* SLC is not preceded by WILL or WONT */ 1202 1.1 cgd /* 1203 1.1 cgd * Process suboption buffer of slc's 1204 1.1 cgd */ 1205 1.1 cgd start_slc(1); 1206 1.28 christos do_opt_slc(subpointer, SB_LEN()); 1207 1.1 cgd (void) end_slc(0); 1208 1.1 cgd break; 1209 1.1 cgd } else if (request == LM_MODE) { 1210 1.1 cgd if (SB_EOF()) 1211 1.1 cgd return; 1212 1.1 cgd useeditmode = SB_GET(); /* get mode flag */ 1213 1.1 cgd clientstat(LM_MODE, 0, 0); 1214 1.1 cgd break; 1215 1.1 cgd } 1216 1.1 cgd 1217 1.1 cgd if (SB_EOF()) 1218 1.1 cgd break; 1219 1.1 cgd switch (SB_GET()) { /* what suboption? */ 1220 1.1 cgd case LM_FORWARDMASK: 1221 1.1 cgd /* 1222 1.1 cgd * According to spec, only server can send request for 1223 1.1 cgd * forwardmask, and client can only return a positive response. 1224 1.1 cgd * So don't worry about it. 1225 1.1 cgd */ 1226 1.1 cgd 1227 1.1 cgd default: 1228 1.1 cgd break; 1229 1.1 cgd } 1230 1.1 cgd break; 1231 1.1 cgd } /* end of case TELOPT_LINEMODE */ 1232 1.1 cgd #endif 1233 1.1 cgd case TELOPT_STATUS: { 1234 1.1 cgd int mode; 1235 1.1 cgd 1236 1.1 cgd if (SB_EOF()) 1237 1.1 cgd break; 1238 1.1 cgd mode = SB_GET(); 1239 1.1 cgd switch (mode) { 1240 1.1 cgd case TELQUAL_SEND: 1241 1.1 cgd if (my_state_is_will(TELOPT_STATUS)) 1242 1.1 cgd send_status(); 1243 1.1 cgd break; 1244 1.1 cgd 1245 1.1 cgd case TELQUAL_IS: 1246 1.1 cgd break; 1247 1.1 cgd 1248 1.1 cgd default: 1249 1.1 cgd break; 1250 1.1 cgd } 1251 1.1 cgd break; 1252 1.1 cgd } /* end of case TELOPT_STATUS */ 1253 1.1 cgd 1254 1.1 cgd case TELOPT_XDISPLOC: { 1255 1.1 cgd if (SB_EOF() || SB_GET() != TELQUAL_IS) 1256 1.1 cgd return; 1257 1.1 cgd settimer(xdisplocsubopt); 1258 1.1 cgd subpointer[SB_LEN()] = '\0'; 1259 1.1 cgd (void)setenv("DISPLAY", (char *)subpointer, 1); 1260 1.1 cgd break; 1261 1.1 cgd } /* end of case TELOPT_XDISPLOC */ 1262 1.1 cgd 1263 1.5 cgd case TELOPT_NEW_ENVIRON: 1264 1.5 cgd case TELOPT_OLD_ENVIRON: { 1265 1.25 perry int c; 1266 1.25 perry char *cp, *varp, *valp; 1267 1.1 cgd 1268 1.1 cgd if (SB_EOF()) 1269 1.1 cgd return; 1270 1.1 cgd c = SB_GET(); 1271 1.5 cgd if (c == TELQUAL_IS) { 1272 1.5 cgd if (subchar == TELOPT_OLD_ENVIRON) 1273 1.5 cgd settimer(oenvironsubopt); 1274 1.5 cgd else 1275 1.5 cgd settimer(environsubopt); 1276 1.5 cgd } else if (c != TELQUAL_INFO) { 1277 1.1 cgd return; 1278 1.5 cgd } 1279 1.5 cgd 1280 1.5 cgd if (subchar == TELOPT_NEW_ENVIRON) { 1281 1.5 cgd while (!SB_EOF()) { 1282 1.5 cgd c = SB_GET(); 1283 1.5 cgd if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) 1284 1.5 cgd break; 1285 1.5 cgd } 1286 1.5 cgd } else 1287 1.5 cgd { 1288 1.5 cgd #ifdef ENV_HACK 1289 1.5 cgd /* 1290 1.5 cgd * We only want to do this if we haven't already decided 1291 1.5 cgd * whether or not the other side has its VALUE and VAR 1292 1.5 cgd * reversed. 1293 1.5 cgd */ 1294 1.5 cgd if (env_ovar < 0) { 1295 1.25 perry int last = -1; /* invalid value */ 1296 1.5 cgd int empty = 0; 1297 1.5 cgd int got_var = 0, got_value = 0, got_uservar = 0; 1298 1.5 cgd 1299 1.5 cgd /* 1300 1.5 cgd * The other side might have its VALUE and VAR values 1301 1.5 cgd * reversed. To be interoperable, we need to determine 1302 1.5 cgd * which way it is. If the first recognized character 1303 1.5 cgd * is a VAR or VALUE, then that will tell us what 1304 1.32 andvar * type of client it is. If the first recognized 1305 1.5 cgd * character is a USERVAR, then we continue scanning 1306 1.5 cgd * the suboption looking for two consecutive 1307 1.5 cgd * VAR or VALUE fields. We should not get two 1308 1.5 cgd * consecutive VALUE fields, so finding two 1309 1.5 cgd * consecutive VALUE or VAR fields will tell us 1310 1.5 cgd * what the client is. 1311 1.5 cgd */ 1312 1.5 cgd SB_SAVE(); 1313 1.5 cgd while (!SB_EOF()) { 1314 1.5 cgd c = SB_GET(); 1315 1.5 cgd switch(c) { 1316 1.5 cgd case OLD_ENV_VAR: 1317 1.5 cgd if (last < 0 || last == OLD_ENV_VAR 1318 1.5 cgd || (empty && (last == OLD_ENV_VALUE))) 1319 1.5 cgd goto env_ovar_ok; 1320 1.5 cgd got_var++; 1321 1.5 cgd last = OLD_ENV_VAR; 1322 1.5 cgd break; 1323 1.5 cgd case OLD_ENV_VALUE: 1324 1.5 cgd if (last < 0 || last == OLD_ENV_VALUE 1325 1.5 cgd || (empty && (last == OLD_ENV_VAR))) 1326 1.5 cgd goto env_ovar_wrong; 1327 1.5 cgd got_value++; 1328 1.5 cgd last = OLD_ENV_VALUE; 1329 1.5 cgd break; 1330 1.5 cgd case ENV_USERVAR: 1331 1.5 cgd /* count strings of USERVAR as one */ 1332 1.5 cgd if (last != ENV_USERVAR) 1333 1.5 cgd got_uservar++; 1334 1.5 cgd if (empty) { 1335 1.5 cgd if (last == OLD_ENV_VALUE) 1336 1.5 cgd goto env_ovar_ok; 1337 1.5 cgd if (last == OLD_ENV_VAR) 1338 1.5 cgd goto env_ovar_wrong; 1339 1.5 cgd } 1340 1.5 cgd last = ENV_USERVAR; 1341 1.5 cgd break; 1342 1.5 cgd case ENV_ESC: 1343 1.5 cgd if (!SB_EOF()) 1344 1.5 cgd c = SB_GET(); 1345 1.5 cgd /* FALL THROUGH */ 1346 1.5 cgd default: 1347 1.5 cgd empty = 0; 1348 1.5 cgd continue; 1349 1.5 cgd } 1350 1.5 cgd empty = 1; 1351 1.5 cgd } 1352 1.5 cgd if (empty) { 1353 1.5 cgd if (last == OLD_ENV_VALUE) 1354 1.5 cgd goto env_ovar_ok; 1355 1.5 cgd if (last == OLD_ENV_VAR) 1356 1.5 cgd goto env_ovar_wrong; 1357 1.5 cgd } 1358 1.5 cgd /* 1359 1.5 cgd * Ok, the first thing was a USERVAR, and there 1360 1.5 cgd * are not two consecutive VAR or VALUE commands, 1361 1.5 cgd * and none of the VAR or VALUE commands are empty. 1362 1.5 cgd * If the client has sent us a well-formed option, 1363 1.5 cgd * then the number of VALUEs received should always 1364 1.5 cgd * be less than or equal to the number of VARs and 1365 1.5 cgd * USERVARs received. 1366 1.5 cgd * 1367 1.5 cgd * If we got exactly as many VALUEs as VARs and 1368 1.5 cgd * USERVARs, the client has the same definitions. 1369 1.5 cgd * 1370 1.5 cgd * If we got exactly as many VARs as VALUEs and 1371 1.5 cgd * USERVARS, the client has reversed definitions. 1372 1.5 cgd */ 1373 1.5 cgd if (got_uservar + got_var == got_value) { 1374 1.5 cgd env_ovar_ok: 1375 1.5 cgd env_ovar = OLD_ENV_VAR; 1376 1.5 cgd env_ovalue = OLD_ENV_VALUE; 1377 1.5 cgd } else if (got_uservar + got_value == got_var) { 1378 1.5 cgd env_ovar_wrong: 1379 1.5 cgd env_ovar = OLD_ENV_VALUE; 1380 1.5 cgd env_ovalue = OLD_ENV_VAR; 1381 1.15 itojun DIAG(TD_OPTIONS, {output_data( 1382 1.15 itojun "ENVIRON VALUE and VAR are reversed!\r\n");}); 1383 1.1 cgd 1384 1.5 cgd } 1385 1.5 cgd } 1386 1.5 cgd SB_RESTORE(); 1387 1.5 cgd #endif 1388 1.5 cgd 1389 1.5 cgd while (!SB_EOF()) { 1390 1.5 cgd c = SB_GET(); 1391 1.5 cgd if ((c == env_ovar) || (c == ENV_USERVAR)) 1392 1.5 cgd break; 1393 1.5 cgd } 1394 1.5 cgd } 1395 1.1 cgd 1396 1.1 cgd if (SB_EOF()) 1397 1.1 cgd return; 1398 1.1 cgd 1399 1.1 cgd cp = varp = (char *)subpointer; 1400 1.1 cgd valp = 0; 1401 1.1 cgd 1402 1.1 cgd while (!SB_EOF()) { 1403 1.5 cgd c = SB_GET(); 1404 1.5 cgd if (subchar == TELOPT_OLD_ENVIRON) { 1405 1.5 cgd if (c == env_ovar) 1406 1.5 cgd c = NEW_ENV_VAR; 1407 1.5 cgd else if (c == env_ovalue) 1408 1.5 cgd c = NEW_ENV_VALUE; 1409 1.5 cgd } 1410 1.5 cgd switch (c) { 1411 1.5 cgd 1412 1.5 cgd case NEW_ENV_VALUE: 1413 1.1 cgd *cp = '\0'; 1414 1.1 cgd cp = valp = (char *)subpointer; 1415 1.1 cgd break; 1416 1.1 cgd 1417 1.5 cgd case NEW_ENV_VAR: 1418 1.5 cgd case ENV_USERVAR: 1419 1.1 cgd *cp = '\0'; 1420 1.7 ghudson if (envvarok(varp)) { 1421 1.6 ghudson if (valp) 1422 1.6 ghudson (void)setenv(varp, valp, 1); 1423 1.6 ghudson else 1424 1.6 ghudson unsetenv(varp); 1425 1.6 ghudson } 1426 1.1 cgd cp = varp = (char *)subpointer; 1427 1.1 cgd valp = 0; 1428 1.1 cgd break; 1429 1.1 cgd 1430 1.1 cgd case ENV_ESC: 1431 1.1 cgd if (SB_EOF()) 1432 1.1 cgd break; 1433 1.1 cgd c = SB_GET(); 1434 1.1 cgd /* FALL THROUGH */ 1435 1.1 cgd default: 1436 1.1 cgd *cp++ = c; 1437 1.1 cgd break; 1438 1.1 cgd } 1439 1.1 cgd } 1440 1.1 cgd *cp = '\0'; 1441 1.7 ghudson if (envvarok(varp)) { 1442 1.7 ghudson if (valp) 1443 1.7 ghudson (void)setenv(varp, valp, 1); 1444 1.7 ghudson else 1445 1.7 ghudson unsetenv(varp); 1446 1.7 ghudson } 1447 1.1 cgd break; 1448 1.5 cgd } /* end of case TELOPT_NEW_ENVIRON */ 1449 1.23 itojun #ifdef AUTHENTICATION 1450 1.1 cgd case TELOPT_AUTHENTICATION: 1451 1.1 cgd if (SB_EOF()) 1452 1.1 cgd break; 1453 1.1 cgd switch(SB_GET()) { 1454 1.1 cgd case TELQUAL_SEND: 1455 1.1 cgd case TELQUAL_REPLY: 1456 1.1 cgd /* 1457 1.1 cgd * These are sent by us and cannot be sent by 1458 1.1 cgd * the client. 1459 1.1 cgd */ 1460 1.1 cgd break; 1461 1.1 cgd case TELQUAL_IS: 1462 1.1 cgd auth_is(subpointer, SB_LEN()); 1463 1.1 cgd break; 1464 1.1 cgd case TELQUAL_NAME: 1465 1.1 cgd auth_name(subpointer, SB_LEN()); 1466 1.1 cgd break; 1467 1.1 cgd } 1468 1.1 cgd break; 1469 1.1 cgd #endif 1470 1.12 thorpej #ifdef ENCRYPTION 1471 1.12 thorpej case TELOPT_ENCRYPT: 1472 1.12 thorpej if (SB_EOF()) 1473 1.12 thorpej break; 1474 1.12 thorpej switch(SB_GET()) { 1475 1.12 thorpej case ENCRYPT_SUPPORT: 1476 1.12 thorpej encrypt_support(subpointer, SB_LEN()); 1477 1.12 thorpej break; 1478 1.12 thorpej case ENCRYPT_IS: 1479 1.12 thorpej encrypt_is(subpointer, SB_LEN()); 1480 1.12 thorpej break; 1481 1.12 thorpej case ENCRYPT_REPLY: 1482 1.12 thorpej encrypt_reply(subpointer, SB_LEN()); 1483 1.12 thorpej break; 1484 1.12 thorpej case ENCRYPT_START: 1485 1.12 thorpej encrypt_start(subpointer, SB_LEN()); 1486 1.12 thorpej break; 1487 1.12 thorpej case ENCRYPT_END: 1488 1.12 thorpej encrypt_end(); 1489 1.12 thorpej break; 1490 1.12 thorpej case ENCRYPT_REQSTART: 1491 1.12 thorpej encrypt_request_start(subpointer, SB_LEN()); 1492 1.12 thorpej break; 1493 1.12 thorpej case ENCRYPT_REQEND: 1494 1.12 thorpej /* 1495 1.12 thorpej * We can always send an REQEND so that we cannot 1496 1.12 thorpej * get stuck encrypting. We should only get this 1497 1.12 thorpej * if we have been able to get in the correct mode 1498 1.12 thorpej * anyhow. 1499 1.12 thorpej */ 1500 1.12 thorpej encrypt_request_end(); 1501 1.12 thorpej break; 1502 1.12 thorpej case ENCRYPT_ENC_KEYID: 1503 1.12 thorpej encrypt_enc_keyid(subpointer, SB_LEN()); 1504 1.12 thorpej break; 1505 1.12 thorpej case ENCRYPT_DEC_KEYID: 1506 1.12 thorpej encrypt_dec_keyid(subpointer, SB_LEN()); 1507 1.12 thorpej break; 1508 1.12 thorpej default: 1509 1.12 thorpej break; 1510 1.12 thorpej } 1511 1.12 thorpej break; 1512 1.12 thorpej #endif /* ENCRYPTION */ 1513 1.1 cgd 1514 1.1 cgd default: 1515 1.1 cgd break; 1516 1.1 cgd } /* end of switch */ 1517 1.1 cgd 1518 1.1 cgd } /* end of suboption */ 1519 1.1 cgd 1520 1.18 wiz #ifdef LINEMODE 1521 1.23 itojun void 1522 1.25 perry doclientstat(void) 1523 1.1 cgd { 1524 1.1 cgd clientstat(TELOPT_LINEMODE, WILL, 0); 1525 1.1 cgd } 1526 1.18 wiz #endif /* LINEMODE */ 1527 1.1 cgd 1528 1.23 itojun void 1529 1.25 perry send_status(void) 1530 1.1 cgd { 1531 1.15 itojun #define ADD(c) \ 1532 1.15 itojun do { \ 1533 1.15 itojun if (ep > ncp) \ 1534 1.15 itojun *ncp++ = c; \ 1535 1.15 itojun else \ 1536 1.15 itojun goto trunc; \ 1537 1.15 itojun } while (0) 1538 1.15 itojun #define ADD_DATA(c) \ 1539 1.15 itojun do { \ 1540 1.15 itojun ADD(c); if (c == SE || c == IAC) ADD(c); \ 1541 1.15 itojun } while (0) 1542 1.15 itojun 1543 1.1 cgd unsigned char statusbuf[256]; 1544 1.15 itojun unsigned char *ep; 1545 1.25 perry unsigned char *ncp; 1546 1.25 perry unsigned char i; 1547 1.1 cgd 1548 1.1 cgd ncp = statusbuf; 1549 1.15 itojun ep = statusbuf + sizeof(statusbuf); 1550 1.1 cgd 1551 1.1 cgd netflush(); /* get rid of anything waiting to go out */ 1552 1.1 cgd 1553 1.1 cgd ADD(IAC); 1554 1.1 cgd ADD(SB); 1555 1.1 cgd ADD(TELOPT_STATUS); 1556 1.1 cgd ADD(TELQUAL_IS); 1557 1.1 cgd 1558 1.1 cgd /* 1559 1.1 cgd * We check the want_state rather than the current state, 1560 1.1 cgd * because if we received a DO/WILL for an option that we 1561 1.1 cgd * don't support, and the other side didn't send a DONT/WONT 1562 1.1 cgd * in response to our WONT/DONT, then the "state" will be 1563 1.1 cgd * WILL/DO, and the "want_state" will be WONT/DONT. We 1564 1.1 cgd * need to go by the latter. 1565 1.1 cgd */ 1566 1.5 cgd for (i = 0; i < (unsigned char)NTELOPTS; i++) { 1567 1.1 cgd if (my_want_state_is_will(i)) { 1568 1.1 cgd ADD(WILL); 1569 1.1 cgd ADD_DATA(i); 1570 1.1 cgd } 1571 1.1 cgd if (his_want_state_is_will(i)) { 1572 1.1 cgd ADD(DO); 1573 1.1 cgd ADD_DATA(i); 1574 1.1 cgd } 1575 1.1 cgd } 1576 1.1 cgd 1577 1.1 cgd if (his_want_state_is_will(TELOPT_LFLOW)) { 1578 1.1 cgd ADD(SB); 1579 1.1 cgd ADD(TELOPT_LFLOW); 1580 1.5 cgd if (flowmode) { 1581 1.5 cgd ADD(LFLOW_ON); 1582 1.5 cgd } else { 1583 1.5 cgd ADD(LFLOW_OFF); 1584 1.5 cgd } 1585 1.1 cgd ADD(SE); 1586 1.5 cgd 1587 1.5 cgd if (restartany >= 0) { 1588 1.8 jtk ADD(SB); 1589 1.5 cgd ADD(TELOPT_LFLOW); 1590 1.5 cgd if (restartany) { 1591 1.5 cgd ADD(LFLOW_RESTART_ANY); 1592 1.5 cgd } else { 1593 1.5 cgd ADD(LFLOW_RESTART_XON); 1594 1.5 cgd } 1595 1.8 jtk ADD(SE); 1596 1.5 cgd } 1597 1.1 cgd } 1598 1.1 cgd 1599 1.1 cgd #ifdef LINEMODE 1600 1.1 cgd if (his_want_state_is_will(TELOPT_LINEMODE)) { 1601 1.1 cgd unsigned char *cp, *cpe; 1602 1.1 cgd int len; 1603 1.1 cgd 1604 1.1 cgd ADD(SB); 1605 1.1 cgd ADD(TELOPT_LINEMODE); 1606 1.1 cgd ADD(LM_MODE); 1607 1.1 cgd ADD_DATA(editmode); 1608 1.1 cgd ADD(SE); 1609 1.1 cgd 1610 1.1 cgd ADD(SB); 1611 1.1 cgd ADD(TELOPT_LINEMODE); 1612 1.1 cgd ADD(LM_SLC); 1613 1.1 cgd start_slc(0); 1614 1.1 cgd send_slc(); 1615 1.1 cgd len = end_slc(&cp); 1616 1.1 cgd for (cpe = cp + len; cp < cpe; cp++) 1617 1.1 cgd ADD_DATA(*cp); 1618 1.1 cgd ADD(SE); 1619 1.1 cgd } 1620 1.1 cgd #endif /* LINEMODE */ 1621 1.1 cgd 1622 1.1 cgd ADD(IAC); 1623 1.1 cgd ADD(SE); 1624 1.1 cgd 1625 1.1 cgd writenet(statusbuf, ncp - statusbuf); 1626 1.1 cgd netflush(); /* Send it on its way */ 1627 1.1 cgd 1628 1.1 cgd DIAG(TD_OPTIONS, 1629 1.1 cgd {printsub('>', statusbuf, ncp - statusbuf); netflush();}); 1630 1.15 itojun return; 1631 1.15 itojun 1632 1.15 itojun trunc: 1633 1.15 itojun /* XXX bark? */ 1634 1.15 itojun return; 1635 1.15 itojun #undef ADD 1636 1.15 itojun #undef ADD_DATA 1637 1.15 itojun } 1638 1.15 itojun 1639 1.15 itojun int 1640 1.15 itojun output_data(const char *format, ...) 1641 1.15 itojun { 1642 1.15 itojun va_list args; 1643 1.15 itojun size_t remaining, ret; 1644 1.15 itojun 1645 1.15 itojun va_start(args, format); 1646 1.15 itojun remaining = BUFSIZ - (nfrontp - netobuf); 1647 1.16 itojun /* try a netflush() if the room is too low */ 1648 1.16 itojun if (strlen(format) > remaining || BUFSIZ / 4 > remaining) { 1649 1.16 itojun netflush(); 1650 1.16 itojun remaining = BUFSIZ - (nfrontp - netobuf); 1651 1.16 itojun } 1652 1.15 itojun ret = vsnprintf(nfrontp, remaining, format, args); 1653 1.17 itojun nfrontp += ((ret < remaining - 1) ? ret : remaining - 1); 1654 1.15 itojun va_end(args); 1655 1.15 itojun return ret; 1656 1.15 itojun } 1657 1.15 itojun 1658 1.15 itojun int 1659 1.15 itojun output_datalen(const char *buf, size_t l) 1660 1.15 itojun { 1661 1.15 itojun size_t remaining; 1662 1.15 itojun 1663 1.15 itojun remaining = BUFSIZ - (nfrontp - netobuf); 1664 1.16 itojun if (remaining < l) { 1665 1.16 itojun netflush(); 1666 1.16 itojun remaining = BUFSIZ - (nfrontp - netobuf); 1667 1.16 itojun } 1668 1.15 itojun if (remaining < l) 1669 1.15 itojun return -1; 1670 1.15 itojun memmove(nfrontp, buf, l); 1671 1.15 itojun nfrontp += l; 1672 1.15 itojun return (int)l; 1673 1.1 cgd } 1674