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