|
1 // state.c |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * Copyright (c) 1989, 1993 |
|
7 * The Regents of the University of California. All rights reserved. |
|
8 * |
|
9 * Redistribution and use in source and binary forms, with or without |
|
10 * modification, are permitted provided that the following conditions |
|
11 * are met: |
|
12 * 1. Redistributions of source code must retain the above copyright |
|
13 * notice, this list of conditions and the following disclaimer. |
|
14 * 2. Redistributions in binary form must reproduce the above copyright |
|
15 * notice, this list of conditions and the following disclaimer in the |
|
16 * documentation and/or other materials provided with the distribution. |
|
17 * 3. 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 #include "telnetd.h" |
|
35 #ifdef __SYMBIAN32__ |
|
36 #ifdef __ARMCC__ |
|
37 // Turn off the 'extended constant initialiser used' warning |
|
38 #pragma diag_suppress 177 |
|
39 #endif//__ARMCC__ |
|
40 #endif//__SYMBIAN32__ |
|
41 |
|
42 #ifndef __SYMBIAN32__ |
|
43 __RCSID("$Heimdal: state.c,v 1.14.12.1 2004/06/21 08:21:58 lha Exp $" |
|
44 "$NetBSD: state.c,v 1.3 2004/09/14 08:08:20 lha Exp $"); |
|
45 #endif |
|
46 |
|
47 unsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; |
|
48 unsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; |
|
49 unsigned char will[] = { IAC, WILL, '%', 'c', 0 }; |
|
50 unsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; |
|
51 int not42 = 1; |
|
52 |
|
53 /* |
|
54 * Buffer for sub-options, and macros |
|
55 * for suboptions buffer manipulations |
|
56 */ |
|
57 unsigned char subbuffer[1024*64], *subpointer= subbuffer, *subend= subbuffer; |
|
58 |
|
59 #define SB_CLEAR() subpointer = subbuffer |
|
60 #define SB_TERM() { subend = subpointer; SB_CLEAR(); } |
|
61 #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ |
|
62 *subpointer++ = (c); \ |
|
63 } |
|
64 #define SB_GET() ((*subpointer++)&0xff) |
|
65 #define SB_EOF() (subpointer >= subend) |
|
66 #define SB_LEN() (subend - subpointer) |
|
67 |
|
68 #ifdef ENV_HACK |
|
69 unsigned char *subsave; |
|
70 #define SB_SAVE() subsave = subpointer; |
|
71 #define SB_RESTORE() subpointer = subsave; |
|
72 #endif |
|
73 |
|
74 |
|
75 /* |
|
76 * State for recv fsm |
|
77 */ |
|
78 #define TS_DATA 0 /* base state */ |
|
79 #define TS_IAC 1 /* look for double IAC's */ |
|
80 #define TS_CR 2 /* CR-LF ->'s CR */ |
|
81 #define TS_SB 3 /* throw away begin's... */ |
|
82 #define TS_SE 4 /* ...end's (suboption negotiation) */ |
|
83 #define TS_WILL 5 /* will option negotiation */ |
|
84 #define TS_WONT 6 /* wont -''- */ |
|
85 #define TS_DO 7 /* do -''- */ |
|
86 #define TS_DONT 8 /* dont -''- */ |
|
87 |
|
88 void |
|
89 telrcv(void) |
|
90 { |
|
91 int c; |
|
92 static int state = TS_DATA; |
|
93 |
|
94 while (ncc > 0) { |
|
95 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) |
|
96 break; |
|
97 c = *netip++ & 0377, ncc--; |
|
98 #ifdef ENCRYPTION |
|
99 if (decrypt_input) |
|
100 c = (*decrypt_input)(c); |
|
101 #endif |
|
102 switch (state) { |
|
103 |
|
104 case TS_CR: |
|
105 state = TS_DATA; |
|
106 /* Strip off \n or \0 after a \r */ |
|
107 if ((c == 0) || (c == '\n')) { |
|
108 break; |
|
109 } |
|
110 /* FALL THROUGH */ |
|
111 |
|
112 case TS_DATA: |
|
113 if (c == IAC) { |
|
114 state = TS_IAC; |
|
115 break; |
|
116 } |
|
117 /* |
|
118 * We now map \r\n ==> \r for pragmatic reasons. |
|
119 * Many client implementations send \r\n when |
|
120 * the user hits the CarriageReturn key. |
|
121 * |
|
122 * We USED to map \r\n ==> \n, since \r\n says |
|
123 * that we want to be in column 1 of the next |
|
124 * printable line, and \n is the standard |
|
125 * unix way of saying that (\r is only good |
|
126 * if CRMOD is set, which it normally is). |
|
127 */ |
|
128 if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { |
|
129 int nc = *netip; |
|
130 #ifdef ENCRYPTION |
|
131 if (decrypt_input) |
|
132 nc = (*decrypt_input)(nc & 0xff); |
|
133 #endif |
|
134 { |
|
135 #ifdef ENCRYPTION |
|
136 if (decrypt_input) |
|
137 (void)(*decrypt_input)(-1); |
|
138 #endif |
|
139 state = TS_CR; |
|
140 } |
|
141 } |
|
142 *pfrontp++ = c; |
|
143 break; |
|
144 |
|
145 case TS_IAC: |
|
146 gotiac: switch (c) { |
|
147 |
|
148 /* |
|
149 * Send the process on the pty side an |
|
150 * interrupt. Do this with a NULL or |
|
151 * interrupt char; depending on the tty mode. |
|
152 */ |
|
153 case IP: |
|
154 DIAG(TD_OPTIONS, |
|
155 printoption("td: recv IAC", c)); |
|
156 interrupt(); |
|
157 break; |
|
158 |
|
159 case BREAK: |
|
160 DIAG(TD_OPTIONS, |
|
161 printoption("td: recv IAC", c)); |
|
162 sendbrk(); |
|
163 break; |
|
164 |
|
165 /* |
|
166 * Are You There? |
|
167 */ |
|
168 case AYT: |
|
169 DIAG(TD_OPTIONS, |
|
170 printoption("td: recv IAC", c)); |
|
171 recv_ayt(); |
|
172 break; |
|
173 |
|
174 /* |
|
175 * Abort Output |
|
176 */ |
|
177 case AO: |
|
178 { |
|
179 DIAG(TD_OPTIONS, |
|
180 printoption("td: recv IAC", c)); |
|
181 ptyflush(); /* half-hearted */ |
|
182 init_termbuf(); |
|
183 |
|
184 if (slctab[SLC_AO].sptr && |
|
185 *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { |
|
186 *pfrontp++ = |
|
187 (unsigned char)*slctab[SLC_AO].sptr; |
|
188 } |
|
189 |
|
190 netclear(); /* clear buffer back */ |
|
191 output_data ("%c%c", IAC, DM); |
|
192 neturg = nfrontp-1; /* off by one XXX */ |
|
193 DIAG(TD_OPTIONS, |
|
194 printoption("td: send IAC", DM)); |
|
195 break; |
|
196 } |
|
197 |
|
198 /* |
|
199 * Erase Character and |
|
200 * Erase Line |
|
201 */ |
|
202 case EC: |
|
203 case EL: |
|
204 { |
|
205 cc_t ch; |
|
206 |
|
207 DIAG(TD_OPTIONS, |
|
208 printoption("td: recv IAC", c)); |
|
209 ptyflush(); /* half-hearted */ |
|
210 init_termbuf(); |
|
211 if (c == EC) |
|
212 ch = *slctab[SLC_EC].sptr; |
|
213 else |
|
214 ch = *slctab[SLC_EL].sptr; |
|
215 if (ch != (cc_t)(_POSIX_VDISABLE)) |
|
216 *pfrontp++ = (unsigned char)ch; |
|
217 break; |
|
218 } |
|
219 |
|
220 /* |
|
221 * Check for urgent data... |
|
222 */ |
|
223 case DM: |
|
224 DIAG(TD_OPTIONS, |
|
225 printoption("td: recv IAC", c)); |
|
226 SYNCHing = stilloob(net); |
|
227 settimer(gotDM); |
|
228 break; |
|
229 |
|
230 |
|
231 /* |
|
232 * Begin option subnegotiation... |
|
233 */ |
|
234 case SB: |
|
235 state = TS_SB; |
|
236 SB_CLEAR(); |
|
237 continue; |
|
238 |
|
239 case WILL: |
|
240 state = TS_WILL; |
|
241 continue; |
|
242 |
|
243 case WONT: |
|
244 state = TS_WONT; |
|
245 continue; |
|
246 |
|
247 case DO: |
|
248 state = TS_DO; |
|
249 continue; |
|
250 |
|
251 case DONT: |
|
252 state = TS_DONT; |
|
253 continue; |
|
254 case EOR: |
|
255 if (his_state_is_will(TELOPT_EOR)) |
|
256 doeof(); |
|
257 break; |
|
258 |
|
259 /* |
|
260 * Handle RFC 10xx Telnet linemode option additions |
|
261 * to command stream (EOF, SUSP, ABORT). |
|
262 */ |
|
263 case xEOF: |
|
264 doeof(); |
|
265 break; |
|
266 |
|
267 case SUSP: |
|
268 sendsusp(); |
|
269 break; |
|
270 |
|
271 case ABORT: |
|
272 sendbrk(); |
|
273 break; |
|
274 |
|
275 case IAC: |
|
276 *pfrontp++ = c; |
|
277 break; |
|
278 } |
|
279 state = TS_DATA; |
|
280 break; |
|
281 |
|
282 case TS_SB: |
|
283 if (c == IAC) { |
|
284 state = TS_SE; |
|
285 } else { |
|
286 SB_ACCUM(c); |
|
287 } |
|
288 break; |
|
289 |
|
290 case TS_SE: |
|
291 if (c != SE) { |
|
292 if (c != IAC) { |
|
293 /* |
|
294 * bad form of suboption negotiation. |
|
295 * handle it in such a way as to avoid |
|
296 * damage to local state. Parse |
|
297 * suboption buffer found so far, |
|
298 * then treat remaining stream as |
|
299 * another command sequence. |
|
300 */ |
|
301 |
|
302 /* for DIAGNOSTICS */ |
|
303 SB_ACCUM(IAC); |
|
304 SB_ACCUM(c); |
|
305 subpointer -= 2; |
|
306 |
|
307 SB_TERM(); |
|
308 suboption(); |
|
309 state = TS_IAC; |
|
310 goto gotiac; |
|
311 } |
|
312 SB_ACCUM(c); |
|
313 state = TS_SB; |
|
314 } else { |
|
315 /* for DIAGNOSTICS */ |
|
316 SB_ACCUM(IAC); |
|
317 SB_ACCUM(SE); |
|
318 subpointer -= 2; |
|
319 |
|
320 SB_TERM(); |
|
321 suboption(); /* handle sub-option */ |
|
322 state = TS_DATA; |
|
323 } |
|
324 break; |
|
325 |
|
326 case TS_WILL: |
|
327 willoption(c); |
|
328 state = TS_DATA; |
|
329 continue; |
|
330 |
|
331 case TS_WONT: |
|
332 wontoption(c); |
|
333 if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) ) |
|
334 dontoption(c); |
|
335 state = TS_DATA; |
|
336 continue; |
|
337 |
|
338 case TS_DO: |
|
339 dooption(c); |
|
340 state = TS_DATA; |
|
341 continue; |
|
342 |
|
343 case TS_DONT: |
|
344 dontoption(c); |
|
345 state = TS_DATA; |
|
346 continue; |
|
347 |
|
348 default: |
|
349 #ifndef __SYMBIAN32__ |
|
350 syslog(LOG_ERR, "telnetd: panic state=%d\n", state); |
|
351 #endif |
|
352 printf("telnetd: panic state=%d\n", state); |
|
353 exit(1); |
|
354 } |
|
355 } |
|
356 } /* end of telrcv */ |
|
357 |
|
358 /* |
|
359 * The will/wont/do/dont state machines are based on Dave Borman's |
|
360 * Telnet option processing state machine. |
|
361 * |
|
362 * These correspond to the following states: |
|
363 * my_state = the last negotiated state |
|
364 * want_state = what I want the state to go to |
|
365 * want_resp = how many requests I have sent |
|
366 * All state defaults are negative, and resp defaults to 0. |
|
367 * |
|
368 * When initiating a request to change state to new_state: |
|
369 * |
|
370 * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { |
|
371 * do nothing; |
|
372 * } else { |
|
373 * want_state = new_state; |
|
374 * send new_state; |
|
375 * want_resp++; |
|
376 * } |
|
377 * |
|
378 * When receiving new_state: |
|
379 * |
|
380 * if (want_resp) { |
|
381 * want_resp--; |
|
382 * if (want_resp && (new_state == my_state)) |
|
383 * want_resp--; |
|
384 * } |
|
385 * if ((want_resp == 0) && (new_state != want_state)) { |
|
386 * if (ok_to_switch_to new_state) |
|
387 * want_state = new_state; |
|
388 * else |
|
389 * want_resp++; |
|
390 * send want_state; |
|
391 * } |
|
392 * my_state = new_state; |
|
393 * |
|
394 * Note that new_state is implied in these functions by the function itself. |
|
395 * will and do imply positive new_state, wont and dont imply negative. |
|
396 * |
|
397 * Finally, there is one catch. If we send a negative response to a |
|
398 * positive request, my_state will be the positive while want_state will |
|
399 * remain negative. my_state will revert to negative when the negative |
|
400 * acknowlegment arrives from the peer. Thus, my_state generally tells |
|
401 * us not only the last negotiated state, but also tells us what the peer |
|
402 * wants to be doing as well. It is important to understand this difference |
|
403 * as we may wish to be processing data streams based on our desired state |
|
404 * (want_state) or based on what the peer thinks the state is (my_state). |
|
405 * |
|
406 * This all works fine because if the peer sends a positive request, the data |
|
407 * that we receive prior to negative acknowlegment will probably be affected |
|
408 * by the positive state, and we can process it as such (if we can; if we |
|
409 * can't then it really doesn't matter). If it is that important, then the |
|
410 * peer probably should be buffering until this option state negotiation |
|
411 * is complete. |
|
412 * |
|
413 */ |
|
414 void |
|
415 send_do(int option, int init) |
|
416 { |
|
417 if (init) { |
|
418 if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || |
|
419 his_want_state_is_will(option)) |
|
420 return; |
|
421 /* |
|
422 * Special case for TELOPT_TM: We send a DO, but pretend |
|
423 * that we sent a DONT, so that we can send more DOs if |
|
424 * we want to. |
|
425 */ |
|
426 if (option == TELOPT_TM) |
|
427 set_his_want_state_wont(option); |
|
428 else |
|
429 set_his_want_state_will(option); |
|
430 do_dont_resp[option]++; |
|
431 } |
|
432 output_data((const char *)doopt, option); |
|
433 |
|
434 DIAG(TD_OPTIONS, printoption("td: send do", option)); |
|
435 } |
|
436 |
|
437 #ifdef AUTHENTICATION |
|
438 extern void auth_request(void); |
|
439 #endif |
|
440 #ifdef ENCRYPTION |
|
441 extern void encrypt_send_support(); |
|
442 #endif |
|
443 |
|
444 void |
|
445 willoption(int option) |
|
446 { |
|
447 int changeok = 0; |
|
448 void (*func)() = 0; |
|
449 |
|
450 /* |
|
451 * process input from peer. |
|
452 */ |
|
453 |
|
454 DIAG(TD_OPTIONS, printoption("td: recv will", option)); |
|
455 |
|
456 if (do_dont_resp[option]) { |
|
457 do_dont_resp[option]--; |
|
458 if (do_dont_resp[option] && his_state_is_will(option)) |
|
459 do_dont_resp[option]--; |
|
460 } |
|
461 if (do_dont_resp[option] == 0) { |
|
462 if (his_want_state_is_wont(option)) { |
|
463 switch (option) { |
|
464 |
|
465 case TELOPT_BINARY: |
|
466 init_termbuf(); |
|
467 tty_binaryin(1); |
|
468 set_termbuf(); |
|
469 changeok++; |
|
470 break; |
|
471 |
|
472 case TELOPT_ECHO: |
|
473 /* |
|
474 * See comments below for more info. |
|
475 */ |
|
476 not42 = 0; /* looks like a 4.2 system */ |
|
477 break; |
|
478 |
|
479 case TELOPT_TM: |
|
480 /* |
|
481 * We never respond to a WILL TM, and |
|
482 * we leave the state WONT. |
|
483 */ |
|
484 return; |
|
485 |
|
486 case TELOPT_LFLOW: |
|
487 /* |
|
488 * If we are going to support flow control |
|
489 * option, then don't worry peer that we can't |
|
490 * change the flow control characters. |
|
491 */ |
|
492 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; |
|
493 slctab[SLC_XON].defset.flag |= SLC_DEFAULT; |
|
494 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; |
|
495 slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; |
|
496 case TELOPT_TTYPE: |
|
497 case TELOPT_SGA: |
|
498 case TELOPT_NAWS: |
|
499 case TELOPT_TSPEED: |
|
500 case TELOPT_XDISPLOC: |
|
501 case TELOPT_NEW_ENVIRON: |
|
502 case TELOPT_OLD_ENVIRON: |
|
503 changeok++; |
|
504 break; |
|
505 |
|
506 |
|
507 #ifdef AUTHENTICATION |
|
508 case TELOPT_AUTHENTICATION: |
|
509 func = auth_request; |
|
510 changeok++; |
|
511 break; |
|
512 #endif |
|
513 |
|
514 #ifdef ENCRYPTION |
|
515 case TELOPT_ENCRYPT: |
|
516 func = encrypt_send_support; |
|
517 changeok++; |
|
518 break; |
|
519 #endif |
|
520 |
|
521 default: |
|
522 break; |
|
523 } |
|
524 if (changeok) { |
|
525 set_his_want_state_will(option); |
|
526 send_do(option, 0); |
|
527 } else { |
|
528 do_dont_resp[option]++; |
|
529 send_dont(option, 0); |
|
530 } |
|
531 } else { |
|
532 /* |
|
533 * Option processing that should happen when |
|
534 * we receive conformation of a change in |
|
535 * state that we had requested. |
|
536 */ |
|
537 switch (option) { |
|
538 case TELOPT_ECHO: |
|
539 not42 = 0; /* looks like a 4.2 system */ |
|
540 /* |
|
541 * Egads, he responded "WILL ECHO". Turn |
|
542 * it off right now! |
|
543 */ |
|
544 send_dont(option, 1); |
|
545 /* |
|
546 * "WILL ECHO". Kludge upon kludge! |
|
547 * A 4.2 client is now echoing user input at |
|
548 * the tty. This is probably undesireable and |
|
549 * it should be stopped. The client will |
|
550 * respond WONT TM to the DO TM that we send to |
|
551 * check for kludge linemode. When the WONT TM |
|
552 * arrives, linemode will be turned off and a |
|
553 * change propogated to the pty. This change |
|
554 * will cause us to process the new pty state |
|
555 * in localstat(), which will notice that |
|
556 * linemode is off and send a WILL ECHO |
|
557 * so that we are properly in character mode and |
|
558 * all is well. |
|
559 */ |
|
560 break; |
|
561 |
|
562 #ifdef AUTHENTICATION |
|
563 case TELOPT_AUTHENTICATION: |
|
564 func = auth_request; |
|
565 break; |
|
566 #endif |
|
567 |
|
568 #ifdef ENCRYPTION |
|
569 case TELOPT_ENCRYPT: |
|
570 func = encrypt_send_support; |
|
571 break; |
|
572 #endif |
|
573 |
|
574 case TELOPT_LFLOW: |
|
575 func = flowstat; |
|
576 break; |
|
577 } |
|
578 } |
|
579 } |
|
580 set_his_state_will(option); |
|
581 if (func) |
|
582 (*func)(); |
|
583 } /* end of willoption */ |
|
584 |
|
585 void |
|
586 send_dont(int option, int init) |
|
587 { |
|
588 if (init) { |
|
589 if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || |
|
590 his_want_state_is_wont(option)) |
|
591 return; |
|
592 set_his_want_state_wont(option); |
|
593 do_dont_resp[option]++; |
|
594 } |
|
595 output_data((const char *)dont, option); |
|
596 |
|
597 DIAG(TD_OPTIONS, printoption("td: send dont", option)); |
|
598 } |
|
599 |
|
600 void |
|
601 wontoption(int option) |
|
602 { |
|
603 /* |
|
604 * Process client input. |
|
605 */ |
|
606 |
|
607 DIAG(TD_OPTIONS, printoption("td: recv wont", option)); |
|
608 |
|
609 if (do_dont_resp[option]) { |
|
610 do_dont_resp[option]--; |
|
611 if (do_dont_resp[option] && his_state_is_wont(option)) |
|
612 do_dont_resp[option]--; |
|
613 } |
|
614 if (do_dont_resp[option] == 0) { |
|
615 if (his_want_state_is_will(option)) { |
|
616 /* it is always ok to change to negative state */ |
|
617 switch (option) { |
|
618 case TELOPT_ECHO: |
|
619 not42 = 1; /* doesn't seem to be a 4.2 system */ |
|
620 break; |
|
621 |
|
622 case TELOPT_BINARY: |
|
623 init_termbuf(); |
|
624 tty_binaryin(0); |
|
625 set_termbuf(); |
|
626 break; |
|
627 |
|
628 case TELOPT_TM: |
|
629 /* |
|
630 * If we get a WONT TM, and had sent a DO TM, |
|
631 * don't respond with a DONT TM, just leave it |
|
632 * as is. Short circut the state machine to |
|
633 * achive this. |
|
634 */ |
|
635 set_his_want_state_wont(TELOPT_TM); |
|
636 return; |
|
637 |
|
638 case TELOPT_LFLOW: |
|
639 /* |
|
640 * If we are not going to support flow control |
|
641 * option, then let peer know that we can't |
|
642 * change the flow control characters. |
|
643 */ |
|
644 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; |
|
645 slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; |
|
646 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; |
|
647 slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; |
|
648 break; |
|
649 |
|
650 #ifdef AUTHENTICATION |
|
651 case TELOPT_AUTHENTICATION: |
|
652 auth_finished(0, AUTH_REJECT); |
|
653 break; |
|
654 #endif |
|
655 |
|
656 /* |
|
657 * For options that we might spin waiting for |
|
658 * sub-negotiation, if the client turns off the |
|
659 * option rather than responding to the request, |
|
660 * we have to treat it here as if we got a response |
|
661 * to the sub-negotiation, (by updating the timers) |
|
662 * so that we'll break out of the loop. |
|
663 */ |
|
664 case TELOPT_TTYPE: |
|
665 settimer(ttypesubopt); |
|
666 break; |
|
667 |
|
668 case TELOPT_TSPEED: |
|
669 settimer(tspeedsubopt); |
|
670 break; |
|
671 |
|
672 case TELOPT_XDISPLOC: |
|
673 settimer(xdisplocsubopt); |
|
674 break; |
|
675 |
|
676 case TELOPT_OLD_ENVIRON: |
|
677 settimer(oenvironsubopt); |
|
678 break; |
|
679 |
|
680 case TELOPT_NEW_ENVIRON: |
|
681 settimer(environsubopt); |
|
682 break; |
|
683 |
|
684 default: |
|
685 break; |
|
686 } |
|
687 set_his_want_state_wont(option); |
|
688 if (his_state_is_will(option)) |
|
689 send_dont(option, 0); |
|
690 } else { |
|
691 switch (option) { |
|
692 case TELOPT_TM: |
|
693 break; |
|
694 |
|
695 #ifdef AUTHENTICATION |
|
696 case TELOPT_AUTHENTICATION: |
|
697 auth_finished(0, AUTH_REJECT); |
|
698 break; |
|
699 #endif |
|
700 default: |
|
701 break; |
|
702 } |
|
703 } |
|
704 } |
|
705 set_his_state_wont(option); |
|
706 |
|
707 } /* end of wontoption */ |
|
708 |
|
709 void |
|
710 send_will(int option, int init) |
|
711 { |
|
712 if (init) { |
|
713 if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| |
|
714 my_want_state_is_will(option)) |
|
715 return; |
|
716 set_my_want_state_will(option); |
|
717 will_wont_resp[option]++; |
|
718 } |
|
719 output_data ((const char *)will, option); |
|
720 |
|
721 DIAG(TD_OPTIONS, printoption("td: send will", option)); |
|
722 } |
|
723 |
|
724 /* |
|
725 * When we get a DONT SGA, we will try once to turn it |
|
726 * back on. If the other side responds DONT SGA, we |
|
727 * leave it at that. This is so that when we talk to |
|
728 * clients that understand KLUDGELINEMODE but not LINEMODE, |
|
729 * we'll keep them in char-at-a-time mode. |
|
730 */ |
|
731 int turn_on_sga = 0; |
|
732 |
|
733 void |
|
734 dooption(int option) |
|
735 { |
|
736 int changeok = 0; |
|
737 |
|
738 /* |
|
739 * Process client input. |
|
740 */ |
|
741 |
|
742 DIAG(TD_OPTIONS, printoption("td: recv do", option)); |
|
743 |
|
744 if (will_wont_resp[option]) { |
|
745 will_wont_resp[option]--; |
|
746 if (will_wont_resp[option] && my_state_is_will(option)) |
|
747 will_wont_resp[option]--; |
|
748 } |
|
749 if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { |
|
750 switch (option) { |
|
751 case TELOPT_ECHO: |
|
752 { |
|
753 init_termbuf(); |
|
754 tty_setecho(1); |
|
755 set_termbuf(); |
|
756 } |
|
757 changeok++; |
|
758 break; |
|
759 |
|
760 case TELOPT_BINARY: |
|
761 init_termbuf(); |
|
762 tty_binaryout(1); |
|
763 set_termbuf(); |
|
764 changeok++; |
|
765 break; |
|
766 |
|
767 case TELOPT_SGA: |
|
768 turn_on_sga = 0; |
|
769 changeok++; |
|
770 break; |
|
771 |
|
772 case TELOPT_STATUS: |
|
773 changeok++; |
|
774 break; |
|
775 |
|
776 case TELOPT_TM: |
|
777 /* |
|
778 * Special case for TM. We send a WILL, but |
|
779 * pretend we sent a WONT. |
|
780 */ |
|
781 send_will(option, 0); |
|
782 set_my_want_state_wont(option); |
|
783 set_my_state_wont(option); |
|
784 return; |
|
785 |
|
786 case TELOPT_LOGOUT: |
|
787 /* |
|
788 * When we get a LOGOUT option, respond |
|
789 * with a WILL LOGOUT, make sure that |
|
790 * it gets written out to the network, |
|
791 * and then just go away... |
|
792 */ |
|
793 set_my_want_state_will(TELOPT_LOGOUT); |
|
794 send_will(TELOPT_LOGOUT, 0); |
|
795 set_my_state_will(TELOPT_LOGOUT); |
|
796 netflush(); |
|
797 cleanup(0); |
|
798 /* NOT REACHED */ |
|
799 break; |
|
800 |
|
801 #ifdef ENCRYPTION |
|
802 case TELOPT_ENCRYPT: |
|
803 changeok++; |
|
804 break; |
|
805 #endif |
|
806 case TELOPT_LINEMODE: |
|
807 case TELOPT_TTYPE: |
|
808 case TELOPT_NAWS: |
|
809 case TELOPT_TSPEED: |
|
810 case TELOPT_LFLOW: |
|
811 case TELOPT_XDISPLOC: |
|
812 #ifdef TELOPT_ENVIRON |
|
813 case TELOPT_NEW_ENVIRON: |
|
814 #endif |
|
815 case TELOPT_OLD_ENVIRON: |
|
816 default: |
|
817 break; |
|
818 } |
|
819 if (changeok) { |
|
820 set_my_want_state_will(option); |
|
821 send_will(option, 0); |
|
822 } else { |
|
823 will_wont_resp[option]++; |
|
824 send_wont(option, 0); |
|
825 } |
|
826 } |
|
827 set_my_state_will(option); |
|
828 |
|
829 } /* end of dooption */ |
|
830 |
|
831 void |
|
832 send_wont(int option, int init) |
|
833 { |
|
834 if (init) { |
|
835 if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || |
|
836 my_want_state_is_wont(option)) |
|
837 return; |
|
838 set_my_want_state_wont(option); |
|
839 will_wont_resp[option]++; |
|
840 } |
|
841 output_data ((const char *)wont, option); |
|
842 |
|
843 DIAG(TD_OPTIONS, printoption("td: send wont", option)); |
|
844 } |
|
845 |
|
846 void |
|
847 dontoption(int option) |
|
848 { |
|
849 /* |
|
850 * Process client input. |
|
851 */ |
|
852 |
|
853 |
|
854 DIAG(TD_OPTIONS, printoption("td: recv dont", option)); |
|
855 |
|
856 if (will_wont_resp[option]) { |
|
857 will_wont_resp[option]--; |
|
858 if (will_wont_resp[option] && my_state_is_wont(option)) |
|
859 will_wont_resp[option]--; |
|
860 } |
|
861 if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { |
|
862 switch (option) { |
|
863 case TELOPT_BINARY: |
|
864 init_termbuf(); |
|
865 tty_binaryout(0); |
|
866 set_termbuf(); |
|
867 break; |
|
868 |
|
869 case TELOPT_ECHO: /* we should stop echoing */ |
|
870 { |
|
871 init_termbuf(); |
|
872 tty_setecho(0); |
|
873 set_termbuf(); |
|
874 } |
|
875 break; |
|
876 |
|
877 case TELOPT_SGA: |
|
878 set_my_want_state_wont(option); |
|
879 if (my_state_is_will(option)) |
|
880 send_wont(option, 0); |
|
881 set_my_state_wont(option); |
|
882 if (turn_on_sga ^= 1) |
|
883 send_will(option, 1); |
|
884 return; |
|
885 |
|
886 default: |
|
887 break; |
|
888 } |
|
889 |
|
890 set_my_want_state_wont(option); |
|
891 if (my_state_is_will(option)) |
|
892 send_wont(option, 0); |
|
893 } |
|
894 set_my_state_wont(option); |
|
895 |
|
896 } /* end of dontoption */ |
|
897 |
|
898 #ifdef ENV_HACK |
|
899 int env_ovar = -1; |
|
900 int env_ovalue = -1; |
|
901 #else /* ENV_HACK */ |
|
902 # define env_ovar OLD_ENV_VAR |
|
903 # define env_ovalue OLD_ENV_VALUE |
|
904 #endif /* ENV_HACK */ |
|
905 |
|
906 /* |
|
907 * suboption() |
|
908 * |
|
909 * Look at the sub-option buffer, and try to be helpful to the other |
|
910 * side. |
|
911 * |
|
912 * Currently we recognize: |
|
913 * |
|
914 * Terminal type is |
|
915 * Linemode |
|
916 * Window size |
|
917 * Terminal speed |
|
918 */ |
|
919 void |
|
920 suboption(void) |
|
921 { |
|
922 int subchar; |
|
923 |
|
924 DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); |
|
925 |
|
926 subchar = SB_GET(); |
|
927 switch (subchar) { |
|
928 case TELOPT_TSPEED: { |
|
929 int xspeed, rspeed; |
|
930 |
|
931 if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ |
|
932 break; |
|
933 |
|
934 settimer(tspeedsubopt); |
|
935 |
|
936 if (SB_EOF() || SB_GET() != TELQUAL_IS) |
|
937 return; |
|
938 |
|
939 xspeed = atoi((char *)subpointer); |
|
940 |
|
941 while (SB_GET() != ',' && !SB_EOF()){} |
|
942 |
|
943 if (SB_EOF()) |
|
944 return; |
|
945 |
|
946 rspeed = atoi((char *)subpointer); |
|
947 clientstat(TELOPT_TSPEED, xspeed, rspeed); |
|
948 |
|
949 break; |
|
950 |
|
951 } /* end of case TELOPT_TSPEED */ |
|
952 |
|
953 case TELOPT_TTYPE: { /* Yaaaay! */ |
|
954 static char terminalname[41]; |
|
955 |
|
956 if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ |
|
957 break; |
|
958 settimer(ttypesubopt); |
|
959 |
|
960 if (SB_EOF() || SB_GET() != TELQUAL_IS) { |
|
961 return; /* ??? XXX but, this is the most robust */ |
|
962 } |
|
963 |
|
964 terminaltype = terminalname; |
|
965 |
|
966 while ((terminaltype < (terminalname + sizeof terminalname-1)) && |
|
967 !SB_EOF()) { |
|
968 int c; |
|
969 |
|
970 c = SB_GET(); |
|
971 if (isupper(c)) { |
|
972 c = tolower(c); |
|
973 } |
|
974 *terminaltype++ = c; /* accumulate name */ |
|
975 } |
|
976 *terminaltype = 0; |
|
977 terminaltype = terminalname; |
|
978 break; |
|
979 } /* end of case TELOPT_TTYPE */ |
|
980 |
|
981 case TELOPT_NAWS: { |
|
982 int xwinsize, ywinsize; |
|
983 |
|
984 if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ |
|
985 break; |
|
986 |
|
987 if (SB_EOF()) |
|
988 return; |
|
989 xwinsize = SB_GET() << 8; |
|
990 if (SB_EOF()) |
|
991 return; |
|
992 xwinsize |= SB_GET(); |
|
993 if (SB_EOF()) |
|
994 return; |
|
995 ywinsize = SB_GET() << 8; |
|
996 if (SB_EOF()) |
|
997 return; |
|
998 ywinsize |= SB_GET(); |
|
999 clientstat(TELOPT_NAWS, xwinsize, ywinsize); |
|
1000 |
|
1001 break; |
|
1002 |
|
1003 } /* end of case TELOPT_NAWS */ |
|
1004 |
|
1005 case TELOPT_STATUS: { |
|
1006 int mode; |
|
1007 |
|
1008 if (SB_EOF()) |
|
1009 break; |
|
1010 mode = SB_GET(); |
|
1011 switch (mode) { |
|
1012 case TELQUAL_SEND: |
|
1013 if (my_state_is_will(TELOPT_STATUS)) |
|
1014 send_status(); |
|
1015 break; |
|
1016 |
|
1017 case TELQUAL_IS: |
|
1018 break; |
|
1019 |
|
1020 default: |
|
1021 break; |
|
1022 } |
|
1023 break; |
|
1024 } /* end of case TELOPT_STATUS */ |
|
1025 |
|
1026 case TELOPT_XDISPLOC: { |
|
1027 if (SB_EOF() || SB_GET() != TELQUAL_IS) |
|
1028 return; |
|
1029 settimer(xdisplocsubopt); |
|
1030 subpointer[SB_LEN()] = '\0'; |
|
1031 setenv("DISPLAY", (char *)subpointer, 1); |
|
1032 break; |
|
1033 } /* end of case TELOPT_XDISPLOC */ |
|
1034 |
|
1035 #ifdef TELOPT_NEW_ENVIRON |
|
1036 case TELOPT_NEW_ENVIRON: |
|
1037 #endif |
|
1038 case TELOPT_OLD_ENVIRON: { |
|
1039 int c; |
|
1040 char *cp, *varp, *valp; |
|
1041 |
|
1042 if (SB_EOF()) |
|
1043 return; |
|
1044 c = SB_GET(); |
|
1045 if (c == TELQUAL_IS) { |
|
1046 if (subchar == TELOPT_OLD_ENVIRON) |
|
1047 settimer(oenvironsubopt); |
|
1048 else |
|
1049 settimer(environsubopt); |
|
1050 } else if (c != TELQUAL_INFO) { |
|
1051 return; |
|
1052 } |
|
1053 |
|
1054 #ifdef TELOPT_NEW_ENVIRON |
|
1055 if (subchar == TELOPT_NEW_ENVIRON) { |
|
1056 while (!SB_EOF()) { |
|
1057 c = SB_GET(); |
|
1058 if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) |
|
1059 break; |
|
1060 } |
|
1061 } else |
|
1062 #endif |
|
1063 { |
|
1064 #ifdef ENV_HACK |
|
1065 /* |
|
1066 * We only want to do this if we haven't already decided |
|
1067 * whether or not the other side has its VALUE and VAR |
|
1068 * reversed. |
|
1069 */ |
|
1070 if (env_ovar < 0) { |
|
1071 int last = -1; /* invalid value */ |
|
1072 int empty = 0; |
|
1073 int got_var = 0, got_value = 0, got_uservar = 0; |
|
1074 |
|
1075 /* |
|
1076 * The other side might have its VALUE and VAR values |
|
1077 * reversed. To be interoperable, we need to determine |
|
1078 * which way it is. If the first recognized character |
|
1079 * is a VAR or VALUE, then that will tell us what |
|
1080 * type of client it is. If the fist recognized |
|
1081 * character is a USERVAR, then we continue scanning |
|
1082 * the suboption looking for two consecutive |
|
1083 * VAR or VALUE fields. We should not get two |
|
1084 * consecutive VALUE fields, so finding two |
|
1085 * consecutive VALUE or VAR fields will tell us |
|
1086 * what the client is. |
|
1087 */ |
|
1088 SB_SAVE(); |
|
1089 while (!SB_EOF()) { |
|
1090 c = SB_GET(); |
|
1091 switch(c) { |
|
1092 case OLD_ENV_VAR: |
|
1093 if (last < 0 || last == OLD_ENV_VAR |
|
1094 || (empty && (last == OLD_ENV_VALUE))) |
|
1095 goto env_ovar_ok; |
|
1096 got_var++; |
|
1097 last = OLD_ENV_VAR; |
|
1098 break; |
|
1099 case OLD_ENV_VALUE: |
|
1100 if (last < 0 || last == OLD_ENV_VALUE |
|
1101 || (empty && (last == OLD_ENV_VAR))) |
|
1102 goto env_ovar_wrong; |
|
1103 got_value++; |
|
1104 last = OLD_ENV_VALUE; |
|
1105 break; |
|
1106 case ENV_USERVAR: |
|
1107 /* count strings of USERVAR as one */ |
|
1108 if (last != ENV_USERVAR) |
|
1109 got_uservar++; |
|
1110 if (empty) { |
|
1111 if (last == OLD_ENV_VALUE) |
|
1112 goto env_ovar_ok; |
|
1113 if (last == OLD_ENV_VAR) |
|
1114 goto env_ovar_wrong; |
|
1115 } |
|
1116 last = ENV_USERVAR; |
|
1117 break; |
|
1118 case ENV_ESC: |
|
1119 if (!SB_EOF()) |
|
1120 c = SB_GET(); |
|
1121 /* FALL THROUGH */ |
|
1122 default: |
|
1123 empty = 0; |
|
1124 continue; |
|
1125 } |
|
1126 empty = 1; |
|
1127 } |
|
1128 if (empty) { |
|
1129 if (last == OLD_ENV_VALUE) |
|
1130 goto env_ovar_ok; |
|
1131 if (last == OLD_ENV_VAR) |
|
1132 goto env_ovar_wrong; |
|
1133 } |
|
1134 /* |
|
1135 * Ok, the first thing was a USERVAR, and there |
|
1136 * are not two consecutive VAR or VALUE commands, |
|
1137 * and none of the VAR or VALUE commands are empty. |
|
1138 * If the client has sent us a well-formed option, |
|
1139 * then the number of VALUEs received should always |
|
1140 * be less than or equal to the number of VARs and |
|
1141 * USERVARs received. |
|
1142 * |
|
1143 * If we got exactly as many VALUEs as VARs and |
|
1144 * USERVARs, the client has the same definitions. |
|
1145 * |
|
1146 * If we got exactly as many VARs as VALUEs and |
|
1147 * USERVARS, the client has reversed definitions. |
|
1148 */ |
|
1149 if (got_uservar + got_var == got_value) { |
|
1150 env_ovar_ok: |
|
1151 env_ovar = OLD_ENV_VAR; |
|
1152 env_ovalue = OLD_ENV_VALUE; |
|
1153 } else if (got_uservar + got_value == got_var) { |
|
1154 env_ovar_wrong: |
|
1155 env_ovar = OLD_ENV_VALUE; |
|
1156 env_ovalue = OLD_ENV_VAR; |
|
1157 DIAG(TD_OPTIONS, { |
|
1158 output_data("ENVIRON VALUE and VAR are reversed!\r\n"); |
|
1159 }); |
|
1160 |
|
1161 } |
|
1162 } |
|
1163 SB_RESTORE(); |
|
1164 #endif |
|
1165 |
|
1166 while (!SB_EOF()) { |
|
1167 c = SB_GET(); |
|
1168 if ((c == env_ovar) || (c == ENV_USERVAR)) |
|
1169 break; |
|
1170 } |
|
1171 } |
|
1172 |
|
1173 if (SB_EOF()) |
|
1174 return; |
|
1175 |
|
1176 cp = varp = (char *)subpointer; |
|
1177 valp = 0; |
|
1178 |
|
1179 while (!SB_EOF()) { |
|
1180 c = SB_GET(); |
|
1181 if (subchar == TELOPT_OLD_ENVIRON) { |
|
1182 if (c == env_ovar) |
|
1183 c = NEW_ENV_VAR; |
|
1184 else if (c == env_ovalue) |
|
1185 c = NEW_ENV_VALUE; |
|
1186 } |
|
1187 switch (c) { |
|
1188 |
|
1189 case NEW_ENV_VALUE: |
|
1190 *cp = '\0'; |
|
1191 cp = valp = (char *)subpointer; |
|
1192 break; |
|
1193 |
|
1194 case NEW_ENV_VAR: |
|
1195 case ENV_USERVAR: |
|
1196 *cp = '\0'; |
|
1197 if (valp) |
|
1198 setenv(varp, valp, 1); |
|
1199 else |
|
1200 unsetenv(varp); |
|
1201 cp = varp = (char *)subpointer; |
|
1202 valp = 0; |
|
1203 break; |
|
1204 |
|
1205 case ENV_ESC: |
|
1206 if (SB_EOF()) |
|
1207 break; |
|
1208 c = SB_GET(); |
|
1209 /* FALL THROUGH */ |
|
1210 default: |
|
1211 *cp++ = c; |
|
1212 break; |
|
1213 } |
|
1214 } |
|
1215 *cp = '\0'; |
|
1216 if (valp) |
|
1217 setenv(varp, valp, 1); |
|
1218 else |
|
1219 unsetenv(varp); |
|
1220 break; |
|
1221 } /* end of case TELOPT_NEW_ENVIRON */ |
|
1222 #ifdef AUTHENTICATION |
|
1223 case TELOPT_AUTHENTICATION: |
|
1224 if (SB_EOF()) |
|
1225 break; |
|
1226 switch(SB_GET()) { |
|
1227 case TELQUAL_SEND: |
|
1228 case TELQUAL_REPLY: |
|
1229 /* |
|
1230 * These are sent by us and cannot be sent by |
|
1231 * the client. |
|
1232 */ |
|
1233 break; |
|
1234 case TELQUAL_IS: |
|
1235 auth_is(subpointer, SB_LEN()); |
|
1236 break; |
|
1237 case TELQUAL_NAME: |
|
1238 auth_name(subpointer, SB_LEN()); |
|
1239 break; |
|
1240 } |
|
1241 break; |
|
1242 #endif |
|
1243 #ifdef ENCRYPTION |
|
1244 case TELOPT_ENCRYPT: |
|
1245 if (SB_EOF()) |
|
1246 break; |
|
1247 switch(SB_GET()) { |
|
1248 case ENCRYPT_SUPPORT: |
|
1249 encrypt_support(subpointer, SB_LEN()); |
|
1250 break; |
|
1251 case ENCRYPT_IS: |
|
1252 encrypt_is(subpointer, SB_LEN()); |
|
1253 break; |
|
1254 case ENCRYPT_REPLY: |
|
1255 encrypt_reply(subpointer, SB_LEN()); |
|
1256 break; |
|
1257 case ENCRYPT_START: |
|
1258 encrypt_start(subpointer, SB_LEN()); |
|
1259 break; |
|
1260 case ENCRYPT_END: |
|
1261 encrypt_end(); |
|
1262 break; |
|
1263 case ENCRYPT_REQSTART: |
|
1264 encrypt_request_start(subpointer, SB_LEN()); |
|
1265 break; |
|
1266 case ENCRYPT_REQEND: |
|
1267 /* |
|
1268 * We can always send an REQEND so that we cannot |
|
1269 * get stuck encrypting. We should only get this |
|
1270 * if we have been able to get in the correct mode |
|
1271 * anyhow. |
|
1272 */ |
|
1273 encrypt_request_end(); |
|
1274 break; |
|
1275 case ENCRYPT_ENC_KEYID: |
|
1276 encrypt_enc_keyid(subpointer, SB_LEN()); |
|
1277 break; |
|
1278 case ENCRYPT_DEC_KEYID: |
|
1279 encrypt_dec_keyid(subpointer, SB_LEN()); |
|
1280 break; |
|
1281 default: |
|
1282 break; |
|
1283 } |
|
1284 break; |
|
1285 #endif |
|
1286 |
|
1287 default: |
|
1288 break; |
|
1289 } /* end of switch */ |
|
1290 |
|
1291 } /* end of suboption */ |
|
1292 |
|
1293 void |
|
1294 doclientstat(void) |
|
1295 { |
|
1296 clientstat(TELOPT_LINEMODE, WILL, 0); |
|
1297 } |
|
1298 |
|
1299 #undef ADD |
|
1300 #define ADD(c) *ncp++ = c |
|
1301 #define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; } |
|
1302 |
|
1303 void |
|
1304 send_status(void) |
|
1305 { |
|
1306 unsigned char statusbuf[256]; |
|
1307 unsigned char *ncp; |
|
1308 unsigned char i; |
|
1309 |
|
1310 ncp = statusbuf; |
|
1311 |
|
1312 netflush(); /* get rid of anything waiting to go out */ |
|
1313 |
|
1314 ADD(IAC); |
|
1315 ADD(SB); |
|
1316 ADD(TELOPT_STATUS); |
|
1317 ADD(TELQUAL_IS); |
|
1318 |
|
1319 /* |
|
1320 * We check the want_state rather than the current state, |
|
1321 * because if we received a DO/WILL for an option that we |
|
1322 * don't support, and the other side didn't send a DONT/WONT |
|
1323 * in response to our WONT/DONT, then the "state" will be |
|
1324 * WILL/DO, and the "want_state" will be WONT/DONT. We |
|
1325 * need to go by the latter. |
|
1326 */ |
|
1327 for (i = 0; i < (unsigned char)NTELOPTS; i++) { |
|
1328 if (my_want_state_is_will(i)) { |
|
1329 ADD(WILL); |
|
1330 ADD_DATA(i); |
|
1331 } |
|
1332 if (his_want_state_is_will(i)) { |
|
1333 ADD(DO); |
|
1334 ADD_DATA(i); |
|
1335 } |
|
1336 } |
|
1337 |
|
1338 if (his_want_state_is_will(TELOPT_LFLOW)) { |
|
1339 ADD(SB); |
|
1340 ADD(TELOPT_LFLOW); |
|
1341 if (flowmode) { |
|
1342 ADD(LFLOW_ON); |
|
1343 } else { |
|
1344 ADD(LFLOW_OFF); |
|
1345 } |
|
1346 ADD(SE); |
|
1347 |
|
1348 if (restartany >= 0) { |
|
1349 ADD(SB); |
|
1350 ADD(TELOPT_LFLOW); |
|
1351 if (restartany) { |
|
1352 ADD(LFLOW_RESTART_ANY); |
|
1353 } else { |
|
1354 ADD(LFLOW_RESTART_XON); |
|
1355 } |
|
1356 ADD(SE); |
|
1357 } |
|
1358 } |
|
1359 |
|
1360 |
|
1361 ADD(IAC); |
|
1362 ADD(SE); |
|
1363 |
|
1364 writenet(statusbuf, ncp - statusbuf); |
|
1365 netflush(); /* Send it on its way */ |
|
1366 |
|
1367 DIAG(TD_OPTIONS, |
|
1368 {printsub('>', statusbuf, ncp - statusbuf); netflush();}); |
|
1369 } |