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