VFLUSHO -> VDISCARD
[unix-history] / usr / src / libexec / telnetd / state.c
CommitLineData
ea139302
PB
1/*
2 * Copyright (c) 1989 Regents of the University of California.
3 * All rights reserved.
4 *
836fe169 5 * %sccs.include.redist.c%
ea139302
PB
6 */
7
8#ifndef lint
836fe169 9static char sccsid[] = "@(#)state.c 5.6 (Berkeley) %G%";
ea139302
PB
10#endif /* not lint */
11
12#include "telnetd.h"
13
14char doopt[] = { IAC, DO, '%', 'c', 0 };
15char dont[] = { IAC, DONT, '%', 'c', 0 };
16char will[] = { IAC, WILL, '%', 'c', 0 };
17char wont[] = { IAC, WONT, '%', 'c', 0 };
18int not42 = 1;
19
20/*
21 * Buffer for sub-options, and macros
22 * for suboptions buffer manipulations
23 */
24char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
25
26#define SB_CLEAR() subpointer = subbuffer;
27#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
28#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
29 *subpointer++ = (c); \
30 }
31#define SB_GET() ((*subpointer++)&0xff)
32#define SB_EOF() (subpointer >= subend)
33
34
35
36/*
37 * State for recv fsm
38 */
39#define TS_DATA 0 /* base state */
40#define TS_IAC 1 /* look for double IAC's */
41#define TS_CR 2 /* CR-LF ->'s CR */
42#define TS_SB 3 /* throw away begin's... */
43#define TS_SE 4 /* ...end's (suboption negotiation) */
44#define TS_WILL 5 /* will option negotiation */
45#define TS_WONT 6 /* wont " */
46#define TS_DO 7 /* do " */
47#define TS_DONT 8 /* dont " */
48
49telrcv()
50{
51 register int c;
52 static int state = TS_DATA;
ed8f31c1 53#if defined(CRAY2) && defined(UNICOS5)
ea139302
PB
54 char *opfrontp = pfrontp;
55#endif
56
57 while (ncc > 0) {
58 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
59 break;
60 c = *netip++ & 0377, ncc--;
61 switch (state) {
62
63 case TS_CR:
64 state = TS_DATA;
65 /* Strip off \n or \0 after a \r */
66 if ((c == 0) || (c == '\n')) {
67 break;
68 }
69 /* FALL THROUGH */
70
71 case TS_DATA:
72 if (c == IAC) {
73 state = TS_IAC;
74 break;
75 }
76 /*
77 * We now map \r\n ==> \r for pragmatic reasons.
78 * Many client implementations send \r\n when
79 * the user hits the CarriageReturn key.
80 *
81 * We USED to map \r\n ==> \n, since \r\n says
82 * that we want to be in column 1 of the next
83 * printable line, and \n is the standard
84 * unix way of saying that (\r is only good
85 * if CRMOD is set, which it normally is).
86 */
87 if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
88 /*
89 * If we are operating in linemode,
90 * convert to local end-of-line.
91 */
92 if ((linemode) && (ncc > 0)&&('\n' == *netip)) {
93 netip++; ncc--;
94 c = '\n';
95 } else {
96 state = TS_CR;
97 }
98 }
99 *pfrontp++ = c;
100 break;
101
102 case TS_IAC:
103gotiac: switch (c) {
104
105 /*
106 * Send the process on the pty side an
107 * interrupt. Do this with a NULL or
108 * interrupt char; depending on the tty mode.
109 */
110 case IP:
111 interrupt();
112 break;
113
114 case BREAK:
115 sendbrk();
116 break;
117
118 /*
119 * Are You There?
120 */
121 case AYT:
122 (void) strcpy(nfrontp, "\r\n[Yes]\r\n");
123 nfrontp += 9;
124 break;
125
126 /*
127 * Abort Output
128 */
129 case AO:
130 {
131 ptyflush(); /* half-hearted */
132 init_termbuf();
133
134 if (slctab[SLC_AO].sptr &&
ed8f31c1
PB
135 *slctab[SLC_AO].sptr != (cc_t)-1) {
136 *pfrontp++ =
137 (unsigned char)*slctab[SLC_AO].sptr;
ea139302
PB
138 }
139
140 netclear(); /* clear buffer back */
141 *nfrontp++ = IAC;
142 *nfrontp++ = DM;
143 neturg = nfrontp-1; /* off by one XXX */
144 break;
145 }
146
147 /*
148 * Erase Character and
149 * Erase Line
150 */
151 case EC:
152 case EL:
153 {
ed8f31c1 154 cc_t ch;
ea139302
PB
155
156 ptyflush(); /* half-hearted */
157 init_termbuf();
158 ch = (c == EC) ? *slctab[SLC_EC].sptr :
159 *slctab[SLC_EL].sptr;
ed8f31c1
PB
160 if (ch != (cc_t)-1)
161 *pfrontp++ = (unsigned char)ch;
ea139302
PB
162 break;
163 }
164
165 /*
166 * Check for urgent data...
167 */
168 case DM:
169 SYNCHing = stilloob(net);
170 settimer(gotDM);
171 break;
172
173
174 /*
175 * Begin option subnegotiation...
176 */
177 case SB:
178 state = TS_SB;
179 SB_CLEAR();
180 continue;
181
182 case WILL:
183 state = TS_WILL;
184 continue;
185
186 case WONT:
187 state = TS_WONT;
188 continue;
189
190 case DO:
191 state = TS_DO;
192 continue;
193
194 case DONT:
195 state = TS_DONT;
196 continue;
197 case EOR:
198 if (hisopts[TELOPT_EOR])
199 doeof();
200 break;
201
202 /*
203 * Handle RFC 10xx Telnet linemode option additions
204 * to command stream (EOF, SUSP, ABORT).
205 */
206 case xEOF:
207 doeof();
208 break;
209
210 case SUSP:
211 sendsusp();
212 break;
213
214 case ABORT:
215 sendbrk();
216 break;
217
218 case IAC:
219 *pfrontp++ = c;
220 break;
221 }
222 state = TS_DATA;
223 break;
224
225 case TS_SB:
226 if (c == IAC) {
227 state = TS_SE;
228 } else {
229 SB_ACCUM(c);
230 }
231 break;
232
233 case TS_SE:
234 if (c != SE) {
235 if (c != IAC) {
236 /*
237 * bad form of suboption negotiation.
238 * handle it in such a way as to avoid
239 * damage to local state. Parse
240 * suboption buffer found so far,
241 * then treat remaining stream as
242 * another command sequence.
243 */
244 SB_TERM();
245 suboption();
246 state = TS_IAC;
247 goto gotiac;
248 }
249 SB_ACCUM(c);
250 state = TS_SB;
251 } else {
252 SB_TERM();
253 suboption(); /* handle sub-option */
254 state = TS_DATA;
255 }
256 break;
257
258 case TS_WILL:
053fd49d 259 willoption(c);
ea139302
PB
260 state = TS_DATA;
261 continue;
262
263 case TS_WONT:
053fd49d 264 wontoption(c);
ea139302
PB
265 state = TS_DATA;
266 continue;
267
268 case TS_DO:
053fd49d 269 dooption(c);
ea139302
PB
270 state = TS_DATA;
271 continue;
272
273 case TS_DONT:
053fd49d 274 dontoption(c);
ea139302
PB
275 state = TS_DATA;
276 continue;
277
278 default:
279 syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
280 printf("telnetd: panic state=%d\n", state);
281 exit(1);
282 }
283 }
ed8f31c1 284#if defined(CRAY2) && defined(UNICOS5)
ea139302
PB
285 if (!linemode) {
286 char xptyobuf[BUFSIZ+NETSLOP];
287 char xbuf2[BUFSIZ];
288 register char *cp;
289 int n = pfrontp - opfrontp, oc;
290 bcopy(opfrontp, xptyobuf, n);
291 pfrontp = opfrontp;
292 pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP,
293 xbuf2, &oc, BUFSIZ);
294 for (cp = xbuf2; oc > 0; --oc)
295 if ((*nfrontp++ = *cp++) == IAC)
296 *nfrontp++ = IAC;
297 }
ed8f31c1 298#endif /* defined(CRAY2) && defined(UNICOS5) */
ea139302
PB
299} /* end of telrcv */
300
301/*
302 * The will/wont/do/dont state machines are based on Dave Borman's
303 * Telnet option processing state machine. We keep track of the full
304 * state of the option negotiation with the following state variables
305 * myopts, hisopts - The last fully negotiated state for each
306 * side of the connection.
307 * mywants, hiswants - The state we wish to be in after a completed
308 * negotiation. (hiswants is slightly misleading,
309 * this is more precisely the state I want him to
310 * be in.
311 * resp - We count the number of requests we have sent out.
312 *
313 * These correspond to the following states:
314 * my_state = the last negotiated state
315 * want_state = what I want the state to go to
316 * want_resp = how many requests I have sent
317 * All state defaults are negative, and resp defaults to 0.
318 *
319 * When initiating a request to change state to new_state:
320 *
321 * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
322 * do nothing;
323 * } else {
324 * want_state = new_state;
325 * send new_state;
326 * want_resp++;
327 * }
328 *
329 * When receiving new_state:
330 *
331 * if (want_resp) {
332 * want_resp--;
333 * if (want_resp && (new_state == my_state))
334 * want_resp--;
335 * }
336 * if ((want_resp == 0) && (new_state != want_state)) {
337 * if (ok_to_switch_to new_state)
338 * want_state = new_state;
339 * else
340 * want_resp++;
341 * send want_state;
342 * }
343 * my_state = new_state;
344 *
345 * Note that new_state is implied in these functions by the function itself.
346 * will and do imply positive new_state, wont and dont imply negative.
347 *
348 * Finally, there is one catch. If we send a negative response to a
349 * positive request, my_state will be the positive while want_state will
350 * remain negative. my_state will revert to negative when the negative
351 * acknowlegment arrives from the peer. Thus, my_state generally tells
352 * us not only the last negotiated state, but also tells us what the peer
353 * wants to be doing as well. It is important to understand this difference
354 * as we may wish to be processing data streams based on our desired state
355 * (want_state) or based on what the peer thinks the state is (my_state).
356 *
357 * This all works fine because if the peer sends a positive request, the data
358 * that we receive prior to negative acknowlegment will probably be affected
359 * by the positive state, and we can process it as such (if we can; if we
360 * can't then it really doesn't matter). If it is that important, then the
361 * peer probably should be buffering until this option state negotiation
362 * is complete.
363 *
ea139302 364 */
053fd49d
PB
365send_do(option, init)
366 int option, init;
367{
368 if (init) {
369 if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_YES) ||
370 hiswants[option] == OPT_YES)
371 return;
cb781470
PB
372 /*
373 * Special case for TELOPT_TM: We send a DO, but pretend
374 * that we sent a DONT, so that we can send more DOs if
375 * we want to.
376 */
377 if (option == TELOPT_TM)
378 hiswants[option] = OPT_NO;
379 else
380 hiswants[option] = OPT_YES;
053fd49d
PB
381 do_dont_resp[option]++;
382 }
383 (void) sprintf(nfrontp, doopt, option);
384 nfrontp += sizeof (dont) - 2;
385}
386
387willoption(option)
388 int option;
ea139302
PB
389{
390 int changeok = 0;
ea139302 391
053fd49d
PB
392 /*
393 * process input from peer.
394 */
395
396 if (do_dont_resp[option]) {
397 do_dont_resp[option]--;
398 if (do_dont_resp[option] && hisopts[option] == OPT_YES)
399 do_dont_resp[option]--;
400 }
cb781470
PB
401 if (do_dont_resp[option] == 0) {
402 if (hiswants[option] != OPT_YES) {
ea139302
PB
403 switch (option) {
404
405 case TELOPT_BINARY:
406 init_termbuf();
407 tty_binaryin(1);
408 set_termbuf();
409 changeok++;
410 break;
411
412 case TELOPT_ECHO:
053fd49d
PB
413 not42 = 0; /* looks like a 4.2 system */
414#ifdef notdef
ea139302 415 /*
053fd49d
PB
416 * Now, in a 4.2 system, to break them out of
417 * ECHOing (to the terminal) mode, we need to
418 * send a WILL ECHO.
ea139302
PB
419 */
420 if (myopts[TELOPT_ECHO] == OPT_YES) {
053fd49d 421 send_will(TELOPT_ECHO, 1);
ea139302 422 }
053fd49d
PB
423#else
424 /*
425 * "WILL ECHO". Kludge upon kludge!
426 * A 4.2 client is now echoing user input at
427 * the tty. This is probably undesireable and
428 * it should be stopped. The client will
429 * respond WONT TM to the DO TM that we send to
430 * check for kludge linemode. When the WONT TM
431 * arrives, linemode will be turned off and a
432 * change propogated to the pty. This change
433 * will cause us to process the new pty state
434 * in localstat(), which will notice that
435 * linemode is off and send a WILL ECHO
436 * so that we are properly in character mode and
437 * all is well.
438 */
439#endif
ea139302
PB
440 /*
441 * Fool the state machine into sending a don't.
053fd49d
PB
442 * This also allows the initial echo sending
443 * code to break out of the loop that it is
ea139302
PB
444 * in. (Look in telnet())
445 */
446 hiswants[TELOPT_ECHO] = OPT_NO;
447 break;
448
449 case TELOPT_TM:
450#if defined(LINEMODE) && defined(KLUDGELINEMODE)
451 /*
053fd49d
PB
452 * This telnetd implementation does not really
453 * support timing marks, it just uses them to
454 * support the kludge linemode stuff. If we
455 * receive a will or wont TM in response to our
456 * do TM request that may have been sent to
457 * determine kludge linemode support, process
458 * it, otherwise TM should get a negative
459 * response back.
ea139302
PB
460 */
461 /*
462 * Handle the linemode kludge stuff.
463 * If we are not currently supporting any
464 * linemode at all, then we assume that this
465 * is the client telling us to use kludge
466 * linemode in response to our query. Set the
467 * linemode type that is to be supported, note
468 * that the client wishes to use linemode, and
469 * eat the will TM as though it never arrived.
470 */
471 if (lmodetype < KLUDGE_LINEMODE) {
472 lmodetype = KLUDGE_LINEMODE;
473 clientstat(TELOPT_LINEMODE, WILL, 0);
053fd49d 474 send_wont(TELOPT_SGA, 1);
ea139302
PB
475 }
476#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
477 /*
cb781470
PB
478 * We never respond to a WILL TM, and
479 * we leave the state OPT_NO.
ea139302 480 */
ea139302
PB
481 return;
482
483 case TELOPT_LFLOW:
484 /*
053fd49d
PB
485 * If we are going to support flow control
486 * option, then don't worry peer that we can't
487 * change the flow control characters.
ea139302
PB
488 */
489 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
490 slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
491 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
492 slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
493 case TELOPT_TTYPE:
494 case TELOPT_SGA:
495 case TELOPT_NAWS:
496 case TELOPT_TSPEED:
cb781470
PB
497 changeok++;
498 break;
499
ea139302
PB
500#ifdef LINEMODE
501 case TELOPT_LINEMODE:
cb781470
PB
502# ifdef KLUDGELINEMODE
503 /*
504 * Note client's desire to use linemode.
505 */
506 lmodetype = REAL_LINEMODE;
507# endif /* KLUDGELINEMODE */
508 clientstat(TELOPT_LINEMODE, WILL, 0);
ea139302
PB
509 changeok++;
510 break;
cb781470 511#endif /* LINEMODE */
ea139302
PB
512
513 default:
514 break;
515 }
053fd49d 516 if (changeok) {
ea139302 517 hiswants[option] = OPT_YES;
053fd49d
PB
518 send_do(option, 0);
519 } else {
520 do_dont_resp[option]++;
521 send_dont(option, 0);
ea139302 522 }
cb781470 523 }
ea139302 524 }
053fd49d 525 hisopts[option] = OPT_YES;
ea139302
PB
526} /* end of willoption */
527
053fd49d
PB
528send_dont(option, init)
529 int option, init;
530{
531 if (init) {
532 if ((do_dont_resp[option] == 0 && hisopts[option] == OPT_NO) ||
533 hiswants[option] == OPT_NO)
534 return;
535 hiswants[option] = OPT_NO;
536 do_dont_resp[option]++;
537 }
538 (void) sprintf(nfrontp, dont, option);
539 nfrontp += sizeof (doopt) - 2;
540}
541
542wontoption(option)
543 int option;
ea139302
PB
544{
545 char *fmt = (char *)0;
546
547 /*
548 * Process client input.
549 */
053fd49d
PB
550
551 if (do_dont_resp[option]) {
552 do_dont_resp[option]--;
553 if (do_dont_resp[option] && hisopts[option] == OPT_NO)
554 do_dont_resp[option]--;
555 }
cb781470
PB
556 if (do_dont_resp[option] == 0) {
557 if (hiswants[option] != OPT_NO) {
053fd49d 558 /* it is always ok to change to negative state */
ea139302
PB
559 switch (option) {
560 case TELOPT_ECHO:
053fd49d 561 not42 = 1; /* doesn't seem to be a 4.2 system */
ea139302
PB
562 break;
563
564 case TELOPT_BINARY:
565 init_termbuf();
566 tty_binaryin(0);
567 set_termbuf();
568 break;
569
570#ifdef LINEMODE
571 case TELOPT_LINEMODE:
572# ifdef KLUDGELINEMODE
573 /*
574 * If real linemode is supported, then client is
575 * asking to turn linemode off.
576 */
577 if (lmodetype == REAL_LINEMODE)
578# endif /* KLUDGELINEMODE */
579 clientstat(TELOPT_LINEMODE, WONT, 0);
580 break;
581#endif LINEMODE
582
583 case TELOPT_TM:
ea139302 584 /*
053fd49d
PB
585 * If we get a WONT TM, and had sent a DO TM,
586 * don't respond with a DONT TM, just leave it
587 * as is. Short circut the state machine to
cb781470 588 * achive this.
ea139302
PB
589 */
590 hiswants[TELOPT_TM] = OPT_NO;
ea139302
PB
591 return;
592
593 case TELOPT_LFLOW:
594 /*
053fd49d
PB
595 * If we are not going to support flow control
596 * option, then let peer know that we can't
597 * change the flow control characters.
ea139302
PB
598 */
599 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
600 slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
601 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
602 slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
603 break;
604
605 default:
606 break;
607 }
053fd49d
PB
608 hiswants[option] = OPT_NO;
609 fmt = dont;
610 send_dont(option, 0);
cb781470
PB
611 } else {
612 switch (option) {
613 case TELOPT_TM:
614#if defined(LINEMODE) && defined(KLUDGELINEMODE)
615 if (lmodetype < REAL_LINEMODE) {
616 lmodetype = NO_LINEMODE;
617 clientstat(TELOPT_LINEMODE, WONT, 0);
618 send_will(TELOPT_SGA, 1);
619/*@*/ send_will(TELOPT_ECHO, 1);
620 }
621#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
622 default:
623 break;
624 }
625 }
ea139302 626 }
053fd49d 627 hisopts[option] = OPT_NO;
ea139302 628
053fd49d 629} /* end of wontoption */
ea139302 630
053fd49d
PB
631send_will(option, init)
632 int option, init;
633{
634 if (init) {
635 if ((will_wont_resp[option] == 0 && myopts[option] == OPT_YES)||
636 mywants[option] == OPT_YES)
637 return;
638 mywants[option] = OPT_YES;
639 will_wont_resp[option]++;
ea139302 640 }
053fd49d
PB
641 (void) sprintf(nfrontp, will, option);
642 nfrontp += sizeof (doopt) - 2;
643}
ea139302 644
053fd49d
PB
645dooption(option)
646 int option;
ea139302
PB
647{
648 int changeok = 0;
ea139302
PB
649
650 /*
651 * Process client input.
652 */
053fd49d
PB
653
654 if (will_wont_resp[option]) {
655 will_wont_resp[option]--;
656 if (will_wont_resp[option] && myopts[option] == OPT_YES)
657 will_wont_resp[option]--;
658 }
659 if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_YES)) {
ea139302
PB
660 switch (option) {
661 case TELOPT_ECHO:
662#ifdef LINEMODE
053fd49d 663 if (lmodetype == NO_LINEMODE) {
ea139302
PB
664#endif
665 init_termbuf();
666 tty_setecho(1);
667 set_termbuf();
668#ifdef LINEMODE
669 }
670#endif
671 changeok++;
672 break;
673
674 case TELOPT_BINARY:
675 init_termbuf();
676 tty_binaryout(1);
677 set_termbuf();
678 changeok++;
679 break;
680
681 case TELOPT_SGA:
682#if defined(LINEMODE) && defined(KLUDGELINEMODE)
683 /*
053fd49d
PB
684 * If kludge linemode is in use, then we must
685 * process an incoming do SGA for linemode
686 * purposes.
ea139302
PB
687 */
688 if (lmodetype == KLUDGE_LINEMODE) {
689 /*
053fd49d
PB
690 * Receipt of "do SGA" in kludge
691 * linemode is the peer asking us to
692 * turn off linemode. Make note of
693 * the request.
ea139302
PB
694 */
695 clientstat(TELOPT_LINEMODE, WONT, 0);
696 /*
053fd49d
PB
697 * If linemode did not get turned off
698 * then don't tell peer that we did.
699 * Breaking here forces a wont SGA to
700 * be returned.
ea139302
PB
701 */
702 if (linemode)
703 break;
704 }
705#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
706 changeok++;
707 break;
708
709 case TELOPT_STATUS:
710 changeok++;
711 break;
712
713 case TELOPT_TM:
053fd49d
PB
714 /*
715 * Special case for TM. We send a WILL, but
716 * pretend we sent a WONT.
717 */
718 send_will(option, 0);
719 mywants[option] = OPT_NO;
720 myopts[option] = OPT_NO;
721 return;
722
ea139302
PB
723 case TELOPT_LINEMODE:
724 case TELOPT_TTYPE:
725 case TELOPT_NAWS:
726 case TELOPT_TSPEED:
727 case TELOPT_LFLOW:
728 default:
729 break;
730 }
053fd49d 731 if (changeok) {
ea139302 732 mywants[option] = OPT_YES;
053fd49d
PB
733 send_will(option, 0);
734 } else {
735 will_wont_resp[option]++;
736 send_wont(option, 0);
ea139302 737 }
ea139302 738 }
053fd49d 739 myopts[option] = OPT_YES;
ea139302
PB
740
741} /* end of dooption */
742
053fd49d
PB
743send_wont(option, init)
744 int option, init;
ea139302 745{
053fd49d
PB
746 if (init) {
747 if ((will_wont_resp[option] == 0 && myopts[option] == OPT_NO) ||
748 mywants[option] == OPT_NO)
749 return;
750 mywants[option] = OPT_NO;
751 will_wont_resp[option]++;
752 }
753 (void) sprintf(nfrontp, wont, option);
754 nfrontp += sizeof (wont) - 2;
755}
ea139302 756
053fd49d
PB
757dontoption(option)
758 int option;
759{
ea139302
PB
760 /*
761 * Process client input.
762 */
ed8f31c1 763
053fd49d
PB
764 if (will_wont_resp[option]) {
765 will_wont_resp[option]--;
766 if (will_wont_resp[option] && myopts[option] == OPT_NO)
767 will_wont_resp[option]--;
768 }
769 if ((will_wont_resp[option] == 0) && (mywants[option] != OPT_NO)) {
ea139302
PB
770 switch (option) {
771 case TELOPT_BINARY:
772 init_termbuf();
773 tty_binaryout(0);
774 set_termbuf();
775 break;
776
053fd49d 777 case TELOPT_ECHO: /* we should stop echoing */
ea139302 778#ifdef LINEMODE
053fd49d 779 if (lmodetype == NO_LINEMODE) {
ea139302
PB
780#endif
781 init_termbuf();
782 tty_setecho(0);
783 set_termbuf();
784#ifdef LINEMODE
785 }
786#endif
787 break;
788
789 case TELOPT_SGA:
790#if defined(LINEMODE) && defined(KLUDGELINEMODE)
791 /*
053fd49d
PB
792 * If kludge linemode is in use, then we
793 * must process an incoming do SGA for
794 * linemode purposes.
ea139302
PB
795 */
796 if (lmodetype == KLUDGE_LINEMODE) {
797 /*
053fd49d
PB
798 * The client is asking us to turn
799 * linemode on.
ea139302
PB
800 */
801 clientstat(TELOPT_LINEMODE, WILL, 0);
802 /*
053fd49d
PB
803 * If we did not turn line mode on,
804 * then what do we say? Will SGA?
805 * This violates design of telnet.
806 * Gross. Very Gross.
ea139302
PB
807 */
808 }
809#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
810
811 default:
812 break;
813 }
ea139302 814
053fd49d
PB
815 mywants[option] = OPT_NO;
816 send_wont(option, 0);
ea139302 817 }
053fd49d 818 myopts[option] = OPT_NO;
ea139302
PB
819
820} /* end of dontoption */
821
822/*
823 * suboption()
824 *
825 * Look at the sub-option buffer, and try to be helpful to the other
826 * side.
827 *
828 * Currently we recognize:
829 *
830 * Terminal type is
831 * Linemode
832 * Window size
833 * Terminal speed
834 */
835suboption()
836{
837 register int subchar;
838
839 subchar = SB_GET();
840 switch (subchar) {
841 case TELOPT_TSPEED: {
842 register int xspeed, rspeed;
843
844 if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */
845 break;
846
847 settimer(tspeedsubopt);
848
849 if (SB_EOF() || SB_GET() != TELQUAL_IS)
850 return;
851
852 xspeed = atoi(subpointer);
853
854 while (SB_GET() != ',' && !SB_EOF());
855 if (SB_EOF())
856 return;
857
858 rspeed = atoi(subpointer);
859 clientstat(TELOPT_TSPEED, xspeed, rspeed);
860
861 break;
862
863 } /* end of case TELOPT_TSPEED */
864
865 case TELOPT_TTYPE: { /* Yaaaay! */
866 static char terminalname[5+41] = "TERM=";
867
ca8b2d5c 868 if (hisopts[TELOPT_TTYPE] == OPT_NO) /* Ignore if option disabled */
ea139302
PB
869 break;
870 settimer(ttypesubopt);
871
872 if (SB_GET() != TELQUAL_IS) {
873 return; /* ??? XXX but, this is the most robust */
874 }
875
876 terminaltype = terminalname+sizeof("TERM=")-1;
877
878 while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
879 !SB_EOF()) {
880 register int c;
881
882 c = SB_GET();
883 if (isupper(c)) {
884 c = tolower(c);
885 }
886 *terminaltype++ = c; /* accumulate name */
887 }
888 *terminaltype = 0;
889 terminaltype = terminalname;
890 break;
891 } /* end of case TELOPT_TTYPE */
892
893 case TELOPT_NAWS: {
894 register int xwinsize, ywinsize;
895
896 if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */
897 break;
898
899 if (SB_EOF())
900 return;
901 xwinsize = SB_GET() << 8;
902 if (SB_EOF())
903 return;
904 xwinsize |= SB_GET();
905 if (SB_EOF())
906 return;
907 ywinsize = SB_GET() << 8;
908 if (SB_EOF())
909 return;
910 ywinsize |= SB_GET();
911 clientstat(TELOPT_NAWS, xwinsize, ywinsize);
912
913 break;
914
915 } /* end of case TELOPT_NAWS */
916
917#ifdef LINEMODE
918 case TELOPT_LINEMODE: {
919 register int request;
920
921 if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */
922 break;
923 /*
924 * Process linemode suboptions.
925 */
926 if (SB_EOF()) break; /* garbage was sent */
927 request = SB_GET(); /* get will/wont */
928 if (SB_EOF()) break; /* another garbage check */
929
930 if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */
931 /*
932 * Process suboption buffer of slc's
933 */
934 start_slc(1);
935 do_opt_slc(subpointer, subend - subpointer);
936 end_slc(0);
937
938 } else if (request == LM_MODE) {
939 useeditmode = SB_GET(); /* get mode flag */
940 clientstat(LM_MODE, 0, 0);
941 }
942
943 switch (SB_GET()) { /* what suboption? */
944 case LM_FORWARDMASK:
945 /*
946 * According to spec, only server can send request for
947 * forwardmask, and client can only return a positive response.
948 * So don't worry about it.
949 */
950
951 default:
952 break;
953 }
ed8f31c1 954 break;
ea139302
PB
955 } /* end of case TELOPT_LINEMODE */
956#endif
957 case TELOPT_STATUS: {
958 int mode;
959
960 mode = SB_GET();
961 switch (mode) {
962 case TELQUAL_SEND:
963 if (myopts[TELOPT_STATUS] == OPT_YES)
964 send_status();
965 break;
966
967 case TELQUAL_IS:
968 break;
969
970 default:
971 break;
972 }
ed8f31c1
PB
973 break;
974 } /* end of case TELOPT_STATUS */
ea139302
PB
975
976 default:
977 break;
978 } /* end of switch */
979
980} /* end of suboption */
981
982#define ADD(c) *ncp++ = c;
983#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
984send_status()
985{
986 char statusbuf[256];
987 register char *ncp;
988 register int i;
989
990 ncp = statusbuf;
991
992 netflush(); /* get rid of anything waiting to go out */
993
994 ADD(IAC);
995 ADD(SB);
996 ADD(TELOPT_STATUS);
997 ADD(TELQUAL_IS);
998
999 for (i = 0; i < NTELOPTS; i++) {
1000 if (myopts[i] == OPT_YES) {
1001 ADD(WILL);
1002 ADD_DATA(i);
1003 if (i == IAC)
1004 ADD(IAC);
1005 }
1006 if (hisopts[i] == OPT_YES) {
1007 ADD(DO);
1008 ADD_DATA(i);
1009 if (i == IAC)
1010 ADD(IAC);
1011 }
1012 }
1013
1014#ifdef LINEMODE
1015 if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
1016 char *cp, *cpe;
1017 int len;
1018
1019 ADD(SB);
1020 ADD(TELOPT_LINEMODE);
1021 ADD(LM_MODE);
1022 ADD_DATA(editmode);
1023 if (editmode == IAC)
1024 ADD(IAC);
1025 ADD(SE);
1026
1027 ADD(SB);
1028 ADD(TELOPT_LINEMODE);
1029 ADD(LM_SLC);
1030 start_slc(0);
1031 send_slc();
1032 len = end_slc(&cp);
1033 for (cpe = cp + len; cp < cpe; cp++)
1034 ADD_DATA(*cp);
1035 ADD(SE);
1036 }
1037#endif /* LINEMODE */
1038
1039 ADD(IAC);
1040 ADD(SE);
1041
1042 writenet(statusbuf, ncp - statusbuf);
1043 netflush(); /* Send it on its way */
1044}