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