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