New telnetd, with LINEMODE support
[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
19static char sccsid[] = "@(#)state.c 5.1 (Berkeley) %G%";
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:
268 willoption(c, 0);
269 state = TS_DATA;
270 continue;
271
272 case TS_WONT:
273 wontoption(c, 0);
274 state = TS_DATA;
275 continue;
276
277 case TS_DO:
278 dooption(c, 0);
279 state = TS_DATA;
280 continue;
281
282 case TS_DONT:
283 dontoption(c, 0);
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 */
377willoption(option, request)
378 int option, request;
379{
380 int changeok = 0;
381 char *fmt = (char *)0;
382
383 /*
384 * process input from peer.
385 */
386 if (request == 0) {
387 switch (option) {
388
389 case TELOPT_BINARY:
390 init_termbuf();
391 tty_binaryin(1);
392 set_termbuf();
393 changeok++;
394 break;
395
396 case TELOPT_ECHO:
397 not42 = 0; /* looks like a 4.2 system */
398 /*
399 * Now, in a 4.2 system, to break them out of ECHOing
400 * (to the terminal) mode, we need to send a
401 * "WILL ECHO". Kludge upon kludge!
402 */
403 if (myopts[TELOPT_ECHO] == OPT_YES) {
404 dooption(TELOPT_ECHO, 1);
405 }
406 /*
407 * Fool the state machine into sending a don't.
408 * This also allows the initial echo sending code to
409 * break out of the loop that it is
410 * in. (Look in telnet())
411 */
412 hiswants[TELOPT_ECHO] = OPT_NO;
413 break;
414
415 case TELOPT_TM:
416#if defined(LINEMODE) && defined(KLUDGELINEMODE)
417 /*
418 * This telnetd implementation does not really support
419 * timing marks, it just uses them to support the kludge
420 * linemode stuff. If we receive a will or wont TM in
421 * response to our do TM request that may have been sent
422 * to determine kludge linemode support, process it,
423 * otherwise TM should get a negative response back.
424 */
425 /*
426 * Handle the linemode kludge stuff.
427 * If we are not currently supporting any
428 * linemode at all, then we assume that this
429 * is the client telling us to use kludge
430 * linemode in response to our query. Set the
431 * linemode type that is to be supported, note
432 * that the client wishes to use linemode, and
433 * eat the will TM as though it never arrived.
434 */
435 if (lmodetype < KLUDGE_LINEMODE) {
436 lmodetype = KLUDGE_LINEMODE;
437 clientstat(TELOPT_LINEMODE, WILL, 0);
438 dontoption(TELOPT_SGA, 0);
439 }
440#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
441 /*
442 * cheat the state machine so that it
443 * looks like we never sent the TM at
444 * all. The bad part of this is that
445 * if the client sends a will TM on his
446 * own to turn on linemode, then he
447 * won't get a response.
448 */
449 hiswants[TELOPT_TM] = OPT_NO;
450 resp[TELOPT_TM]--;
451 return;
452
453 case TELOPT_LFLOW:
454 /*
455 * If we are going to support flow control option,
456 * then don't worry peer that we can't change the
457 * flow control characters.
458 */
459 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
460 slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
461 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
462 slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
463 case TELOPT_TTYPE:
464 case TELOPT_SGA:
465 case TELOPT_NAWS:
466 case TELOPT_TSPEED:
467#ifdef LINEMODE
468 case TELOPT_LINEMODE:
469#endif LINEMODE
470 changeok++;
471 break;
472
473 default:
474 break;
475 }
476
477 }
478
479 if (request) {
480 if (!((resp[option] == 0 && hisopts[option] == OPT_YES) ||
481 hiswants[option] == OPT_YES)) {
482 hiswants[option] = OPT_YES;
483 fmt = doopt;
484 resp[option]++;
485 }
486 } else {
487 if (resp[option]) {
488 resp[option]--;
489 if (resp[option] && hisopts[option] == OPT_YES)
490 resp[option]--;
491 }
492 if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) {
493 if (changeok)
494 hiswants[option] = OPT_YES;
495 else
496 resp[option]++;
497 fmt = (hiswants[option] ? doopt : dont);
498 }
499 hisopts[option] = OPT_YES;
500 }
501
502 if (fmt) {
503 (void) sprintf(nfrontp, fmt, option);
504 nfrontp += sizeof (dont) - 2;
505 }
506
507 /*
508 * Handle other processing that should occur after we have
509 * responded to client input.
510 */
511 if (!request) {
512 switch (option) {
513#ifdef LINEMODE
514 case TELOPT_LINEMODE:
515# ifdef KLUDGELINEMODE
516 /*
517 * Note client's desire to use linemode.
518 */
519 lmodetype = REAL_LINEMODE;
520# endif /* KLUDGELINEMODE */
521 clientstat(TELOPT_LINEMODE, WILL, 0);
522 break;
523#endif LINEMODE
524
525 default:
526 break;
527 }
528 }
529
530} /* end of willoption */
531
532wontoption(option, request)
533 int option, request;
534{
535 char *fmt = (char *)0;
536
537 /*
538 * Process client input.
539 */
540 if (!request) {
541 switch (option) {
542 case TELOPT_ECHO:
543 not42 = 1; /* doesn't seem to be a 4.2 system */
544 break;
545
546 case TELOPT_BINARY:
547 init_termbuf();
548 tty_binaryin(0);
549 set_termbuf();
550 break;
551
552#ifdef LINEMODE
553 case TELOPT_LINEMODE:
554# ifdef KLUDGELINEMODE
555 /*
556 * If real linemode is supported, then client is
557 * asking to turn linemode off.
558 */
559 if (lmodetype == REAL_LINEMODE)
560# endif /* KLUDGELINEMODE */
561 clientstat(TELOPT_LINEMODE, WONT, 0);
562 break;
563#endif LINEMODE
564
565 case TELOPT_TM:
566#if defined(LINEMODE) && defined(KLUDGELINEMODE)
567 if (lmodetype < REAL_LINEMODE) {
568 lmodetype = NO_LINEMODE;
569 clientstat(TELOPT_LINEMODE, WONT, 0);
570 dooption(TELOPT_SGA, 0);
571 }
572#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
573 /*
574 * If we get a WONT TM, and had sent a DO TM, don't
575 * respond with a DONT TM, just leave it as is.
576 * Short circut the state machine to achive this.
577 * The bad part of this is that if the client sends
578 * a WONT TM on his own to turn off linemode, then he
579 * won't get a response.
580 */
581 hiswants[TELOPT_TM] = OPT_NO;
582 resp[TELOPT_TM]--;
583 return;
584
585 case TELOPT_LFLOW:
586 /*
587 * If we are not going to support flow control option,
588 * then let peer know that we can't change the
589 * flow control characters.
590 */
591 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
592 slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
593 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
594 slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
595 break;
596
597 default:
598 break;
599 }
600 }
601
602
603 if (request) {
604 if (!((resp[option] == 0 && hisopts[option] == OPT_NO) ||
605 hiswants[option] == OPT_NO)) {
606 hiswants[option] = OPT_NO;
607 fmt = dont;
608 resp[option]++;
609 }
610 } else {
611 if (resp[option]) {
612 resp[option]--;
613 if (resp[option] && hisopts[option] == OPT_NO)
614 resp[option]--;
615 }
616 if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) {
617 /* it is always ok to change to negative state */
618 hiswants[option] = OPT_NO;
619 fmt = dont;
620 }
621 hisopts[option] = OPT_NO;
622 }
623
624 if (fmt) {
625 (void) sprintf(nfrontp, fmt, option);
626 nfrontp += sizeof (doopt) - 2;
627 }
628
629} /* end of wontoption */
630
631dooption(option, request)
632 int option, request;
633{
634 int changeok = 0;
635 char *fmt = (char *)0;
636
637 /*
638 * Process client input.
639 */
640 if (!request) {
641 switch (option) {
642 case TELOPT_ECHO:
643#ifdef LINEMODE
644 if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
645#endif
646 init_termbuf();
647 tty_setecho(1);
648 set_termbuf();
649#ifdef LINEMODE
650 }
651#endif
652 changeok++;
653 break;
654
655 case TELOPT_BINARY:
656 init_termbuf();
657 tty_binaryout(1);
658 set_termbuf();
659 changeok++;
660 break;
661
662 case TELOPT_SGA:
663#if defined(LINEMODE) && defined(KLUDGELINEMODE)
664 /*
665 * If kludge linemode is in use, then we must process
666 * an incoming do SGA for linemode purposes.
667 */
668 if (lmodetype == KLUDGE_LINEMODE) {
669 /*
670 * Receipt of "do SGA" in kludge linemode
671 * is the peer asking us to turn off linemode.
672 * Make note of the request.
673 */
674 clientstat(TELOPT_LINEMODE, WONT, 0);
675 /*
676 * If linemode did not get turned off then
677 * don't tell peer that we did. Breaking
678 * here forces a wont SGA to be returned.
679 */
680 if (linemode)
681 break;
682 }
683#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
684 changeok++;
685 break;
686
687 case TELOPT_STATUS:
688 changeok++;
689 break;
690
691 case TELOPT_TM:
692 case TELOPT_LINEMODE:
693 case TELOPT_TTYPE:
694 case TELOPT_NAWS:
695 case TELOPT_TSPEED:
696 case TELOPT_LFLOW:
697 default:
698 break;
699 }
700 }
701
702 if (request) {
703 if (!((resp[option] == 0 && myopts[option] == OPT_YES) ||
704 mywants[option] == OPT_YES)) {
705 mywants[option] = OPT_YES;
706 fmt = will;
707 resp[option]++;
708 }
709 } else {
710 if (resp[option]) {
711 resp[option]--;
712 if (resp[option] && myopts[option] == OPT_YES)
713 resp[option]--;
714 }
715 if ((resp[option] == 0) && (mywants[option] != OPT_YES)) {
716 if (changeok)
717 mywants[option] = OPT_YES;
718 else
719 resp[option]++;
720 fmt = (mywants[option] ? will : wont);
721 }
722 myopts[option] = OPT_YES;
723 }
724
725 if (fmt) {
726 (void) sprintf(nfrontp, fmt, option);
727 nfrontp += sizeof (doopt) - 2;
728 }
729
730} /* end of dooption */
731
732
733dontoption(option, request)
734 int option, request;
735{
736 char *fmt = (char *)0;
737
738 /*
739 * Process client input.
740 */
741 if (!request) {
742 switch (option) {
743 case TELOPT_BINARY:
744 init_termbuf();
745 tty_binaryout(0);
746 set_termbuf();
747 break;
748
749 case TELOPT_ECHO: /* we should stop echoing */
750#ifdef LINEMODE
751 if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
752#endif
753 init_termbuf();
754 tty_setecho(0);
755 set_termbuf();
756#ifdef LINEMODE
757 }
758#endif
759 break;
760
761 case TELOPT_SGA:
762#if defined(LINEMODE) && defined(KLUDGELINEMODE)
763 /*
764 * If kludge linemode is in use, then we must process an
765 * incoming do SGA for linemode purposes.
766 */
767 if (lmodetype == KLUDGE_LINEMODE) {
768 /*
769 * The client is asking us to turn linemode
770 * on.
771 */
772 clientstat(TELOPT_LINEMODE, WILL, 0);
773 /*
774 * If we did not turn line mode on, then what do
775 * we say? Will SGA? This violates design of
776 * telnet. Gross. Very Gross.
777 */
778 }
779#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
780
781 default:
782 break;
783 }
784 }
785
786 if (request) {
787 if (!((resp[option] == 0 && myopts[option] == OPT_NO) ||
788 mywants[option] == OPT_NO)) {
789 mywants[option] = OPT_NO;
790 fmt = wont;
791 resp[option]++;
792 }
793 } else {
794 if (resp[option]) {
795 resp[option]--;
796 if (resp[option] && myopts[option] == OPT_NO)
797 resp[option]--;
798 }
799 if ((resp[option] == 0) && (mywants[option] != OPT_NO)) {
800 mywants[option] = OPT_NO;
801 fmt = wont;
802 }
803 myopts[option] = OPT_NO;
804 }
805
806 if (fmt) {
807 (void) sprintf(nfrontp, fmt, option);
808 nfrontp += sizeof (wont) - 2;
809 }
810
811} /* end of dontoption */
812
813/*
814 * suboption()
815 *
816 * Look at the sub-option buffer, and try to be helpful to the other
817 * side.
818 *
819 * Currently we recognize:
820 *
821 * Terminal type is
822 * Linemode
823 * Window size
824 * Terminal speed
825 */
826suboption()
827{
828 register int subchar;
829
830 subchar = SB_GET();
831 switch (subchar) {
832 case TELOPT_TSPEED: {
833 register int xspeed, rspeed;
834
835 if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */
836 break;
837
838 settimer(tspeedsubopt);
839
840 if (SB_EOF() || SB_GET() != TELQUAL_IS)
841 return;
842
843 xspeed = atoi(subpointer);
844
845 while (SB_GET() != ',' && !SB_EOF());
846 if (SB_EOF())
847 return;
848
849 rspeed = atoi(subpointer);
850 clientstat(TELOPT_TSPEED, xspeed, rspeed);
851
852 break;
853
854 } /* end of case TELOPT_TSPEED */
855
856 case TELOPT_TTYPE: { /* Yaaaay! */
857 static char terminalname[5+41] = "TERM=";
858
859 if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */
860 break;
861 settimer(ttypesubopt);
862
863 if (SB_GET() != TELQUAL_IS) {
864 return; /* ??? XXX but, this is the most robust */
865 }
866
867 terminaltype = terminalname+sizeof("TERM=")-1;
868
869 while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
870 !SB_EOF()) {
871 register int c;
872
873 c = SB_GET();
874 if (isupper(c)) {
875 c = tolower(c);
876 }
877 *terminaltype++ = c; /* accumulate name */
878 }
879 *terminaltype = 0;
880 terminaltype = terminalname;
881 break;
882 } /* end of case TELOPT_TTYPE */
883
884 case TELOPT_NAWS: {
885 register int xwinsize, ywinsize;
886
887 if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */
888 break;
889
890 if (SB_EOF())
891 return;
892 xwinsize = SB_GET() << 8;
893 if (SB_EOF())
894 return;
895 xwinsize |= SB_GET();
896 if (SB_EOF())
897 return;
898 ywinsize = SB_GET() << 8;
899 if (SB_EOF())
900 return;
901 ywinsize |= SB_GET();
902 clientstat(TELOPT_NAWS, xwinsize, ywinsize);
903
904 break;
905
906 } /* end of case TELOPT_NAWS */
907
908#ifdef LINEMODE
909 case TELOPT_LINEMODE: {
910 register int request;
911
912 if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */
913 break;
914 /*
915 * Process linemode suboptions.
916 */
917 if (SB_EOF()) break; /* garbage was sent */
918 request = SB_GET(); /* get will/wont */
919 if (SB_EOF()) break; /* another garbage check */
920
921 if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */
922 /*
923 * Process suboption buffer of slc's
924 */
925 start_slc(1);
926 do_opt_slc(subpointer, subend - subpointer);
927 end_slc(0);
928
929 } else if (request == LM_MODE) {
930 useeditmode = SB_GET(); /* get mode flag */
931 clientstat(LM_MODE, 0, 0);
932 }
933
934 switch (SB_GET()) { /* what suboption? */
935 case LM_FORWARDMASK:
936 /*
937 * According to spec, only server can send request for
938 * forwardmask, and client can only return a positive response.
939 * So don't worry about it.
940 */
941
942 default:
943 break;
944 }
945
946 } /* end of case TELOPT_LINEMODE */
947#endif
948 case TELOPT_STATUS: {
949 int mode;
950
951 mode = SB_GET();
952 switch (mode) {
953 case TELQUAL_SEND:
954 if (myopts[TELOPT_STATUS] == OPT_YES)
955 send_status();
956 break;
957
958 case TELQUAL_IS:
959 break;
960
961 default:
962 break;
963 }
964 }
965
966 default:
967 break;
968 } /* end of switch */
969
970} /* end of suboption */
971
972#define ADD(c) *ncp++ = c;
973#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
974send_status()
975{
976 char statusbuf[256];
977 register char *ncp;
978 register int i;
979
980 ncp = statusbuf;
981
982 netflush(); /* get rid of anything waiting to go out */
983
984 ADD(IAC);
985 ADD(SB);
986 ADD(TELOPT_STATUS);
987 ADD(TELQUAL_IS);
988
989 for (i = 0; i < NTELOPTS; i++) {
990 if (myopts[i] == OPT_YES) {
991 ADD(WILL);
992 ADD_DATA(i);
993 if (i == IAC)
994 ADD(IAC);
995 }
996 if (hisopts[i] == OPT_YES) {
997 ADD(DO);
998 ADD_DATA(i);
999 if (i == IAC)
1000 ADD(IAC);
1001 }
1002 }
1003
1004#ifdef LINEMODE
1005 if (hisopts[TELOPT_LINEMODE] == OPT_YES) {
1006 char *cp, *cpe;
1007 int len;
1008
1009 ADD(SB);
1010 ADD(TELOPT_LINEMODE);
1011 ADD(LM_MODE);
1012 ADD_DATA(editmode);
1013 if (editmode == IAC)
1014 ADD(IAC);
1015 ADD(SE);
1016
1017 ADD(SB);
1018 ADD(TELOPT_LINEMODE);
1019 ADD(LM_SLC);
1020 start_slc(0);
1021 send_slc();
1022 len = end_slc(&cp);
1023 for (cpe = cp + len; cp < cpe; cp++)
1024 ADD_DATA(*cp);
1025 ADD(SE);
1026 }
1027#endif /* LINEMODE */
1028
1029 ADD(IAC);
1030 ADD(SE);
1031
1032 writenet(statusbuf, ncp - statusbuf);
1033 netflush(); /* Send it on its way */
1034}