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