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