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