root/src/noit_console_telnet.c

Revision ee48b2cbeba42dd8458480eac5f4d3fae7de576e, 79.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

this assignment is never used, refs #283

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2007
5  *      OmnitTI Computer Consulting, Inc.  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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by the University of
18  *      California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include "noit_defines.h"
37 #include <unistd.h>
38 #include <ctype.h>
39 #include <sys/ioctl.h>
40 #ifdef HAVE_SYS_IOCTL_COMPAT_H
41 #include <sys/ioctl_compat.h>
42 #endif
43 #define TELOPTS
44 #include <arpa/telnet.h>
45 #include "noit_console.h"
46 #include "noit_console_telnet.h"
47 #include "utils/noit_hash.h"
48
49 /*
50 RCSID("$Id: state.c,v 1.14.12.1 2004/06/21 08:21:58 lha Exp $");
51 */
52
53 unsigned char   doopt[] = { IAC, DO, '%', 'c', 0 };
54 unsigned char   dont[] = { IAC, DONT, '%', 'c', 0 };
55 unsigned char   will[] = { IAC, WILL, '%', 'c', 0 };
56 unsigned char   wont[] = { IAC, WONT, '%', 'c', 0 };
57 int     not42 = 1;
58
59 /*
60  * Buffer for sub-options, and macros
61  * for suboptions buffer manipulations
62  */
63
64 #define subbuffer ncct->telnet->_subbuffer
65 #define subpointer ncct->telnet->_subpointer
66 #define subend ncct->telnet->_subend
67 #define subsave ncct->telnet->_subsave
68 #define slctab ncct->telnet->_slctab
69 #define pty ncct->pty_master
70 #define do_dont_resp ncct->telnet->_do_dont_resp
71 #define will_wont_resp ncct->telnet->_will_wont_resp
72 #define termbuf ncct->telnet->_termbuf
73 #define termbuf2 ncct->telnet->_termbuf2
74 #define SYNCHing ncct->telnet->_SYNCHing
75 #define terminalname ncct->telnet->_terminalname
76 #define flowmode ncct->telnet->_flowmode
77 #define linemode ncct->telnet->_linemode
78 #define uselinemode ncct->telnet->_uselinemode
79 #define alwayslinemode ncct->telnet->_alwayslinemode
80 #define restartany ncct->telnet->_restartany
81 #define _terminit ncct->telnet->__terminit
82 #define turn_on_sga ncct->telnet->_turn_on_sga
83 #define useeditmode ncct->telnet->_useeditmode
84 #define editmode ncct->telnet->_editmode
85 #define def_slcbuf ncct->telnet->_def_slcbuf
86 #define def_slclen ncct->telnet->_def_slclen
87 #define slcchange ncct->telnet->_slcchange
88 #define slcptr ncct->telnet->_slcptr
89 #define slcbuf ncct->telnet->_slcbuf
90 #define def_tspeed ncct->telnet->_def_tspeed
91 #define def_rspeed ncct->telnet->_def_rspeed
92 #define def_row ncct->telnet->_def_row
93 #define def_col ncct->telnet->_def_col
94
95 #define settimer(x)     (ncct->telnet->_clocks.x = ++ncct->telnet->_clocks.system)
96 #define sequenceIs(x,y) (ncct->telnet->_clocks.x < ncct->telnet->_clocks.y)
97
98 #define dooption(a) noit_console_telnet_dooption(ncct,a)
99 #define dontoption(a) noit_console_telnet_dontoption(ncct,a)
100 #define willoption(a) noit_console_telnet_willoption(ncct,a)
101 #define wontoption(a) noit_console_telnet_wontoption(ncct,a)
102 #define send_do(a,i) noit_console_telnet_send_do(ncct,a,i)
103 #define send_dont(a,i) noit_console_telnet_send_dont(ncct,a,i)
104 #define send_will(a,i) noit_console_telnet_send_will(ncct,a,i)
105 #define send_wont(a,i) noit_console_telnet_send_wont(ncct,a,i)
106 #define send_status() noit_console_telnet_send_status(ncct)
107 #define doclientstat() noit_console_telnet_doclientstat(ncct)
108
109 #ifndef TCSIG
110 # ifdef TIOCSIG
111 #  define TCSIG TIOCSIG
112 # endif
113 #endif
114
115 static void
116 netflush(noit_console_closure_t ncct) {
117   int unused;
118   noit_console_continue_sending(ncct, &unused);
119 }
120 static void set_termbuf(noit_console_closure_t ncct);
121 static void init_termbuf(noit_console_closure_t ncct);
122 static void flowstat(noit_console_closure_t ncct);
123 static int spcset(noit_console_closure_t ncct, int func, cc_t *valp, cc_t **valpp);
124 static void suboption(noit_console_closure_t ncct);
125 static void get_slc_defaults(noit_console_closure_t ncct);
126
127 #ifdef LINEMODE
128 static void defer_terminit(noit_console_closure_t ncct);
129 static void send_slc(noit_console_closure_t ncct);
130 static void default_slc(noit_console_closure_t ncct);
131 static void add_slc(noit_console_closure_t ncct, char func, char flag, cc_t val);
132 static void start_slc(noit_console_closure_t ncct, int getit);
133 static int end_slc(noit_console_closure_t ncct, unsigned char **bufp);
134 static void process_slc(noit_console_closure_t ncct, unsigned char func, unsigned char flag, cc_t val);
135 static void change_slc(noit_console_closure_t ncct, char func, char flag, cc_t val);
136 static void check_slc(noit_console_closure_t ncct);
137 static void do_opt_slc(noit_console_closure_t ncct, unsigned char *ptr, int len);
138 #endif
139
140 #ifdef LINEMODE
141 static void
142 send_slc(noit_console_closure_t ncct)
143 {
144         int i;
145
146         /*
147          * Send out list of triplets of special characters
148          * to client.  We only send info on the characters
149          * that are currently supported.
150          */
151         for (i = 1; i <= NSLC; i++) {
152                 if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
153                         continue;
154                 add_slc(ncct, (unsigned char)i, slctab[i].current.flag,
155                                                         slctab[i].current.val);
156         }
157
158 }  /* end of send_slc */
159
160 /*
161  * default_slc
162  *
163  * Set pty special characters to all the defaults.
164  */
165 static void
166 default_slc(noit_console_closure_t ncct)
167 {
168         int i;
169
170         for (i = 1; i <= NSLC; i++) {
171                 slctab[i].current.val = slctab[i].defset.val;
172                 if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE))
173                         slctab[i].current.flag = SLC_NOSUPPORT;
174                 else
175                         slctab[i].current.flag = slctab[i].defset.flag;
176                 if (slctab[i].sptr) {
177                         *(slctab[i].sptr) = slctab[i].defset.val;
178                 }
179         }
180         slcchange = 1;
181
182 }  /* end of default_slc */
183 #endif  /* LINEMODE */
184
185 /*
186  * get_slc_defaults
187  *
188  * Initialize the slc mapping table.
189  */
190 static void
191 get_slc_defaults(noit_console_closure_t ncct)
192 {
193         int i;
194
195         init_termbuf(ncct);
196
197         for (i = 1; i <= NSLC; i++) {
198                 slctab[i].defset.flag =
199                         spcset(ncct, i, &slctab[i].defset.val, &slctab[i].sptr);
200                 slctab[i].current.flag = SLC_NOSUPPORT;
201                 slctab[i].current.val = 0;
202         }
203
204 }  /* end of get_slc_defaults */
205
206 #ifdef  LINEMODE
207 /*
208  * add_slc
209  *
210  * Add an slc triplet to the slc buffer.
211  */
212 static void
213 add_slc(noit_console_closure_t ncct, char func, char flag, cc_t val)
214 {
215
216         if ((*slcptr++ = (unsigned char)func) == 0xff)
217                 *slcptr++ = 0xff;
218
219         if ((*slcptr++ = (unsigned char)flag) == 0xff)
220                 *slcptr++ = 0xff;
221
222         if ((*slcptr++ = (unsigned char)val) == 0xff)
223                 *slcptr++ = 0xff;
224
225 }  /* end of add_slc */
226
227 /*
228  * start_slc
229  *
230  * Get ready to process incoming slc's and respond to them.
231  *
232  * The parameter getit is non-zero if it is necessary to grab a copy
233  * of the terminal control structures.
234  */
235 static void
236 start_slc(noit_console_closure_t ncct, int getit)
237 {
238
239         slcchange = 0;
240         if (getit)
241                 init_termbuf(ncct);
242         (void) snprintf((char *)slcbuf, 5, "%c%c%c%c",
243                                         IAC, SB, TELOPT_LINEMODE, LM_SLC);
244         slcptr = slcbuf + 4;
245
246 }  /* end of start_slc */
247
248 /*
249  * end_slc
250  *
251  * Finish up the slc negotiation.  If something to send, then send it.
252  */
253 int
254 end_slc(noit_console_closure_t ncct, unsigned char **bufp)
255 {
256         int len;
257
258         /*
259          * If a change has occured, store the new terminal control
260          * structures back to the terminal driver.
261          */
262         if (slcchange) {
263                 set_termbuf(ncct);
264         }
265
266         /*
267          * If the pty state has not yet been fully processed and there is a
268          * deferred slc request from the client, then do not send any
269          * sort of slc negotiation now.  We will respond to the client's
270          * request very soon.
271          */
272         if (def_slcbuf && (_terminit == 0)) {
273                 return(0);
274         }
275
276         if (slcptr > (slcbuf + 4)) {
277                 if (bufp) {
278                         *bufp = &slcbuf[4];
279                         return(slcptr - slcbuf - 4);
280                 } else {
281                         (void) snprintf((char *)slcptr, 3, "%c%c", IAC, SE);
282                         slcptr += 2;
283                         len = slcptr - slcbuf;
284                         nc_write(ncct, slcbuf, len);
285                         netflush(ncct);  /* force it out immediately */
286                 }
287         }
288         return (0);
289
290 }  /* end of end_slc */
291
292 /*
293  * process_slc
294  *
295  * Figure out what to do about the client's slc
296  */
297 static void
298 process_slc(noit_console_closure_t ncct, unsigned char func, unsigned char flag, cc_t val)
299 {
300         int hislevel, mylevel, ack;
301
302         /*
303          * Ensure that we know something about this function
304          */
305         if (func > NSLC) {
306                 add_slc(ncct, func, SLC_NOSUPPORT, 0);
307                 return;
308         }
309
310         /*
311          * Process the special case requests of 0 SLC_DEFAULT 0
312          * and 0 SLC_VARIABLE 0.  Be a little forgiving here, don't
313          * worry about whether the value is actually 0 or not.
314          */
315         if (func == 0) {
316                 if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
317                         default_slc(ncct);
318                         send_slc(ncct);
319                 } else if (flag == SLC_VARIABLE) {
320                         send_slc(ncct);
321                 }
322                 return;
323         }
324
325         /*
326          * Appears to be a function that we know something about.  So
327          * get on with it and see what we know.
328          */
329
330         hislevel = flag & SLC_LEVELBITS;
331         mylevel = slctab[func].current.flag & SLC_LEVELBITS;
332         ack = flag & SLC_ACK;
333         /*
334          * ignore the command if:
335          * the function value and level are the same as what we already have;
336          * or the level is the same and the ack bit is set
337          */
338         if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
339                 return;
340         } else if (ack) {
341                 /*
342                  * If we get here, we got an ack, but the levels don't match.
343                  * This shouldn't happen.  If it does, it is probably because
344                  * we have sent two requests to set a variable without getting
345                  * a response between them, and this is the first response.
346                  * So, ignore it, and wait for the next response.
347                  */
348                 return;
349         } else {
350                 change_slc(ncct, func, flag, val);
351         }
352
353 }  /* end of process_slc */
354
355 /*
356  * change_slc
357  *
358  * Process a request to change one of our special characters.
359  * Compare client's request with what we are capable of supporting.
360  */
361 static void
362 change_slc(noit_console_closure_t ncct, char func, char flag, cc_t val)
363 {
364         int hislevel, mylevel;
365
366         hislevel = flag & SLC_LEVELBITS;
367         mylevel = slctab[(int)func].defset.flag & SLC_LEVELBITS;
368         /*
369          * If client is setting a function to NOSUPPORT
370          * or DEFAULT, then we can easily and directly
371          * accomodate the request.
372          */
373         if (hislevel == SLC_NOSUPPORT) {
374                 slctab[(int)func].current.flag = flag;
375                 slctab[(int)func].current.val = (cc_t)_POSIX_VDISABLE;
376                 flag |= SLC_ACK;
377                 add_slc(ncct, func, flag, val);
378                 return;
379         }
380         if (hislevel == SLC_DEFAULT) {
381                 /*
382                  * Special case here.  If client tells us to use
383                  * the default on a function we don't support, then
384                  * return NOSUPPORT instead of what we may have as a
385                  * default level of DEFAULT.
386                  */
387                 if (mylevel == SLC_DEFAULT) {
388                         slctab[(int)func].current.flag = SLC_NOSUPPORT;
389                 } else {
390                         slctab[(int)func].current.flag = slctab[(int)func].defset.flag;
391                 }
392                 slctab[(int)func].current.val = slctab[(int)func].defset.val;
393                 add_slc(ncct, func, slctab[(int)func].current.flag,
394                                                 slctab[(int)func].current.val);
395                 return;
396         }
397
398         /*
399          * Client wants us to change to a new value or he
400          * is telling us that he can't change to our value.
401          * Some of the slc's we support and can change,
402          * some we do support but can't change,
403          * and others we don't support at all.
404          * If we can change it then we have a pointer to
405          * the place to put the new value, so change it,
406          * otherwise, continue the negotiation.
407          */
408         if (slctab[(int)func].sptr) {
409                 /*
410                  * We can change this one.
411                  */
412                 slctab[(int)func].current.val = val;
413                 *(slctab[(int)func].sptr) = val;
414                 slctab[(int)func].current.flag = flag;
415                 flag |= SLC_ACK;
416                 slcchange = 1;
417                 add_slc(ncct, func, flag, val);
418         } else {
419                 /*
420                 * It is not possible for us to support this
421                 * request as he asks.
422                 *
423                 * If our level is DEFAULT, then just ack whatever was
424                 * sent.
425                 *
426                 * If he can't change and we can't change,
427                 * then degenerate to NOSUPPORT.
428                 *
429                 * Otherwise we send our level back to him, (CANTCHANGE
430                 * or NOSUPPORT) and if CANTCHANGE, send
431                 * our value as well.
432                 */
433                 if (mylevel == SLC_DEFAULT) {
434                         slctab[(int)func].current.flag = flag;
435                         slctab[(int)func].current.val = val;
436                         flag |= SLC_ACK;
437                 } else if (hislevel == SLC_CANTCHANGE &&
438                                     mylevel == SLC_CANTCHANGE) {
439                         flag &= ~SLC_LEVELBITS;
440                         flag |= SLC_NOSUPPORT;
441                         slctab[(int)func].current.flag = flag;
442                 } else {
443                         flag &= ~SLC_LEVELBITS;
444                         flag |= mylevel;
445                         slctab[(int)func].current.flag = flag;
446                         if (mylevel == SLC_CANTCHANGE) {
447                                 slctab[(int)func].current.val =
448                                         slctab[(int)func].defset.val;
449                                 val = slctab[(int)func].current.val;
450                         }
451                 }
452                 add_slc(ncct, func, flag, val);
453         }
454
455 }  /* end of change_slc */
456
457 #if     defined(USE_TERMIO) && (VEOF == VMIN)
458 cc_t oldeofc = '\004';
459 #endif
460
461 /*
462  * check_slc
463  *
464  * Check the special characters in use and notify the client if any have
465  * changed.  Only those characters that are capable of being changed are
466  * likely to have changed.  If a local change occurs, kick the support level
467  * and flags up to the defaults.
468  */
469 static void
470 check_slc(noit_console_closure_t ncct)
471 {
472         int i;
473
474         for (i = 1; i <= NSLC; i++) {
475 #if     defined(USE_TERMIO) && (VEOF == VMIN)
476                 /*
477                  * In a perfect world this would be a neat little
478                  * function.  But in this world, we should not notify
479                  * client of changes to the VEOF char when
480                  * ICANON is off, because it is not representing
481                  * a special character.
482                  */
483                 if (i == SLC_EOF) {
484                         if (!noit_console_telnet_tty_isediting(ncct))
485                                 continue;
486                         else if (slctab[i].sptr)
487                                 oldeofc = *(slctab[i].sptr);
488                 }
489 #endif  /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
490                 if (slctab[i].sptr &&
491                                 (*(slctab[i].sptr) != slctab[i].current.val)) {
492                         slctab[i].current.val = *(slctab[i].sptr);
493                         if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE)
494                                 slctab[i].current.flag = SLC_NOSUPPORT;
495                         else
496                                 slctab[i].current.flag = slctab[i].defset.flag;
497                         add_slc(ncct, (unsigned char)i, slctab[i].current.flag,
498                                                 slctab[i].current.val);
499                 }
500         }
501 }  /* check_slc */
502
503 /*
504  * do_opt_slc
505  *
506  * Process an slc option buffer.  Defer processing of incoming slc's
507  * until after the terminal state has been processed.  Save the first slc
508  * request that comes along, but discard all others.
509  *
510  * ptr points to the beginning of the buffer, len is the length.
511  */
512 static void
513 do_opt_slc(noit_console_closure_t ncct, unsigned char *ptr, int len)
514 {
515         unsigned char func, flag;
516         cc_t val;
517         unsigned char *end = ptr + len;
518
519         if (_terminit) {  /* go ahead */
520                 while (ptr < end) {
521                         func = *ptr++;
522                         if (ptr >= end) break;
523                         flag = *ptr++;
524                         if (ptr >= end) break;
525                         val = (cc_t)*ptr++;
526
527                         process_slc(ncct, func, flag, val);
528
529                 }
530         } else {
531                 /*
532                  * save this slc buffer if it is the first, otherwise dump
533                  * it.
534                  */
535                 if (def_slcbuf == (unsigned char *)0) {
536                         def_slclen = len;
537                         def_slcbuf = (unsigned char *)malloc((unsigned)len);
538                         if (def_slcbuf == (unsigned char *)0)
539                                 return;  /* too bad */
540                         memmove(def_slcbuf, ptr, len);
541                 }
542         }
543
544 }  /* end of do_opt_slc */
545
546 /*
547  * deferslc
548  *
549  * Do slc stuff that was deferred.
550  */
551 static void
552 deferslc(noit_console_closure_t ncct)
553 {
554         if (def_slcbuf) {
555                 start_slc(ncct, 1);
556                 do_opt_slc(ncct, def_slcbuf, def_slclen);
557                 (void) end_slc(ncct, 0);
558                 free(def_slcbuf);
559                 def_slcbuf = (unsigned char *)0;
560                 def_slclen = 0;
561         }
562
563 }  /* end of deferslc */
564 #endif
565
566 #ifdef  LINEMODE
567 /*
568  * tty_flowmode()       Find out if flow control is enabled or disabled.
569  * tty_linemode()       Find out if linemode (external processing) is enabled.
570  * tty_setlinemod(on)   Turn on/off linemode.
571  * tty_isecho()         Find out if echoing is turned on.
572  * tty_setecho(on)      Enable/disable character echoing.
573  * tty_israw()          Find out if terminal is in RAW mode.
574  * tty_binaryin(on)     Turn on/off BINARY on input.
575  * tty_binaryout(on)    Turn on/off BINARY on output.
576  * tty_isediting()      Find out if line editing is enabled.
577  * tty_istrapsig()      Find out if signal trapping is enabled.
578  * tty_setedit(on)      Turn on/off line editing.
579  * tty_setsig(on)       Turn on/off signal trapping.
580  * tty_issofttab()      Find out if tab expansion is enabled.
581  * tty_setsofttab(on)   Turn on/off soft tab expansion.
582  * tty_islitecho()      Find out if typed control chars are echoed literally
583  * tty_setlitecho()     Turn on/off literal echo of control chars
584  * tty_tspeed(val)      Set transmit speed to val.
585  * tty_rspeed(val)      Set receive speed to val.
586  */
587
588
589 int
590 noit_console_telnet_tty_linemode(noit_console_closure_t ncct)
591 {
592 #ifndef    USE_TERMIO
593 #ifdef TS_EXTPROC
594     return(termbuf.state & TS_EXTPROC);
595 #else
596     return 0;
597 #endif
598 #else
599 #ifdef EXTPROC
600     return(termbuf.c_lflag & EXTPROC);
601 #endif
602 #endif
603 }
604
605 void
606 noit_console_telnet_tty_setlinemode(noit_console_closure_t ncct, int on)
607 {
608 #ifdef    TIOCEXT
609     set_termbuf(ncct);
610     (void) ioctl(pty, TIOCEXT, (char *)&on);
611     init_termbuf(ncct);
612 #else    /* !TIOCEXT */
613 # ifdef    EXTPROC
614     if (on)
615         termbuf.c_lflag |= EXTPROC;
616     else
617         termbuf.c_lflag &= ~EXTPROC;
618 # endif
619 #endif    /* TIOCEXT */
620 }
621
622 void
623 noit_console_telnet_tty_setsig(noit_console_closure_t ncct, int on)
624 {
625 #ifndef USE_TERMIO
626         if (on)
627                 ;
628 #else
629         if (on)
630                 termbuf.c_lflag |= ISIG;
631         else
632                 termbuf.c_lflag &= ~ISIG;
633 #endif
634 }
635
636 void
637 noit_console_telnet_tty_setedit(noit_console_closure_t ncct, int on)
638 {
639 #ifndef USE_TERMIO
640     if (on)
641         termbuf.sg.sg_flags &= ~CBREAK;
642     else
643         termbuf.sg.sg_flags |= CBREAK;
644 #else
645     if (on)
646         termbuf.c_lflag |= ICANON;
647     else
648         termbuf.c_lflag &= ~ICANON;
649 #endif
650 }
651 #endif  /* LINEMODE */
652
653 int
654 noit_console_telnet_tty_istrapsig(noit_console_closure_t ncct)
655 {
656 #ifndef USE_TERMIO
657     return(!(termbuf.sg.sg_flags&RAW));
658 #else
659     return(termbuf.c_lflag & ISIG);
660 #endif
661 }
662
663 #ifdef  LINEMODE
664 int
665 noit_console_telnet_tty_isediting(noit_console_closure_t ncct)
666 {
667 #ifndef USE_TERMIO
668     return(!(termbuf.sg.sg_flags & (CBREAK|RAW)));
669 #else
670     return(termbuf.c_lflag & ICANON);
671 #endif
672 }
673 #endif
674
675 int
676 noit_console_telnet_tty_issofttab(noit_console_closure_t ncct)
677 {
678 #ifndef    USE_TERMIO
679     return (termbuf.sg.sg_flags & XTABS);
680 #else
681 # ifdef    OXTABS
682     return (termbuf.c_oflag & OXTABS);
683 # endif
684 # ifdef    TABDLY
685     return ((termbuf.c_oflag & TABDLY) == TAB3);
686 # endif
687 #endif
688 }
689
690 void
691 noit_console_telnet_tty_setsofttab(noit_console_closure_t ncct, int on)
692 {
693 #ifndef    USE_TERMIO
694     if (on)
695         termbuf.sg.sg_flags |= XTABS;
696     else
697         termbuf.sg.sg_flags &= ~XTABS;
698 #else
699     if (on) {
700 # ifdef    OXTABS
701         termbuf.c_oflag |= OXTABS;
702 # endif
703 # ifdef    TABDLY
704         termbuf.c_oflag &= ~TABDLY;
705         termbuf.c_oflag |= TAB3;
706 # endif
707     } else {
708 # ifdef    OXTABS
709         termbuf.c_oflag &= ~OXTABS;
710 # endif
711 # ifdef    TABDLY
712         termbuf.c_oflag &= ~TABDLY;
713         termbuf.c_oflag |= TAB0;
714 # endif
715     }
716 #endif
717 }
718
719 int
720 noit_console_telnet_tty_islitecho(noit_console_closure_t ncct)
721 {
722 #ifndef    USE_TERMIO
723     return (!(termbuf.lflags & LCTLECH));
724 #else
725 # ifdef    ECHOCTL
726     return (!(termbuf.c_lflag & ECHOCTL));
727 # endif
728 # ifdef    TCTLECH
729     return (!(termbuf.c_lflag & TCTLECH));
730 # endif
731 # if    !defined(ECHOCTL) && !defined(TCTLECH)
732     return (0);    /* assumes ctl chars are echoed '^x' */
733 # endif
734 #endif
735 }
736
737 void
738 noit_console_telnet_tty_setlitecho(noit_console_closure_t ncct, int on)
739 {
740 #ifndef    USE_TERMIO
741     if (on)
742         termbuf.lflags &= ~LCTLECH;
743     else
744         termbuf.lflags |= LCTLECH;
745 #else
746 # ifdef    ECHOCTL
747     if (on)
748         termbuf.c_lflag &= ~ECHOCTL;
749     else
750         termbuf.c_lflag |= ECHOCTL;
751 # endif
752 # ifdef    TCTLECH
753     if (on)
754         termbuf.c_lflag &= ~TCTLECH;
755     else
756         termbuf.c_lflag |= TCTLECH;
757 # endif
758 #endif
759 }
760
761 int
762 noit_console_telnet_tty_iscrnl(noit_console_closure_t ncct)
763 {
764 #ifndef USE_TERMIO
765     return (termbuf.sg.sg_flags & CRMOD);
766 #else
767     return (termbuf.c_iflag & ICRNL);
768 #endif
769 }
770
771 void
772 noit_console_telnet_tty_tspeed(noit_console_closure_t ncct, int val)
773 {
774 #ifdef  DECODE_BAUD
775     struct termspeeds *tp;
776
777     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
778         ;
779     if (tp->speed == -1)        /* back up to last valid value */
780         --tp;
781     cfsetospeed(&termbuf, tp->value);
782 #else   /* DECODE_BUAD */
783     cfsetospeed(&termbuf, val);
784 #endif  /* DECODE_BUAD */
785 }
786
787 void
788 noit_console_telnet_tty_rspeed(noit_console_closure_t ncct, int val)
789 {
790 #ifdef  DECODE_BAUD
791     struct termspeeds *tp;
792
793     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
794         ;
795     if (tp->speed == -1)        /* back up to last valid value */
796         --tp;
797     cfsetispeed(&termbuf, tp->value);
798 #else   /* DECODE_BAUD */
799     cfsetispeed(&termbuf, val);
800 #endif  /* DECODE_BAUD */
801 }
802
803
804 int
805 noit_console_telnet_tty_flowmode(noit_console_closure_t ncct)
806 {
807 #ifndef USE_TERMIO
808     return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0);
809 #else
810     return((termbuf.c_iflag & IXON) ? 1 : 0);
811 #endif
812 }
813
814 void
815 noit_console_telnet_tty_binaryin(noit_console_closure_t ncct, int on)
816 {
817 #ifndef USE_TERMIO
818     if (on)
819         termbuf.lflags |= LPASS8;
820     else
821         termbuf.lflags &= ~LPASS8;
822 #else
823     if (on)
824         termbuf.c_iflag &= ~ISTRIP;
825     else
826         termbuf.c_iflag |= ISTRIP;
827 #endif
828 }
829
830 int
831 noit_console_telnet_tty_isbinaryin(noit_console_closure_t ncct)
832 {
833 #ifndef USE_TERMIO
834         return(termbuf.lflags & LPASS8);
835 #else
836         return(!(termbuf.c_iflag & ISTRIP));
837 #endif
838 }
839
840 void
841 noit_console_telnet_tty_binaryout(noit_console_closure_t ncct, int on)
842 {
843 #ifndef USE_TERMIO
844     if (on)
845         termbuf.lflags |= LLITOUT;
846     else
847         termbuf.lflags &= ~LLITOUT;
848 #else
849     if (on) {
850         termbuf.c_cflag &= ~(CSIZE|PARENB);
851         termbuf.c_cflag |= CS8;
852         termbuf.c_oflag &= ~OPOST;
853     } else {
854         termbuf.c_cflag &= ~CSIZE;
855         termbuf.c_cflag |= CS7|PARENB;
856         termbuf.c_oflag |= OPOST;
857     }
858 #endif
859 }
860
861 int
862 noit_console_telnet_tty_isbinaryout(noit_console_closure_t ncct)
863 {
864 #ifndef USE_TERMIO
865         return(termbuf.lflags & LLITOUT);
866 #else
867         return(!(termbuf.c_oflag&OPOST));
868 #endif
869 }
870
871 int
872 noit_console_telnet_tty_restartany(noit_console_closure_t ncct)
873 {
874 #ifndef USE_TERMIO
875 # ifdef DECCTQ
876     return((termbuf.lflags & DECCTQ) ? 0 : 1);
877 # else
878     return(-1);
879 # endif
880 #else
881     return((termbuf.c_iflag & IXANY) ? 1 : 0);
882 #endif
883 }
884
885 int
886 noit_console_telnet_tty_isecho(noit_console_closure_t ncct)
887 {
888 #ifndef USE_TERMIO
889         return (termbuf.sg.sg_flags & ECHO);
890 #else
891         return (termbuf.c_lflag & ECHO);
892 #endif
893 }
894
895 void
896 noit_console_telnet_tty_setecho(noit_console_closure_t ncct, int on)
897 {
898     int i;
899     i = ECHO;
900 #ifdef CRMOD
901     i |= CRMOD;
902 #endif
903 #ifndef USE_TERMIO
904     if (on)
905         termbuf.sg.sg_flags |= i;
906     else
907         termbuf.sg.sg_flags &= ~(i);
908 #else
909     if (on)
910         termbuf.c_lflag |= i;
911     else
912         termbuf.c_lflag &= ~i;
913 #endif
914 }
915
916 static void
917 init_termbuf(noit_console_closure_t ncct)
918 {
919 #ifndef USE_TERMIO
920         (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg);
921         (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc);
922         (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc);
923 # ifdef TIOCGSTATE
924         (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state);
925 # endif
926 #else
927         (void) tcgetattr(pty, &termbuf);
928 #endif
929         termbuf2 = termbuf;
930 }
931
932 #if     defined(LINEMODE) && defined(TIOCPKT_IOCTL)
933 void
934 copy_termbuf(noit_console_closure_t ncct, char *cp, size_t len)
935 {
936         if (len > sizeof(termbuf))
937                 len = sizeof(termbuf);
938         memmove((char *)&termbuf, cp, len);
939         termbuf2 = termbuf;
940 }
941 #endif  /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
942
943 static void
944 set_termbuf(noit_console_closure_t ncct)
945 {
946         /*
947          * Only make the necessary changes.
948          */
949 #ifndef USE_TERMIO
950         if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg,
951                                                         sizeof(termbuf.sg)))
952                 (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg);
953         if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc,
954                                                         sizeof(termbuf.tc)))
955                 (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc);
956         if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc,
957                                                         sizeof(termbuf.ltc)))
958                 (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc);
959         if (termbuf.lflags != termbuf2.lflags)
960                 (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags);
961 #else   /* USE_TERMIO */
962         if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
963                 (void) tcsetattr(pty, TCSANOW, &termbuf);
964 #endif  /* USE_TERMIO */
965 }
966
967 #ifdef  LINEMODE
968 /*
969  * localstat
970  *
971  * This function handles all management of linemode.
972  *
973  * Linemode allows the client to do the local editing of data
974  * and send only complete lines to the server.  Linemode state is
975  * based on the state of the pty driver.  If the pty is set for
976  * external processing, then we can use linemode.  Further, if we
977  * can use real linemode, then we can look at the edit control bits
978  * in the pty to determine what editing the client should do.
979  *
980  * Linemode support uses the following state flags to keep track of
981  * current and desired linemode state.
982  *      alwayslinemode : true if -l was specified on the telnetd
983  *      command line.  It means to have linemode on as much as
984  *      possible.
985  *
986  *      lmodetype: signifies whether the client can
987  *      handle real linemode, or if use of kludgeomatic linemode
988  *      is preferred.  It will be set to one of the following:
989  *              REAL_LINEMODE : use linemode option
990  *              NO_KLUDGE : don't initiate kludge linemode.
991  *              KLUDGE_LINEMODE : use kludge linemode
992  *              NO_LINEMODE : client is ignorant of linemode
993  *
994  *      linemode, uselinemode : linemode is true if linemode
995  *      is currently on, uselinemode is the state that we wish
996  *      to be in.  If another function wishes to turn linemode
997  *      on or off, it sets or clears uselinemode.
998  *
999  *      editmode, useeditmode : like linemode/uselinemode, but
1000  *      these contain the edit mode states (edit and trapsig).
1001  *
1002  * The state variables correspond to some of the state information
1003  * in the pty.
1004  *      linemode:
1005  *              In real linemode, this corresponds to whether the pty
1006  *              expects external processing of incoming data.
1007  *              In kludge linemode, this more closely corresponds to the
1008  *              whether normal processing is on or not.  (ICANON in
1009  *              system V, or COOKED mode in BSD.)
1010  *              If the -l option was specified (alwayslinemode), then
1011  *              an attempt is made to force external processing on at
1012  *              all times.
1013  *
1014  * The following heuristics are applied to determine linemode
1015  * handling within the server.
1016  *      1) Early on in starting up the server, an attempt is made
1017  *         to negotiate the linemode option.  If this succeeds
1018  *         then lmodetype is set to REAL_LINEMODE and all linemode
1019  *         processing occurs in the context of the linemode option.
1020  *      2) If the attempt to negotiate the linemode option failed,
1021  *         and the "-k" (don't initiate kludge linemode) isn't set,
1022  *         then we try to use kludge linemode.  We test for this
1023  *         capability by sending "do Timing Mark".  If a positive
1024  *         response comes back, then we assume that the client
1025  *         understands kludge linemode (ech!) and the
1026  *         lmodetype flag is set to KLUDGE_LINEMODE.
1027  *      3) Otherwise, linemode is not supported at all and
1028  *         lmodetype remains set to NO_LINEMODE (which happens
1029  *         to be 0 for convenience).
1030  *      4) At any time a command arrives that implies a higher
1031  *         state of linemode support in the client, we move to that
1032  *         linemode support.
1033  *
1034  * A short explanation of kludge linemode is in order here.
1035  *      1) The heuristic to determine support for kludge linemode
1036  *         is to send a do timing mark.  We assume that a client
1037  *         that supports timing marks also supports kludge linemode.
1038  *         A risky proposition at best.
1039  *      2) Further negotiation of linemode is done by changing the
1040  *         the server's state regarding SGA.  If server will SGA,
1041  *         then linemode is off, if server won't SGA, then linemode
1042  *         is on.
1043  */
1044 static void
1045 localstat(noit_console_closure_t ncct)
1046 {
1047         int need_will_echo = 0;
1048
1049         /*
1050          * Check for changes to flow control if client supports it.
1051          */
1052         flowstat(ncct);
1053
1054         /*
1055          * Check linemode on/off state
1056          */
1057         uselinemode = noit_console_telnet_tty_linemode(ncct);
1058
1059         /*
1060          * If alwayslinemode is on, and pty is changing to turn it off, then
1061          * force linemode back on.
1062          */
1063         if (alwayslinemode && linemode && !uselinemode) {
1064                 uselinemode = 1;
1065                 noit_console_telnet_tty_setlinemode(ncct, uselinemode);
1066         }
1067
1068         if (uselinemode) {
1069                 /*
1070                  * Check for state of BINARY options.
1071                  *
1072                  * We only need to do the binary dance if we are actually going
1073                  * to use linemode.  As this confuses some telnet clients
1074                  * that don't support linemode, and doesn't gain us
1075                  * anything, we don't do it unless we're doing linemode.
1076                  * -Crh (henrich@msu.edu)
1077                  */
1078
1079                 if (noit_console_telnet_tty_isbinaryin(ncct)) {
1080                         if (his_want_state_is_wont(TELOPT_BINARY))
1081                                 send_do(TELOPT_BINARY, 1);
1082                 } else {
1083                         if (his_want_state_is_will(TELOPT_BINARY))
1084                                 send_dont(TELOPT_BINARY, 1);
1085                 }
1086
1087                 if (noit_console_telnet_tty_isbinaryout(ncct)) {
1088                         if (my_want_state_is_wont(TELOPT_BINARY))
1089                                 send_will(TELOPT_BINARY, 1);
1090                 } else {
1091                         if (my_want_state_is_will(TELOPT_BINARY))
1092                                 send_wont(TELOPT_BINARY, 1);
1093                 }
1094         }
1095
1096
1097         /*
1098          * Do echo mode handling as soon as we know what the
1099          * linemode is going to be.
1100          * If the pty has echo turned off, then tell the client that
1101          * the server will echo.  If echo is on, then the server
1102          * will echo if in character mode, but in linemode the
1103          * client should do local echoing.  The state machine will
1104          * not send anything if it is unnecessary, so don't worry
1105          * about that here.
1106          *
1107          * If we need to send the WILL ECHO (because echo is off),
1108          * then delay that until after we have changed the MODE.
1109          * This way, when the user is turning off both editing
1110          * and echo, the client will get editing turned off first.
1111          * This keeps the client from going into encryption mode
1112          * and then right back out if it is doing auto-encryption
1113          * when passwords are being typed.
1114          */
1115         if (uselinemode) {
1116                 if (noit_console_telnet_tty_isecho(ncct))
1117                         send_wont(TELOPT_ECHO, 1);
1118                 else
1119                         need_will_echo = 1;
1120 #ifdef  KLUDGELINEMODE
1121                 if (lmodetype == KLUDGE_OK)
1122                         lmodetype = KLUDGE_LINEMODE;
1123 #endif
1124         }
1125
1126         /*
1127          * If linemode is being turned off, send appropriate
1128          * command and then we're all done.
1129          */
1130          if (!uselinemode && linemode) {
1131 # ifdef KLUDGELINEMODE
1132                 if (lmodetype == REAL_LINEMODE) {
1133 # endif /* KLUDGELINEMODE */
1134                         send_dont(TELOPT_LINEMODE, 1);
1135 # ifdef KLUDGELINEMODE
1136                 } else if (lmodetype == KLUDGE_LINEMODE)
1137                         send_will(TELOPT_SGA, 1);
1138 # endif /* KLUDGELINEMODE */
1139                 send_will(TELOPT_ECHO, 1);
1140                 linemode = uselinemode;
1141                 goto done;
1142         }
1143
1144 # ifdef KLUDGELINEMODE
1145         /*
1146          * If using real linemode check edit modes for possible later use.
1147          * If we are in kludge linemode, do the SGA negotiation.
1148          */
1149         if (lmodetype == REAL_LINEMODE) {
1150 # endif /* KLUDGELINEMODE */
1151                 useeditmode = 0;
1152                 if (noit_console_telnet_tty_isediting(ncct))
1153                         useeditmode |= MODE_EDIT;
1154                 if (noit_console_telnet_tty_istrapsig(ncct))
1155                         useeditmode |= MODE_TRAPSIG;
1156                 if (noit_console_telnet_tty_issofttab(ncct))
1157                         useeditmode |= MODE_SOFT_TAB;
1158                 if (noit_console_telnet_tty_islitecho(ncct))
1159                         useeditmode |= MODE_LIT_ECHO;
1160 # ifdef KLUDGELINEMODE
1161         } else if (lmodetype == KLUDGE_LINEMODE) {
1162                 if (noit_console_telnet_tty_isediting(ncct) && uselinemode)
1163                         send_wont(TELOPT_SGA, 1);
1164                 else
1165                         send_will(TELOPT_SGA, 1);
1166         }
1167 # endif /* KLUDGELINEMODE */
1168
1169         /*
1170          * Negotiate linemode on if pty state has changed to turn it on.
1171          * Send appropriate command and send along edit mode, then all done.
1172          */
1173         if (uselinemode && !linemode) {
1174 # ifdef KLUDGELINEMODE
1175                 if (lmodetype == KLUDGE_LINEMODE) {
1176                         send_wont(TELOPT_SGA, 1);
1177                 } else if (lmodetype == REAL_LINEMODE) {
1178 # endif /* KLUDGELINEMODE */
1179                         send_do(TELOPT_LINEMODE, 1);
1180                         /* send along edit modes */
1181                         nc_printf(ncct, "%c%c%c%c%c%c%c", IAC, SB,
1182                                 TELOPT_LINEMODE, LM_MODE, useeditmode,
1183                                 IAC, SE);
1184                         editmode = useeditmode;
1185 # ifdef KLUDGELINEMODE
1186                 }
1187 # endif /* KLUDGELINEMODE */
1188                 linemode = uselinemode;
1189                 goto done;
1190         }
1191
1192 # ifdef KLUDGELINEMODE
1193         /*
1194          * None of what follows is of any value if not using
1195          * real linemode.
1196          */
1197         if (lmodetype < REAL_LINEMODE)
1198                 goto done;
1199 # endif /* KLUDGELINEMODE */
1200
1201         if (linemode && his_state_is_will(TELOPT_LINEMODE)) {
1202                 /*
1203                  * If edit mode changed, send edit mode.
1204                  */
1205                  if (useeditmode != editmode) {
1206                         /*
1207                          * Send along appropriate edit mode mask.
1208                          */
1209                         nc_printf(ncct, "%c%c%c%c%c%c%c", IAC, SB,
1210                                 TELOPT_LINEMODE, LM_MODE, useeditmode,
1211                                 IAC, SE);
1212                         editmode = useeditmode;
1213                 }
1214
1215
1216                 /*
1217                  * Check for changes to special characters in use.
1218                  */
1219                 start_slc(ncct, 0);
1220                 check_slc(ncct);
1221                 (void) end_slc(ncct, 0);
1222         }
1223
1224 done:
1225         if (need_will_echo)
1226                 send_will(TELOPT_ECHO, 1);
1227         /*
1228          * Some things should be deferred until after the pty state has
1229          * been set by the local process.  Do those things that have been
1230          * deferred now.  This only happens once.
1231          */
1232         if (_terminit == 0) {
1233                 _terminit = 1;
1234                 defer_terminit(ncct);
1235         }
1236
1237         netflush(ncct);
1238         set_termbuf(ncct);
1239         return;
1240
1241 }  /* end of localstat */
1242 #endif  /* LINEMODE */
1243
1244 /*
1245  * flowstat
1246  *
1247  * Check for changes to flow control
1248  */
1249 static void
1250 flowstat(noit_console_closure_t ncct)
1251 {
1252     if (his_state_is_will(TELOPT_LFLOW)) {
1253         if (noit_console_telnet_tty_flowmode(ncct) != flowmode) {
1254             flowmode = noit_console_telnet_tty_flowmode(ncct);
1255             nc_printf(ncct, "%c%c%c%c%c%c",
1256                         IAC, SB, TELOPT_LFLOW,
1257                         flowmode ? LFLOW_ON : LFLOW_OFF,
1258                         IAC, SE);
1259         }
1260         if (noit_console_telnet_tty_restartany(ncct) != restartany) {
1261             restartany = noit_console_telnet_tty_restartany(ncct);
1262             nc_printf(ncct, "%c%c%c%c%c%c",
1263                         IAC, SB, TELOPT_LFLOW,
1264                         restartany ? LFLOW_RESTART_ANY
1265                         : LFLOW_RESTART_XON,
1266                         IAC, SE);
1267         }
1268     }
1269 }
1270
1271 /*
1272  * clientstat
1273  *
1274  * Process linemode related requests from the client.
1275  * Client can request a change to only one of linemode, editmode or slc's
1276  * at a time, and if using kludge linemode, then only linemode may be
1277  * affected.
1278  */
1279 void
1280 clientstat(noit_console_closure_t ncct, int code, int parm1, int parm2)
1281 {
1282     /*
1283      * Get a copy of terminal characteristics.
1284      */
1285     init_termbuf(ncct);
1286
1287     /*
1288      * Process request from client. code tells what it is.
1289      */
1290     switch (code) {
1291 #ifdef  LINEMODE
1292         case TELOPT_LINEMODE:
1293                 /*
1294                  * Don't do anything unless client is asking us to change
1295                  * modes.
1296                  */
1297                 uselinemode = (parm1 == WILL);
1298                 if (uselinemode != linemode) {
1299 # ifdef KLUDGELINEMODE
1300                         /*
1301                          * If using kludge linemode, make sure that
1302                          * we can do what the client asks.
1303                          * We can not turn off linemode if alwayslinemode
1304                          * and the ICANON bit is set.
1305                          */
1306                         if (lmodetype == KLUDGE_LINEMODE) {
1307                                 if (alwayslinemode && noit_console_telnet_tty_isediting(ncct)) {
1308                                         uselinemode = 1;
1309                                 }
1310                         }
1311
1312                         /*
1313                          * Quit now if we can't do it.
1314                          */
1315                         if (uselinemode == linemode)
1316                                 return;
1317
1318                         /*
1319                          * If using real linemode and linemode is being
1320                          * turned on, send along the edit mode mask.
1321                          */
1322                         if (lmodetype == REAL_LINEMODE && uselinemode)
1323 # else  /* KLUDGELINEMODE */
1324                         if (uselinemode)
1325 # endif /* KLUDGELINEMODE */
1326                         {
1327                                 useeditmode = 0;
1328                                 if (noit_console_telnet_tty_isediting(ncct))
1329                                         useeditmode |= MODE_EDIT;
1330                                 if (noit_console_telnet_tty_istrapsig(ncct))
1331                                         useeditmode |= MODE_TRAPSIG;
1332                                 if (noit_console_telnet_tty_issofttab(ncct))
1333                                         useeditmode |= MODE_SOFT_TAB;
1334                                 if (noit_console_telnet_tty_islitecho(ncct))
1335                                         useeditmode |= MODE_LIT_ECHO;
1336                                 nc_printf(ncct, "%c%c%c%c%c%c%c", IAC,
1337                                         SB, TELOPT_LINEMODE, LM_MODE,
1338                                                         useeditmode, IAC, SE);
1339                                 editmode = useeditmode;
1340                         }
1341
1342
1343                         noit_console_telnet_tty_setlinemode(ncct, uselinemode);
1344
1345                         linemode = uselinemode;
1346
1347                         if (!linemode)
1348                                 send_will(TELOPT_ECHO, 1);
1349                 }
1350                 break;
1351
1352         case LM_MODE:
1353             {
1354                 int ack, changed;
1355
1356                 /*
1357                  * Client has sent along a mode mask.  If it agrees with
1358                  * what we are currently doing, ignore it; if not, it could
1359                  * be viewed as a request to change.  Note that the server
1360                  * will change to the modes in an ack if it is different from
1361                  * what we currently have, but we will not ack the ack.
1362                  */
1363                  useeditmode &= MODE_MASK;
1364                  ack = (useeditmode & MODE_ACK);
1365                  useeditmode &= ~MODE_ACK;
1366
1367                  if ((changed = (useeditmode ^ editmode))) {
1368                         /*
1369                          * This check is for a timing problem.  If the
1370                          * state of the tty has changed (due to the user
1371                          * application) we need to process that info
1372                          * before we write in the state contained in the
1373                          * ack!!!  This gets out the new MODE request,
1374                          * and when the ack to that command comes back
1375                          * we'll set it and be in the right mode.
1376                          */
1377 #ifdef LINEMODE
1378                         if (ack)
1379                                 localstat(ncct);
1380 #endif
1381                         if (changed & MODE_EDIT)
1382                                 noit_console_telnet_tty_setedit(ncct, useeditmode & MODE_EDIT);
1383
1384                         if (changed & MODE_TRAPSIG)
1385                                 noit_console_telnet_tty_setsig(ncct, useeditmode & MODE_TRAPSIG);
1386
1387                         if (changed & MODE_SOFT_TAB)
1388                                 noit_console_telnet_tty_setsofttab(ncct, useeditmode & MODE_SOFT_TAB);
1389
1390                         if (changed & MODE_LIT_ECHO)
1391                                 noit_console_telnet_tty_setlitecho(ncct, useeditmode & MODE_LIT_ECHO);
1392
1393                         set_termbuf(ncct);
1394
1395                         if (!ack) {
1396                                 nc_printf(ncct, "%c%c%c%c%c%c%c", IAC,
1397                                         SB, TELOPT_LINEMODE, LM_MODE,
1398                                         useeditmode|MODE_ACK,
1399                                         IAC, SE);
1400                         }
1401
1402                         editmode = useeditmode;
1403                 }
1404
1405                 break;
1406
1407             }  /* end of case LM_MODE */
1408 #endif  /* LINEMODE */
1409     case TELOPT_NAWS:
1410 #ifdef  TIOCSWINSZ
1411         {
1412             struct winsize ws;
1413
1414             def_col = parm1;
1415             def_row = parm2;
1416
1417             /*
1418              * Change window size as requested by client.
1419              */
1420
1421             ws.ws_col = parm1;
1422             ws.ws_row = parm2;
1423             ioctl(pty, TIOCSWINSZ, (char *)&ws);
1424         }
1425 #endif  /* TIOCSWINSZ */
1426
1427     break;
1428
1429     case TELOPT_TSPEED:
1430         {
1431             def_tspeed = parm1;
1432             def_rspeed = parm2;
1433             /*
1434              * Change terminal speed as requested by client.
1435              * We set the receive speed first, so that if we can't
1436              * store seperate receive and transmit speeds, the transmit
1437              * speed will take precedence.
1438              */
1439             noit_console_telnet_tty_rspeed(ncct, parm2);
1440             noit_console_telnet_tty_tspeed(ncct, parm1);
1441             set_termbuf(ncct);
1442
1443             break;
1444
1445         }  /* end of case TELOPT_TSPEED */
1446
1447     default:
1448         /* What? */
1449         break;
1450     }  /* end of switch */
1451
1452     netflush(ncct);
1453 }
1454
1455 #ifdef  LINEMODE
1456 /*
1457  * defer_terminit
1458  *
1459  * Some things should not be done until after the login process has started
1460  * and all the pty modes are set to what they are supposed to be.  This
1461  * function is called when the pty state has been processed for the first time.
1462  * It calls other functions that do things that were deferred in each module.
1463  */
1464 static void
1465 defer_terminit(noit_console_closure_t ncct)
1466 {
1467
1468         /*
1469          * local stuff that got deferred.
1470          */
1471         if (def_tspeed != -1) {
1472                 clientstat(ncct, TELOPT_TSPEED, def_tspeed, def_rspeed);
1473                 def_tspeed = def_rspeed = 0;
1474         }
1475
1476 #ifdef  TIOCSWINSZ
1477         if (def_col || def_row) {
1478                 struct winsize ws;
1479
1480                 memset((char *)&ws, 0, sizeof(ws));
1481                 ws.ws_col = def_col;
1482                 ws.ws_row = def_row;
1483                 (void) ioctl(pty, TIOCSWINSZ, (char *)&ws);
1484         }
1485 #endif
1486
1487         /*
1488          * The only other module that currently defers anything.
1489          */
1490         deferslc(ncct);
1491
1492 }  /* end of defer_terminit */
1493 #endif
1494
1495 /*
1496  * spcset(func, valp, valpp)
1497  *
1498  * This function takes various special characters (func), and
1499  * sets *valp to the current value of that character, and
1500  * *valpp to point to where in the "termbuf" structure that
1501  * value is kept.
1502  *
1503  * It returns the SLC_ level of support for this function.
1504  */
1505
1506 #ifndef USE_TERMIO
1507 static int
1508 spcset(noit_console_closure_t ncct, int func, cc_t *valp, cc_t **valpp)
1509 {
1510         switch(func) {
1511         case SLC_EOF:
1512                 *valp = termbuf.tc.t_eofc;
1513                 *valpp = (cc_t *)&termbuf.tc.t_eofc;
1514                 return(SLC_VARIABLE);
1515         case SLC_EC:
1516                 *valp = termbuf.sg.sg_erase;
1517                 *valpp = (cc_t *)&termbuf.sg.sg_erase;
1518                 return(SLC_VARIABLE);
1519         case SLC_EL:
1520                 *valp = termbuf.sg.sg_kill;
1521                 *valpp = (cc_t *)&termbuf.sg.sg_kill;
1522                 return(SLC_VARIABLE);
1523         case SLC_IP:
1524                 *valp = termbuf.tc.t_intrc;
1525                 *valpp = (cc_t *)&termbuf.tc.t_intrc;
1526                 return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
1527         case SLC_ABORT:
1528                 *valp = termbuf.tc.t_quitc;
1529                 *valpp = (cc_t *)&termbuf.tc.t_quitc;
1530                 return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
1531         case SLC_XON:
1532                 *valp = termbuf.tc.t_startc;
1533                 *valpp = (cc_t *)&termbuf.tc.t_startc;
1534                 return(SLC_VARIABLE);
1535         case SLC_XOFF:
1536                 *valp = termbuf.tc.t_stopc;
1537                 *valpp = (cc_t *)&termbuf.tc.t_stopc;
1538                 return(SLC_VARIABLE);
1539         case SLC_AO:
1540                 *valp = termbuf.ltc.t_flushc;
1541                 *valpp = (cc_t *)&termbuf.ltc.t_flushc;
1542                 return(SLC_VARIABLE);
1543         case SLC_SUSP:
1544                 *valp = termbuf.ltc.t_suspc;
1545                 *valpp = (cc_t *)&termbuf.ltc.t_suspc;
1546                 return(SLC_VARIABLE);
1547         case SLC_EW:
1548                 *valp = termbuf.ltc.t_werasc;
1549                 *valpp = (cc_t *)&termbuf.ltc.t_werasc;
1550                 return(SLC_VARIABLE);
1551         case SLC_RP:
1552                 *valp = termbuf.ltc.t_rprntc;
1553                 *valpp = (cc_t *)&termbuf.ltc.t_rprntc;
1554                 return(SLC_VARIABLE);
1555         case SLC_LNEXT:
1556                 *valp = termbuf.ltc.t_lnextc;
1557                 *valpp = (cc_t *)&termbuf.ltc.t_lnextc;
1558                 return(SLC_VARIABLE);
1559         case SLC_FORW1:
1560                 *valp = termbuf.tc.t_brkc;
1561                 *valpp = (cc_t *)&termbuf.ltc.t_lnextc;
1562                 return(SLC_VARIABLE);
1563         case SLC_BRK:
1564         case SLC_SYNCH:
1565         case SLC_AYT:
1566         case SLC_EOR:
1567                 *valp = (cc_t)0;
1568                 *valpp = (cc_t *)0;
1569                 return(SLC_DEFAULT);
1570         default:
1571                 *valp = (cc_t)0;
1572                 *valpp = (cc_t *)0;
1573                 return(SLC_NOSUPPORT);
1574         }
1575 }
1576
1577 #else   /* USE_TERMIO */
1578
1579
1580 #define setval(a, b)    *valp = termbuf.c_cc[a]; \
1581                         *valpp = &termbuf.c_cc[a]; \
1582                         return(b);
1583 #define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
1584
1585 int
1586 spcset(noit_console_closure_t ncct, int func, cc_t *valp, cc_t **valpp)
1587 {
1588         switch(func) {
1589         case SLC_EOF:
1590                 setval(VEOF, SLC_VARIABLE);
1591         case SLC_EC:
1592                 setval(VERASE, SLC_VARIABLE);
1593         case SLC_EL:
1594                 setval(VKILL, SLC_VARIABLE);
1595         case SLC_IP:
1596                 setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
1597         case SLC_ABORT:
1598                 setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
1599         case SLC_XON:
1600 #ifdef  VSTART
1601                 setval(VSTART, SLC_VARIABLE);
1602 #else
1603                 defval(0x13);
1604 #endif
1605         case SLC_XOFF:
1606 #ifdef  VSTOP
1607                 setval(VSTOP, SLC_VARIABLE);
1608 #else
1609                 defval(0x11);
1610 #endif
1611         case SLC_EW:
1612 #ifdef  VWERASE
1613                 setval(VWERASE, SLC_VARIABLE);
1614 #else
1615                 defval(0);
1616 #endif
1617         case SLC_RP:
1618 #ifdef  VREPRINT
1619                 setval(VREPRINT, SLC_VARIABLE);
1620 #else
1621                 defval(0);
1622 #endif
1623         case SLC_LNEXT:
1624 #ifdef  VLNEXT
1625                 setval(VLNEXT, SLC_VARIABLE);
1626 #else
1627                 defval(0);
1628 #endif
1629         case SLC_AO:
1630 #if     !defined(VDISCARD) && defined(VFLUSHO)
1631 # define VDISCARD VFLUSHO
1632 #endif
1633 #ifdef  VDISCARD
1634                 setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
1635 #else
1636                 defval(0);
1637 #endif
1638         case SLC_SUSP:
1639 #ifdef  VSUSP
1640                 setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
1641 #else
1642                 defval(0);
1643 #endif
1644 #ifdef  VEOL
1645         case SLC_FORW1:
1646                 setval(VEOL, SLC_VARIABLE);
1647 #endif
1648 #ifdef  VEOL2
1649         case SLC_FORW2:
1650                 setval(VEOL2, SLC_VARIABLE);
1651 #endif
1652         case SLC_AYT:
1653 #ifdef  VSTATUS
1654                 setval(VSTATUS, SLC_VARIABLE);
1655 #else
1656                 defval(0);
1657 #endif
1658
1659         case SLC_BRK:
1660         case SLC_SYNCH:
1661         case SLC_EOR:
1662                 defval(0);
1663
1664         default:
1665                 *valp = 0;
1666                 *valpp = 0;
1667                 return(SLC_NOSUPPORT);
1668         }
1669 }
1670 #endif  /* USE_TERMIO */
1671
1672 void
1673 ptyflush(noit_console_closure_t ncct) {
1674   int written;
1675   if(ncct->telnet->_pty_fill == 0) return;
1676   written = write(ncct->pty_slave, ncct->telnet->_pty_buf,
1677                   ncct->telnet->_pty_fill);
1678   if(written != ncct->telnet->_pty_fill) {
1679     /* We can't do anything useful here... just cope. */
1680   }
1681   ncct->telnet->_pty_fill = 0;
1682 }
1683
1684 static void
1685 netclear(noit_console_closure_t ncct) {
1686   ncct->outbuf_len = 0;
1687 }
1688
1689 static void
1690 pty_write(noit_console_closure_t ncct, void *buf, int len) {
1691   if(len > sizeof(ncct->telnet->_pty_buf)) {
1692     int i;
1693     /* split it up */
1694     for(i=0; i<len; i+=sizeof(ncct->telnet->_pty_buf)) {
1695       pty_write(ncct, (unsigned char *)buf + i,
1696                 MIN(sizeof(ncct->telnet->_pty_buf),len-i));
1697     }
1698   }
1699   while(ncct->telnet->_pty_fill + len > sizeof(ncct->telnet->_pty_buf)) {
1700     ptyflush(ncct);
1701   }
1702   memcpy(ncct->telnet->_pty_buf + ncct->telnet->_pty_fill, buf, len);
1703   ncct->telnet->_pty_fill += len;
1704 }
1705
1706 /*
1707  * Send interrupt to process on other side of pty.
1708  * If it is in raw mode, just write NULL;
1709  * otherwise, write intr char.
1710  */
1711 void
1712 interrupt(noit_console_closure_t ncct)
1713 {
1714 #ifdef  TCSIG
1715         ptyflush(ncct); /* half-hearted */
1716         (void) ioctl(pty, TCSIG, (char *)SIGINT);
1717 #else   /* TCSIG */
1718         unsigned char ch;
1719         ptyflush(ncct); /* half-hearted */
1720         init_termbuf(ncct);
1721         ch = slctab[SLC_IP].sptr ?
1722                         (unsigned char)*slctab[SLC_IP].sptr : '\177';
1723         pty_write(ncct, &ch, 1);
1724 #endif  /* TCSIG */
1725 }
1726
1727 /*
1728  * Send quit to process on other side of pty.
1729  * If it is in raw mode, just write NULL;
1730  * otherwise, write quit char.
1731  */
1732 void
1733 sendbrk(noit_console_closure_t ncct)
1734 {
1735 #ifdef  TCSIG
1736         ptyflush(ncct); /* half-hearted */
1737         (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
1738 #else   /* TCSIG */
1739         unsigned char ch;
1740         ptyflush(ncct); /* half-hearted */
1741         init_termbuf(ncct);
1742         ch = slctab[SLC_ABORT].sptr ?
1743                         (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
1744         pty_write(ncct, &ch, 1);
1745 #endif  /* TCSIG */
1746 }
1747
1748 void
1749 sendsusp(noit_console_closure_t ncct)
1750 {
1751 #ifdef  SIGTSTP
1752 # ifdef TCSIG
1753         ptyflush(ncct); /* half-hearted */
1754         (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
1755 # else  /* TCSIG */
1756         unsigned char ch;
1757         ptyflush(ncct); /* half-hearted */
1758         ch = slctab[SLC_SUSP].sptr ?
1759                         (unsigned char)*slctab[SLC_SUSP].sptr : '\032';
1760         pty_write(ncct, &ch, 1);
1761 # endif /* TCSIG */
1762 #endif  /* SIGTSTP */
1763 }
1764
1765 /*
1766  * When we get an AYT, if ^T is enabled, use that.  Otherwise,
1767  * just send back "[Yes]".
1768  */
1769 void
1770 recv_ayt(noit_console_closure_t ncct)
1771 {
1772 #if     defined(SIGINFO) && defined(TCSIG)
1773         if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
1774                 (void) ioctl(pty, TCSIG, (char *)SIGINFO);
1775                 return;
1776         }
1777 #endif
1778         nc_printf(ncct, "\r\n[Yes]\r\n");
1779 }
1780
1781 void
1782 doeof(noit_console_closure_t ncct)
1783 {
1784         unsigned char ch;
1785         init_termbuf(ncct);
1786
1787 #if     defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
1788         if (!noit_console_telnet_tty_isediting(ncct)) {
1789                 extern char oldeofc;
1790                 pty_write(ncct, &oldeofc, 1);
1791                 return;
1792         }
1793 #endif
1794         ch = slctab[SLC_EOF].sptr ?
1795                         (unsigned char)*slctab[SLC_EOF].sptr : '\004';
1796         pty_write(ncct, &ch, 1);
1797 }
1798
1799 noit_console_telnet_closure_t
1800 noit_console_telnet_alloc(noit_console_closure_t ncct) {
1801   noit_console_telnet_closure_t telnet, tmp;
1802   static unsigned char ttytype_sbbuf[] = {
1803     IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
1804   };
1805   tmp = ncct->telnet;
1806
1807   ncct->telnet = calloc(1, sizeof(*telnet));
1808   subbuffer = malloc(1024*64);
1809   subpointer = subbuffer;
1810   subend= subbuffer;
1811   def_tspeed = -1;
1812   def_rspeed = -1;
1813   get_slc_defaults(ncct);
1814   if (my_state_is_wont(TELOPT_SGA))
1815     send_will(TELOPT_SGA, 1);
1816   send_do(TELOPT_ECHO, 1);
1817   send_do(TELOPT_NAWS, 1);
1818   send_will(TELOPT_STATUS, 1);
1819   flowmode = 1;         /* default flow control state */
1820   restartany = -1;      /* uninitialized... */
1821   send_do(TELOPT_LFLOW, 1);
1822   if (his_want_state_is_will(TELOPT_ECHO)) {
1823     willoption(TELOPT_ECHO);
1824   }
1825   if (my_state_is_wont(TELOPT_ECHO))
1826     send_will(TELOPT_ECHO, 1);
1827   init_termbuf(ncct);
1828 #ifdef LINEMODE
1829   localstat(ncct);
1830 #endif
1831   send_do(TELOPT_TTYPE, 1);
1832   send_do(TELOPT_TSPEED, 1);
1833   send_do(TELOPT_XDISPLOC, 1);
1834   send_do(TELOPT_NEW_ENVIRON, 1);
1835   send_do(TELOPT_OLD_ENVIRON, 1);
1836   nc_write(ncct, ttytype_sbbuf, sizeof(ttytype_sbbuf));
1837
1838   telnet = ncct->telnet;
1839   ncct->telnet = tmp;
1840   return telnet;
1841 }
1842 void
1843 noit_console_telnet_free(noit_console_telnet_closure_t telnet) {
1844   free(telnet->_subbuffer);
1845   noit_hash_destroy(&telnet->_env, free, free);
1846   free(telnet);
1847 }
1848
1849 #define SB_CLEAR()      subpointer = subbuffer
1850 #define SB_TERM()       { subend = subpointer; SB_CLEAR(); }
1851 #define SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
1852     *subpointer++ = (c); \
1853                              }
1854 #define SB_GET()        ((*subpointer++)&0xff)
1855 #define SB_EOF()        (subpointer >= subend)
1856 #define SB_LEN()        (subend - subpointer)
1857
1858 #ifdef  ENV_HACK
1859 unsigned char *subsave;
1860 #define SB_SAVE()       subsave = subpointer;
1861 #define SB_RESTORE()    subpointer = subsave;
1862 #endif
1863
1864
1865 /*
1866  * State for recv fsm
1867  */
1868 #define TS_DATA         0       /* base state */
1869 #define TS_IAC          1       /* look for double IAC's */
1870 #define TS_CR           2       /* CR-LF ->'s CR */
1871 #define TS_SB           3       /* throw away begin's... */
1872 #define TS_SE           4       /* ...end's (suboption negotiation) */
1873 #define TS_WILL         5       /* will option negotiation */
1874 #define TS_WONT         6       /* wont -''- */
1875 #define TS_DO           7       /* do -''- */
1876 #define TS_DONT         8       /* dont -''- */
1877
1878 int
1879 noit_console_telnet_telrcv(noit_console_closure_t ncct,
1880                            const void *buf, int buflen)
1881 {
1882     int c;
1883     unsigned char cc;
1884     unsigned char *netip = (unsigned char *)buf;
1885     int ncc = buflen;
1886     static int state = TS_DATA;
1887
1888     while (ncc > 0) {
1889         c = *netip++ & 0377, ncc--;
1890 #ifdef ENCRYPTION
1891         if (decrypt_input)
1892             c = (*decrypt_input)(c);
1893 #endif
1894         switch (state) {
1895
1896         case TS_CR:
1897             state = TS_DATA;
1898             /* Strip off \n or \0 after a \r */
1899             if ((c == 0) || (c == '\n')) {
1900                 break;
1901             }
1902             /* FALL THROUGH */
1903
1904         case TS_DATA:
1905             if (c == IAC) {
1906                 state = TS_IAC;
1907                 break;
1908             }
1909             /*
1910              * We now map \r\n ==> \r for pragmatic reasons.
1911              * Many client implementations send \r\n when
1912              * the user hits the CarriageReturn key.
1913              *
1914              * We USED to map \r\n ==> \n, since \r\n says
1915              * that we want to be in column 1 of the next
1916              * printable line, and \n is the standard
1917              * unix way of saying that (\r is only good
1918              * if CRMOD is set, which it normally is).
1919              */
1920             if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
1921                 int nc = *netip;
1922 #ifdef ENCRYPTION
1923                 if (decrypt_input)
1924                     nc = (*decrypt_input)(nc & 0xff);
1925 #endif
1926                 if (linemode && (ncc > 0) && (('\n' == nc) ||
1927                          ((0 == nc) && noit_console_telnet_tty_iscrnl(ncct))) ) {
1928                         netip++; ncc--;
1929                         c = '\n';
1930                 } else
1931                 {
1932 #ifdef ENCRYPTION
1933                     if (decrypt_input)
1934                         (void)(*decrypt_input)(-1);
1935 #endif
1936                     state = TS_CR;
1937                 }
1938             }
1939             cc = (unsigned char)c;
1940             pty_write(ncct, &cc, 1);
1941             break;
1942
1943         case TS_IAC:
1944         gotiac:                 switch (c) {
1945
1946             /*
1947              * Send the process on the pty side an
1948              * interrupt.  Do this with a NULL or
1949              * interrupt char; depending on the tty mode.
1950              */
1951         case IP:
1952             interrupt(ncct);
1953             break;
1954
1955         case BREAK:
1956             sendbrk(ncct);
1957             break;
1958
1959             /*
1960              * Are You There?
1961              */
1962         case AYT:
1963             recv_ayt(ncct);
1964             break;
1965
1966             /*
1967              * Abort Output
1968              */
1969         case AO:
1970             {
1971                 ptyflush(ncct); /* half-hearted */
1972                 init_termbuf(ncct);
1973
1974                 if (slctab[SLC_AO].sptr &&
1975                     *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
1976                     cc = (unsigned char)*slctab[SLC_AO].sptr;
1977                     pty_write(ncct, &cc, 1);
1978                 }
1979
1980                 netclear(ncct); /* clear buffer back */
1981                 nc_printf (ncct, "%c%c", IAC, DM);
1982                 /* Wha?
1983                 neturg = nfrontp-1;
1984                 */
1985                 /* off by one XXX */
1986                 break;
1987             }
1988
1989         /*
1990          * Erase Character and
1991          * Erase Line
1992          */
1993         case EC:
1994         case EL:
1995             {
1996                 cc_t ch;
1997
1998                 ptyflush(ncct); /* half-hearted */
1999                 init_termbuf(ncct);
2000                 if (c == EC)
2001                     ch = *slctab[SLC_EC].sptr;
2002                 else
2003                     ch = *slctab[SLC_EL].sptr;
2004                 if (ch != (cc_t)(_POSIX_VDISABLE)) {
2005                     cc = (unsigned char)ch;
2006                     pty_write(ncct, &cc, 1);
2007                 }
2008                 break;
2009             }
2010
2011         /*
2012          * Check for urgent data...
2013          */
2014         case DM:
2015 #ifdef SUPPORT_OOB
2016             SYNCHing = stilloob(net);
2017             settimer(gotDM);
2018 #endif
2019             break;
2020
2021
2022             /*
2023              * Begin option subnegotiation...
2024              */
2025         case SB:
2026             state = TS_SB;
2027             SB_CLEAR();
2028             continue;
2029
2030         case WILL:
2031             state = TS_WILL;
2032             continue;
2033
2034         case WONT:
2035             state = TS_WONT;
2036             continue;
2037
2038         case DO:
2039             state = TS_DO;
2040             continue;
2041
2042         case DONT:
2043             state = TS_DONT;
2044             continue;
2045         case EOR:
2046             if (his_state_is_will(TELOPT_EOR))
2047                 doeof(ncct);
2048             break;
2049
2050             /*
2051              * Handle RFC 10xx Telnet linemode option additions
2052              * to command stream (EOF, SUSP, ABORT).
2053              */
2054         case xEOF:
2055             doeof(ncct);
2056             break;
2057
2058         case SUSP:
2059             sendsusp(ncct);
2060             break;
2061
2062         case ABORT:
2063             sendbrk(ncct);
2064             break;
2065
2066         case IAC:
2067             cc = (unsigned char)c;
2068             pty_write(ncct, &cc, 1);
2069             break;
2070         }
2071         state = TS_DATA;
2072         break;
2073
2074         case TS_SB:
2075             if (c == IAC) {
2076                 state = TS_SE;
2077             } else {
2078                 SB_ACCUM(c);
2079             }
2080             break;
2081
2082         case TS_SE:
2083             if (c != SE) {
2084                 if (c != IAC) {
2085                     /*
2086                      * bad form of suboption negotiation.
2087                      * handle it in such a way as to avoid
2088                      * damage to local state.  Parse
2089                      * suboption buffer found so far,
2090                      * then treat remaining stream as
2091                      * another command sequence.
2092                      */
2093
2094                     /* for DIAGNOSTICS */
2095                     SB_ACCUM(IAC);
2096                     SB_ACCUM(c);
2097                     subpointer -= 2;
2098
2099                     SB_TERM();
2100                     suboption(ncct);
2101                     state = TS_IAC;
2102                     goto gotiac;
2103                 }
2104                 SB_ACCUM(c);
2105                 state = TS_SB;
2106             } else {
2107                 /* for DIAGNOSTICS */
2108                 SB_ACCUM(IAC);
2109                 SB_ACCUM(SE);
2110                 subpointer -= 2;
2111
2112                 SB_TERM();
2113                 suboption(ncct);        /* handle sub-option */
2114                 state = TS_DATA;
2115             }
2116             break;
2117
2118         case TS_WILL:
2119             willoption(c);
2120             state = TS_DATA;
2121             continue;
2122
2123         case TS_WONT:
2124             wontoption(c);
2125             if (c==TELOPT_ENCRYPT && his_do_dont_is_changing(TELOPT_ENCRYPT) )
2126                 dontoption(c);
2127             state = TS_DATA;
2128             continue;
2129
2130         case TS_DO:
2131             dooption(c);
2132             state = TS_DATA;
2133             continue;
2134
2135         case TS_DONT:
2136             dontoption(c);
2137             state = TS_DATA;
2138             continue;
2139
2140         default:
2141             return -1;
2142         }
2143     }
2144     return 0;
2145 }  /* end of telrcv */
2146
2147 /*
2148  * The will/wont/do/dont state machines are based on Dave Borman's
2149  * Telnet option processing state machine.
2150  *
2151  * These correspond to the following states:
2152  *      my_state = the last negotiated state
2153  *      want_state = what I want the state to go to
2154  *      want_resp = how many requests I have sent
2155  * All state defaults are negative, and resp defaults to 0.
2156  *
2157  * When initiating a request to change state to new_state:
2158  *
2159  * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) {
2160  *      do nothing;
2161  * } else {
2162  *      want_state = new_state;
2163  *      send new_state;
2164  *      want_resp++;
2165  * }
2166  *
2167  * When receiving new_state:
2168  *
2169  * if (want_resp) {
2170  *      want_resp--;
2171  *      if (want_resp && (new_state == my_state))
2172  *              want_resp--;
2173  * }
2174  * if ((want_resp == 0) && (new_state != want_state)) {
2175  *      if (ok_to_switch_to new_state)
2176  *              want_state = new_state;
2177  *      else
2178  *              want_resp++;
2179  *      send want_state;
2180  * }
2181  * my_state = new_state;
2182  *
2183  * Note that new_state is implied in these functions by the function itself.
2184  * will and do imply positive new_state, wont and dont imply negative.
2185  *
2186  * Finally, there is one catch.  If we send a negative response to a
2187  * positive request, my_state will be the positive while want_state will
2188  * remain negative.  my_state will revert to negative when the negative
2189  * acknowlegment arrives from the peer.  Thus, my_state generally tells
2190  * us not only the last negotiated state, but also tells us what the peer
2191  * wants to be doing as well.  It is important to understand this difference
2192  * as we may wish to be processing data streams based on our desired state
2193  * (want_state) or based on what the peer thinks the state is (my_state).
2194  *
2195  * This all works fine because if the peer sends a positive request, the data
2196  * that we receive prior to negative acknowlegment will probably be affected
2197  * by the positive state, and we can process it as such (if we can; if we
2198  * can't then it really doesn't matter).  If it is that important, then the
2199  * peer probably should be buffering until this option state negotiation
2200  * is complete.
2201  *
2202  */
2203 void
2204 noit_console_telnet_send_do(noit_console_closure_t ncct, int option, int init)
2205 {
2206     if (init) {
2207         if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
2208             his_want_state_is_will(option))
2209             return;
2210         /*
2211          * Special case for TELOPT_TM:  We send a DO, but pretend
2212          * that we sent a DONT, so that we can send more DOs if
2213          * we want to.
2214          */
2215         if (option == TELOPT_TM)
2216             set_his_want_state_wont(option);
2217         else
2218             set_his_want_state_will(option);
2219         do_dont_resp[option]++;
2220     }
2221     nc_printf(ncct, (const char *)doopt, option);
2222 }
2223
2224 #ifdef  AUTHENTICATION
2225 extern void auth_request(void);
2226 #endif
2227 #ifdef  ENCRYPTION
2228 extern void encrypt_send_support();
2229 #endif
2230
2231 void
2232 noit_console_telnet_willoption(noit_console_closure_t ncct, int option)
2233 {
2234     int changeok = 0;
2235     void (*func)(noit_console_closure_t) = 0;
2236
2237     /*
2238      * process input from peer.
2239      */
2240
2241     if (do_dont_resp[option]) {
2242         do_dont_resp[option]--;
2243         if (do_dont_resp[option] && his_state_is_will(option))
2244             do_dont_resp[option]--;
2245     }
2246     if (do_dont_resp[option] == 0) {
2247         if (his_want_state_is_wont(option)) {
2248             switch (option) {
2249
2250             case TELOPT_BINARY:
2251                 init_termbuf(ncct);
2252                 noit_console_telnet_tty_binaryin(ncct, 1);
2253                 set_termbuf(ncct);
2254                 changeok++;
2255                 break;
2256
2257             case TELOPT_ECHO:
2258                 /*
2259                  * See comments below for more info.
2260                  */
2261                 not42 = 0;      /* looks like a 4.2 system */
2262                 break;
2263
2264             case TELOPT_TM:
2265 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)
2266                         /*
2267                          * This telnetd implementation does not really
2268                          * support timing marks, it just uses them to
2269                          * support the kludge linemode stuff.  If we
2270                          * receive a will or wont TM in response to our
2271                          * do TM request that may have been sent to
2272                          * determine kludge linemode support, process
2273                          * it, otherwise TM should get a negative
2274                          * response back.
2275                          */
2276                         /*
2277                          * Handle the linemode kludge stuff.
2278                          * If we are not currently supporting any
2279                          * linemode at all, then we assume that this
2280                          * is the client telling us to use kludge
2281                          * linemode in response to our query.  Set the
2282                          * linemode type that is to be supported, note
2283                          * that the client wishes to use linemode, and
2284                          * eat the will TM as though it never arrived.
2285                          */
2286                         if (lmodetype < KLUDGE_LINEMODE) {
2287                                 lmodetype = KLUDGE_LINEMODE;
2288                                 clientstat(ncct, TELOPT_LINEMODE, WILL, 0);
2289                                 send_wont(TELOPT_SGA, 1);
2290                         } else if (lmodetype == NO_AUTOKLUDGE) {
2291                                 lmodetype = KLUDGE_OK;
2292                         }
2293 #endif  /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
2294                 break;
2295
2296             case TELOPT_LFLOW:
2297                 /*
2298                  * If we are going to support flow control
2299                  * option, then don't worry peer that we can't
2300                  * change the flow control characters.
2301                  */
2302                 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
2303                 slctab[SLC_XON].defset.flag |= SLC_DEFAULT;
2304                 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
2305                 slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT;
2306             case TELOPT_TTYPE:
2307             case TELOPT_SGA:
2308             case TELOPT_NAWS:
2309             case TELOPT_TSPEED:
2310             case TELOPT_XDISPLOC:
2311             case TELOPT_NEW_ENVIRON:
2312             case TELOPT_OLD_ENVIRON:
2313                 changeok++;
2314                 break;
2315
2316 #ifdef  LINEMODE
2317                 case TELOPT_LINEMODE:
2318 # ifdef KLUDGELINEMODE
2319                         /*
2320                          * Note client's desire to use linemode.
2321                          */
2322                         lmodetype = REAL_LINEMODE;
2323 # endif /* KLUDGELINEMODE */
2324                         func = noit_console_telnet_doclientstat;
2325                         changeok++;
2326                         break;
2327 #endif  /* LINEMODE */
2328
2329 #ifdef  AUTHENTICATION
2330             case TELOPT_AUTHENTICATION:
2331                 func = auth_request;
2332                 changeok++;
2333                 break;
2334 #endif
2335
2336 #ifdef  ENCRYPTION
2337             case TELOPT_ENCRYPT:
2338                 func = encrypt_send_support;
2339                 changeok++;
2340                 break;
2341 #endif
2342                        
2343             default:
2344                 break;
2345             }
2346             if (changeok) {
2347                 set_his_want_state_will(option);
2348                 send_do(option, 0);
2349             } else {
2350                 do_dont_resp[option]++;
2351                 send_dont(option, 0);
2352             }
2353         } else {
2354             /*
2355              * Option processing that should happen when
2356              * we receive conformation of a change in
2357              * state that we had requested.
2358              */
2359             switch (option) {
2360             case TELOPT_ECHO:
2361                 not42 = 0;      /* looks like a 4.2 system */
2362                 /*
2363                  * Egads, he responded "WILL ECHO".  Turn
2364                  * it off right now!
2365                  */
2366                 send_dont(option, 1);
2367                 /*
2368                  * "WILL ECHO".  Kludge upon kludge!
2369                  * A 4.2 client is now echoing user input at
2370                  * the tty.  This is probably undesireable and
2371                  * it should be stopped.  The client will
2372                  * respond WONT TM to the DO TM that we send to
2373                  * check for kludge linemode.  When the WONT TM
2374                  * arrives, linemode will be turned off and a
2375                  * change propogated to the pty.  This change
2376                  * will cause us to process the new pty state
2377                  * in localstat(), which will notice that
2378                  * linemode is off and send a WILL ECHO
2379                  * so that we are properly in character mode and
2380                  * all is well.
2381                  */
2382                 break;
2383
2384 #ifdef  LINEMODE
2385                 case TELOPT_LINEMODE:
2386 # ifdef KLUDGELINEMODE
2387                         /*
2388                          * Note client's desire to use linemode.
2389                          */
2390                         lmodetype = REAL_LINEMODE;
2391 # endif /* KLUDGELINEMODE */
2392                         func = noit_console_telnet_doclientstat;
2393                         break;
2394 #endif  /* LINEMODE */
2395 #ifdef  AUTHENTICATION
2396             case TELOPT_AUTHENTICATION:
2397                 func = auth_request;
2398                 break;
2399 #endif
2400
2401 #ifdef  ENCRYPTION
2402             case TELOPT_ENCRYPT:
2403                 func = encrypt_send_support;
2404                 break;
2405 #endif
2406
2407             case TELOPT_LFLOW:
2408                 func = flowstat;
2409                 break;
2410             }
2411         }
2412     }
2413     set_his_state_will(option);
2414     if (func)
2415         (*func)(ncct);
2416 }  /* end of willoption */
2417
2418 void
2419 noit_console_telnet_send_dont(noit_console_closure_t ncct, int option, int init)
2420 {
2421     if (init) {
2422         if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
2423             his_want_state_is_wont(option))
2424             return;
2425         set_his_want_state_wont(option);
2426         do_dont_resp[option]++;
2427     }
2428     nc_printf(ncct, (const char *)dont, option);
2429 }
2430
2431 void
2432 noit_console_telnet_wontoption(noit_console_closure_t ncct, int option)
2433 {
2434     /*
2435      * Process client input.
2436          */
2437
2438     if (do_dont_resp[option]) {
2439         do_dont_resp[option]--;
2440         if (do_dont_resp[option] && his_state_is_wont(option))
2441             do_dont_resp[option]--;
2442     }
2443     if (do_dont_resp[option] == 0) {
2444         if (his_want_state_is_will(option)) {
2445             /* it is always ok to change to negative state */
2446             switch (option) {
2447             case TELOPT_ECHO:
2448                 not42 = 1; /* doesn't seem to be a 4.2 system */
2449                 break;
2450
2451             case TELOPT_BINARY:
2452                 init_termbuf(ncct);
2453                 noit_console_telnet_tty_binaryin(ncct, 0);
2454                 set_termbuf(ncct);
2455                 break;
2456
2457 #ifdef  LINEMODE
2458             case TELOPT_LINEMODE:
2459 # ifdef KLUDGELINEMODE
2460                 /*
2461                  * If real linemode is supported, then client is
2462                  * asking to turn linemode off.
2463                  */
2464                 if (lmodetype != REAL_LINEMODE)
2465                         break;
2466                 lmodetype = KLUDGE_LINEMODE;
2467 # endif /* KLUDGELINEMODE */
2468                 clientstat(ncct, TELOPT_LINEMODE, WONT, 0);
2469                 break;
2470 #endif  /* LINEMODE */
2471
2472             case TELOPT_TM:
2473                 /*
2474                  * If we get a WONT TM, and had sent a DO TM,
2475                  * don't respond with a DONT TM, just leave it
2476                  * as is.  Short circut the state machine to
2477                  * achive this.
2478                  */
2479                 set_his_want_state_wont(TELOPT_TM);
2480                 return;
2481
2482             case TELOPT_LFLOW:
2483                 /*
2484                  * If we are not going to support flow control
2485                  * option, then let peer know that we can't
2486                  * change the flow control characters.
2487                  */
2488                 slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS;
2489                 slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE;
2490                 slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS;
2491                 slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE;
2492                 break;
2493
2494 #ifdef AUTHENTICATION
2495             case TELOPT_AUTHENTICATION:
2496                 auth_finished(0, AUTH_REJECT);
2497                 break;
2498 #endif
2499
2500                 /*
2501                  * For options that we might spin waiting for
2502                  * sub-negotiation, if the client turns off the
2503                  * option rather than responding to the request,
2504                  * we have to treat it here as if we got a response
2505                  * to the sub-negotiation, (by updating the timers)
2506                  * so that we'll break out of the loop.
2507                  */
2508             case TELOPT_TTYPE:
2509                 settimer(ttypesubopt);
2510                 break;
2511
2512             case TELOPT_TSPEED:
2513                 settimer(tspeedsubopt);
2514                 break;
2515
2516             case TELOPT_XDISPLOC:
2517                 settimer(xdisplocsubopt);
2518                 break;
2519
2520             case TELOPT_OLD_ENVIRON:
2521                 settimer(oenvironsubopt);
2522                 break;
2523
2524             case TELOPT_NEW_ENVIRON:
2525                 settimer(environsubopt);
2526                 break;
2527
2528             default:
2529                 break;
2530             }
2531             set_his_want_state_wont(option);
2532             if (his_state_is_will(option))
2533                 send_dont(option, 0);
2534         } else {
2535             switch (option) {
2536             case TELOPT_TM:
2537 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)
2538                 if (lmodetype < NO_AUTOKLUDGE) {
2539                         lmodetype = NO_LINEMODE;
2540                         clientstat(TELOPT_LINEMODE, WONT, 0);
2541                         send_will(TELOPT_SGA, 1);
2542                         send_will(TELOPT_ECHO, 1);
2543                 }
2544 #endif  /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
2545                 break;
2546
2547 #ifdef AUTHENTICATION
2548             case TELOPT_AUTHENTICATION:
2549                 auth_finished(0, AUTH_REJECT);
2550                 break;
2551 #endif
2552             default:
2553                 break;
2554             }
2555         }
2556     }
2557     set_his_state_wont(option);
2558
2559 }  /* end of wontoption */
2560
2561 void
2562 noit_console_telnet_send_will(noit_console_closure_t ncct, int option, int init)
2563 {
2564     if (init) {
2565         if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
2566             my_want_state_is_will(option))
2567             return;
2568         set_my_want_state_will(option);
2569         will_wont_resp[option]++;
2570     }
2571     nc_printf (ncct, (const char *)will, option);
2572 }
2573
2574 void
2575 noit_console_telnet_dooption(noit_console_closure_t ncct, int option)
2576 {
2577     int changeok = 0;
2578
2579     /*
2580      * Process client input.
2581      */
2582
2583     if (will_wont_resp[option]) {
2584         will_wont_resp[option]--;
2585         if (will_wont_resp[option] && my_state_is_will(option))
2586             will_wont_resp[option]--;
2587     }
2588     if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) {
2589         switch (option) {
2590         case TELOPT_ECHO:
2591 #ifdef  LINEMODE
2592 # ifdef KLUDGELINEMODE
2593             if (lmodetype == NO_LINEMODE)
2594 # else
2595             if (his_state_is_wont(TELOPT_LINEMODE))
2596 # endif
2597 #endif
2598             {
2599                 init_termbuf(ncct);
2600                 noit_console_telnet_tty_setecho(ncct, 1);
2601                 set_termbuf(ncct);
2602             }
2603         changeok++;
2604         break;
2605
2606         case TELOPT_BINARY:
2607             init_termbuf(ncct);
2608             noit_console_telnet_tty_binaryout(ncct, 1);
2609             set_termbuf(ncct);
2610             changeok++;
2611             break;
2612
2613         case TELOPT_SGA:
2614 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)
2615                 /*
2616                  * If kludge linemode is in use, then we must
2617                  * process an incoming do SGA for linemode
2618                  * purposes.
2619                  */
2620             if (lmodetype == KLUDGE_LINEMODE) {
2621                 /*
2622                  * Receipt of "do SGA" in kludge
2623                  * linemode is the peer asking us to
2624                  * turn off linemode.  Make note of
2625                  * the request.
2626                  */
2627                 clientstat(TELOPT_LINEMODE, WONT, 0);
2628                 /*
2629                  * If linemode did not get turned off
2630                  * then don't tell peer that we did.
2631                  * Breaking here forces a wont SGA to
2632                  * be returned.
2633                  */
2634                 if (linemode)
2635                         break;
2636             }
2637 #else
2638             turn_on_sga = 0;
2639 #endif  /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
2640             changeok++;
2641             break;
2642
2643         case TELOPT_STATUS:
2644             changeok++;
2645             break;
2646
2647         case TELOPT_TM:
2648             /*
2649              * Special case for TM.  We send a WILL, but
2650              * pretend we sent a WONT.
2651              */
2652             send_will(option, 0);
2653             set_my_want_state_wont(option);
2654             set_my_state_wont(option);
2655             return;
2656
2657         case TELOPT_LOGOUT:
2658             /*
2659              * When we get a LOGOUT option, respond
2660              * with a WILL LOGOUT, make sure that
2661              * it gets written out to the network,
2662              * and then just go away...
2663              */
2664             set_my_want_state_will(TELOPT_LOGOUT);
2665             send_will(TELOPT_LOGOUT, 0);
2666             set_my_state_will(TELOPT_LOGOUT);
2667             netflush(ncct);
2668             return;
2669             break;
2670
2671 #ifdef ENCRYPTION
2672         case TELOPT_ENCRYPT:
2673             changeok++;
2674             break;
2675 #endif
2676         case TELOPT_LINEMODE:
2677         case TELOPT_TTYPE:
2678         case TELOPT_NAWS:
2679         case TELOPT_TSPEED:
2680         case TELOPT_LFLOW:
2681         case TELOPT_XDISPLOC:
2682 #ifdef  TELOPT_ENVIRON
2683         case TELOPT_NEW_ENVIRON:
2684 #endif
2685         case TELOPT_OLD_ENVIRON:
2686         default:
2687             break;
2688         }
2689         if (changeok) {
2690             set_my_want_state_will(option);
2691             send_will(option, 0);
2692         } else {
2693             will_wont_resp[option]++;
2694             send_wont(option, 0);
2695         }
2696     }
2697     set_my_state_will(option);
2698 }  /* end of dooption */
2699
2700 void
2701 noit_console_telnet_send_wont(noit_console_closure_t ncct, int option, int init)
2702 {
2703     if (init) {
2704         if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
2705             my_want_state_is_wont(option))
2706             return;
2707         set_my_want_state_wont(option);
2708         will_wont_resp[option]++;
2709     }
2710     nc_printf (ncct, (const char *)wont, option);
2711 }
2712
2713 void
2714 noit_console_telnet_dontoption(noit_console_closure_t ncct, int option)
2715 {
2716     /*
2717      * Process client input.
2718          */
2719
2720
2721     if (will_wont_resp[option]) {
2722         will_wont_resp[option]--;
2723         if (will_wont_resp[option] && my_state_is_wont(option))
2724             will_wont_resp[option]--;
2725     }
2726     if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) {
2727         switch (option) {
2728         case TELOPT_BINARY:
2729             init_termbuf(ncct);
2730             noit_console_telnet_tty_binaryout(ncct, 0);
2731             set_termbuf(ncct);
2732             break;
2733
2734         case TELOPT_ECHO:       /* we should stop echoing */
2735 #ifdef  LINEMODE
2736 # ifdef KLUDGELINEMODE
2737             if ((lmodetype != REAL_LINEMODE) &&
2738                     (lmodetype != KLUDGE_LINEMODE))
2739 # else
2740             if (his_state_is_wont(TELOPT_LINEMODE))
2741 # endif
2742 #endif
2743             {
2744                 init_termbuf(ncct);
2745                 noit_console_telnet_tty_setecho(ncct, 0);
2746                 set_termbuf(ncct);
2747             }
2748         break;
2749
2750         case TELOPT_SGA:
2751 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)
2752                 /*
2753                  * If kludge linemode is in use, then we
2754                  * must process an incoming do SGA for
2755                  * linemode purposes.
2756                  */
2757             if ((lmodetype == KLUDGE_LINEMODE) ||
2758                     (lmodetype == KLUDGE_OK)) {
2759                 /*
2760                  * The client is asking us to turn
2761                  * linemode on.
2762                  */
2763                 lmodetype = KLUDGE_LINEMODE;
2764                 clientstat(TELOPT_LINEMODE, WILL, 0);
2765                 /*
2766                  * If we did not turn line mode on,
2767                  * then what do we say?  Will SGA?
2768                  * This violates design of telnet.
2769                  * Gross.  Very Gross.
2770                  */
2771             }
2772             break;
2773 #else
2774             set_my_want_state_wont(option);
2775             if (my_state_is_will(option))
2776                 send_wont(option, 0);
2777             set_my_state_wont(option);
2778             if (turn_on_sga ^= 1)
2779                 send_will(option, 1);
2780             return;
2781 #endif  /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
2782
2783         default:
2784             break;
2785         }
2786
2787         set_my_want_state_wont(option);
2788         if (my_state_is_will(option))
2789             send_wont(option, 0);
2790     }
2791     set_my_state_wont(option);
2792
2793 }  /* end of dontoption */
2794
2795 #ifdef  ENV_HACK
2796 int env_ovar = -1;
2797 int env_ovalue = -1;
2798 #else   /* ENV_HACK */
2799 # define env_ovar OLD_ENV_VAR
2800 # define env_ovalue OLD_ENV_VALUE
2801 #endif  /* ENV_HACK */
2802
2803 /*
2804  * suboption()
2805  *
2806  *      Look at the sub-option buffer, and try to be helpful to the other
2807  * side.
2808  *
2809  *      Currently we recognize:
2810  *
2811  *      Terminal type is
2812  *      Linemode
2813  *      Window size
2814  *      Terminal speed
2815  */
2816 static void
2817 suboption(noit_console_closure_t ncct)
2818 {
2819     int subchar;
2820
2821     subchar = SB_GET();
2822     switch (subchar) {
2823     case TELOPT_TSPEED: {
2824         int xspeed, rspeed;
2825
2826         if (his_state_is_wont(TELOPT_TSPEED))   /* Ignore if option disabled */
2827             break;
2828
2829         settimer(tspeedsubopt);
2830
2831         if (SB_EOF() || SB_GET() != TELQUAL_IS)
2832             return;
2833
2834         xspeed = atoi((char *)subpointer);
2835
2836         while (SB_GET() != ',' && !SB_EOF());
2837         if (SB_EOF())
2838             return;
2839
2840         rspeed = atoi((char *)subpointer);
2841         clientstat(ncct, TELOPT_TSPEED, xspeed, rspeed);
2842
2843         break;
2844
2845     }  /* end of case TELOPT_TSPEED */
2846
2847     case TELOPT_TTYPE: {                /* Yaaaay! */
2848         char *terminaltype;
2849         if (his_state_is_wont(TELOPT_TTYPE))    /* Ignore if option disabled */
2850             break;
2851         settimer(ttypesubopt);
2852
2853         if (SB_EOF() || SB_GET() != TELQUAL_IS) {
2854             return;             /* ??? XXX but, this is the most robust */
2855         }
2856
2857         terminaltype = terminalname;
2858
2859         while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
2860                !SB_EOF()) {
2861             int c;
2862
2863             c = SB_GET();
2864             if (isupper(c)) {
2865                 c = tolower(c);
2866             }
2867             *terminaltype++ = c;    /* accumulate name */
2868         }
2869         *terminaltype = 0;
2870         break;
2871     }  /* end of case TELOPT_TTYPE */
2872
2873     case TELOPT_NAWS: {
2874         int xwinsize, ywinsize;
2875
2876         if (his_state_is_wont(TELOPT_NAWS))     /* Ignore if option disabled */
2877             break;
2878
2879         if (SB_EOF())
2880             return;
2881         xwinsize = SB_GET() << 8;
2882         if (SB_EOF())
2883             return;
2884         xwinsize |= SB_GET();
2885         if (SB_EOF())
2886             return;
2887         ywinsize = SB_GET() << 8;
2888         if (SB_EOF())
2889             return;
2890         ywinsize |= SB_GET();
2891         clientstat(ncct, TELOPT_NAWS, xwinsize, ywinsize);
2892
2893         break;
2894
2895     }  /* end of case TELOPT_NAWS */
2896 #ifdef  LINEMODE
2897     case TELOPT_LINEMODE: {
2898         int request;
2899
2900         if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */
2901                 break;
2902         /*
2903          * Process linemode suboptions.
2904          */
2905         if (SB_EOF())
2906             break;              /* garbage was sent */
2907         request = SB_GET();     /* get will/wont */
2908
2909         if (SB_EOF())
2910             break;              /* another garbage check */
2911
2912         if (request == LM_SLC) {  /* SLC is not preceeded by WILL or WONT */
2913                 /*
2914                  * Process suboption buffer of slc's
2915                  */
2916                 start_slc(ncct, 1);
2917                 do_opt_slc(ncct, subpointer, subend - subpointer);
2918                 (void) end_slc(ncct, 0);
2919                 break;
2920         } else if (request == LM_MODE) {
2921                 if (SB_EOF())
2922                     return;
2923                 useeditmode = SB_GET();  /* get mode flag */
2924                 clientstat(ncct, LM_MODE, 0, 0);
2925                 break;
2926         }
2927
2928         if (SB_EOF())
2929             break;
2930         switch (SB_GET()) {  /* what suboption? */
2931         case LM_FORWARDMASK:
2932                 /*
2933                  * According to spec, only server can send request for
2934                  * forwardmask, and client can only return a positive response.
2935                  * So don't worry about it.
2936                  */
2937
2938         default:
2939                 break;
2940         }
2941         break;
2942     }  /* end of case TELOPT_LINEMODE */
2943 #endif
2944     case TELOPT_STATUS: {
2945         int mode;
2946
2947         if (SB_EOF())
2948             break;
2949         mode = SB_GET();
2950         switch (mode) {
2951         case TELQUAL_SEND:
2952             if (my_state_is_will(TELOPT_STATUS))
2953                 send_status();
2954             break;
2955
2956         case TELQUAL_IS:
2957             break;
2958
2959         default:
2960             break;
2961         }
2962         break;
2963     }  /* end of case TELOPT_STATUS */
2964
2965     case TELOPT_XDISPLOC: {
2966         if (SB_EOF() || SB_GET() != TELQUAL_IS)
2967             return;
2968         settimer(xdisplocsubopt);
2969         subpointer[SB_LEN()] = '\0';
2970         noit_hash_replace(&ncct->telnet->_env,
2971                           strdup("DISPLAY"), strlen("DISPLAY"),
2972                           strdup((char *)subpointer),
2973                           free, free);
2974         break;
2975     }  /* end of case TELOPT_XDISPLOC */
2976
2977 #ifdef  TELOPT_NEW_ENVIRON
2978     case TELOPT_NEW_ENVIRON:
2979 #endif
2980     case TELOPT_OLD_ENVIRON: {
2981         int c;
2982         char *cp, *varp, *valp;
2983
2984         if (SB_EOF())
2985             return;
2986         c = SB_GET();
2987         if (c == TELQUAL_IS) {
2988             if (subchar == TELOPT_OLD_ENVIRON)
2989                 settimer(oenvironsubopt);
2990             else
2991                 settimer(environsubopt);
2992         } else if (c != TELQUAL_INFO) {
2993             return;
2994         }
2995
2996 #ifdef  TELOPT_NEW_ENVIRON
2997         if (subchar == TELOPT_NEW_ENVIRON) {
2998             while (!SB_EOF()) {
2999                 c = SB_GET();
3000                 if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
3001                     break;
3002             }
3003         } else
3004 #endif
3005             {
3006 #ifdef  ENV_HACK
3007                 /*
3008                  * We only want to do this if we haven't already decided
3009                  * whether or not the other side has its VALUE and VAR
3010                  * reversed.
3011                  */
3012                 if (env_ovar < 0) {
3013                     int last = -1;              /* invalid value */
3014                     int empty = 0;
3015                     int got_var = 0, got_value = 0, got_uservar = 0;
3016
3017                     /*
3018                      * The other side might have its VALUE and VAR values
3019                      * reversed.  To be interoperable, we need to determine
3020                      * which way it is.  If the first recognized character
3021                      * is a VAR or VALUE, then that will tell us what
3022                      * type of client it is.  If the fist recognized
3023                      * character is a USERVAR, then we continue scanning
3024                      * the suboption looking for two consecutive
3025                      * VAR or VALUE fields.  We should not get two
3026                      * consecutive VALUE fields, so finding two
3027                      * consecutive VALUE or VAR fields will tell us
3028                      * what the client is.
3029                      */
3030                     SB_SAVE();
3031                     while (!SB_EOF()) {
3032                         c = SB_GET();
3033                         switch(c) {
3034                         case OLD_ENV_VAR:
3035                             if (last < 0 || last == OLD_ENV_VAR
3036                                 || (empty && (last == OLD_ENV_VALUE)))
3037                                 goto env_ovar_ok;
3038                             got_var++;
3039                             last = OLD_ENV_VAR;
3040                             break;
3041                         case OLD_ENV_VALUE:
3042                             if (last < 0 || last == OLD_ENV_VALUE
3043                                 || (empty && (last == OLD_ENV_VAR)))
3044                                 goto env_ovar_wrong;
3045                             got_value++;
3046                             last = OLD_ENV_VALUE;
3047                             break;
3048                         case ENV_USERVAR:
3049                             /* count strings of USERVAR as one */
3050                             if (last != ENV_USERVAR)
3051                                 got_uservar++;
3052                             if (empty) {
3053                                 if (last == OLD_ENV_VALUE)
3054                                     goto env_ovar_ok;
3055                                 if (last == OLD_ENV_VAR)
3056                                     goto env_ovar_wrong;
3057                             }
3058                             last = ENV_USERVAR;
3059                             break;
3060                         case ENV_ESC:
3061                             if (!SB_EOF())
3062                                 c = SB_GET();
3063                             /* FALL THROUGH */
3064                         default:
3065                             empty = 0;
3066                             continue;
3067                         }
3068                         empty = 1;
3069                     }
3070                     if (empty) {
3071                         if (last == OLD_ENV_VALUE)
3072                             goto env_ovar_ok;
3073                         if (last == OLD_ENV_VAR)
3074                             goto env_ovar_wrong;
3075                     }
3076                     /*
3077                      * Ok, the first thing was a USERVAR, and there
3078                      * are not two consecutive VAR or VALUE commands,
3079                      * and none of the VAR or VALUE commands are empty.
3080                      * If the client has sent us a well-formed option,
3081                      * then the number of VALUEs received should always
3082                      * be less than or equal to the number of VARs and
3083                      * USERVARs received.
3084                      *
3085                      * If we got exactly as many VALUEs as VARs and
3086                      * USERVARs, the client has the same definitions.
3087                      *
3088                      * If we got exactly as many VARs as VALUEs and
3089                      * USERVARS, the client has reversed definitions.
3090                      */
3091                     if (got_uservar + got_var == got_value) {
3092                     env_ovar_ok:
3093                         env_ovar = OLD_ENV_VAR;
3094                         env_ovalue = OLD_ENV_VALUE;
3095                     } else if (got_uservar + got_value == got_var) {
3096                     env_ovar_wrong:
3097                         env_ovar = OLD_ENV_VALUE;
3098                         env_ovalue = OLD_ENV_VAR;
3099                     }
3100                 }
3101                 SB_RESTORE();
3102 #endif
3103
3104                 while (!SB_EOF()) {
3105                     c = SB_GET();
3106                     if ((c == env_ovar) || (c == ENV_USERVAR))
3107                         break;
3108                 }
3109             }
3110
3111         if (SB_EOF())
3112             return;
3113
3114         cp = varp = (char *)subpointer;
3115         valp = 0;
3116
3117         while (!SB_EOF()) {
3118             c = SB_GET();
3119             if (subchar == TELOPT_OLD_ENVIRON) {
3120                 if (c == env_ovar)
3121                     c = NEW_ENV_VAR;
3122                 else if (c == env_ovalue)
3123                     c = NEW_ENV_VALUE;
3124             }
3125             switch (c) {
3126
3127             case NEW_ENV_VALUE:
3128                 *cp = '\0';
3129                 cp = valp = (char *)subpointer;
3130                 break;
3131
3132             case NEW_ENV_VAR:
3133             case ENV_USERVAR:
3134                 *cp = '\0';
3135                 if (valp)
3136                     noit_hash_replace(&ncct->telnet->_env,
3137                                       strdup(varp), strlen(varp),
3138                                       strdup(valp),
3139                                       free, free);
3140                 else
3141                     noit_hash_delete(&ncct->telnet->_env, varp, strlen(varp),
3142                                      free, free);
3143                 cp = varp = (char *)subpointer;
3144                 valp = 0;
3145                 break;
3146
3147             case ENV_ESC:
3148                 if (SB_EOF())
3149                     break;
3150                 c = SB_GET();
3151                 /* FALL THROUGH */
3152             default:
3153                 *cp++ = c;
3154                 break;
3155             }
3156         }
3157         *cp = '\0';
3158         if (valp)
3159             noit_hash_replace(&ncct->telnet->_env,
3160                               strdup(varp), strlen(varp),
3161                               strdup(valp),
3162                               free, free);
3163         else
3164             noit_hash_delete(&ncct->telnet->_env, varp, strlen(varp),
3165                              free, free);
3166         break;
3167     }  /* end of case TELOPT_NEW_ENVIRON */
3168 #ifdef AUTHENTICATION
3169     case TELOPT_AUTHENTICATION:
3170         if (SB_EOF())
3171             break;
3172         switch(SB_GET()) {
3173         case TELQUAL_SEND:
3174         case TELQUAL_REPLY:
3175             /*
3176              * These are sent by us and cannot be sent by
3177              * the client.
3178              */
3179             break;
3180         case TELQUAL_IS:
3181             auth_is(subpointer, SB_LEN());
3182             break;
3183         case TELQUAL_NAME:
3184             auth_name(subpointer, SB_LEN());
3185             break;
3186         }
3187         break;
3188 #endif
3189 #ifdef ENCRYPTION
3190     case TELOPT_ENCRYPT:
3191         if (SB_EOF())
3192             break;
3193         switch(SB_GET()) {
3194         case ENCRYPT_SUPPORT:
3195             encrypt_support(subpointer, SB_LEN());
3196             break;
3197         case ENCRYPT_IS:
3198             encrypt_is(subpointer, SB_LEN());
3199             break;
3200         case ENCRYPT_REPLY:
3201             encrypt_reply(subpointer, SB_LEN());
3202             break;
3203         case ENCRYPT_START:
3204             encrypt_start(subpointer, SB_LEN());
3205             break;
3206         case ENCRYPT_END:
3207             encrypt_end();
3208             break;
3209         case ENCRYPT_REQSTART:
3210             encrypt_request_start(subpointer, SB_LEN());
3211             break;
3212         case ENCRYPT_REQEND:
3213             /*
3214              * We can always send an REQEND so that we cannot
3215              * get stuck encrypting.  We should only get this
3216              * if we have been able to get in the correct mode
3217              * anyhow.
3218              */
3219             encrypt_request_end();
3220             break;
3221         case ENCRYPT_ENC_KEYID:
3222             encrypt_enc_keyid(subpointer, SB_LEN());
3223             break;
3224         case ENCRYPT_DEC_KEYID:
3225             encrypt_dec_keyid(subpointer, SB_LEN());
3226             break;
3227         default:
3228             break;
3229         }
3230         break;
3231 #endif
3232
3233     default:
3234         break;
3235     }  /* end of switch */
3236
3237 }  /* end of suboption */
3238
3239 void
3240 noit_console_telnet_doclientstat(noit_console_closure_t ncct)
3241 {
3242     clientstat(ncct, TELOPT_LINEMODE, WILL, 0);
3243 }
3244
3245 #undef ADD
3246 #define ADD(c)   *ncp++ = c
3247 #define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; }
3248
3249 void
3250 noit_console_telnet_send_status(noit_console_closure_t ncct)
3251 {
3252     unsigned char statusbuf[256];
3253     unsigned char *ncp;
3254     unsigned char i;
3255
3256     ncp = statusbuf;
3257
3258     netflush(ncct);     /* get rid of anything waiting to go out */
3259
3260     ADD(IAC);
3261     ADD(SB);
3262     ADD(TELOPT_STATUS);
3263     ADD(TELQUAL_IS);
3264
3265     /*
3266      * We check the want_state rather than the current state,
3267      * because if we received a DO/WILL for an option that we
3268      * don't support, and the other side didn't send a DONT/WONT
3269      * in response to our WONT/DONT, then the "state" will be
3270      * WILL/DO, and the "want_state" will be WONT/DONT.  We
3271      * need to go by the latter.
3272      */
3273     for (i = 0; i < (unsigned char)NTELOPTS; i++) {
3274         if (my_want_state_is_will(i)) {
3275             ADD(WILL);
3276             ADD_DATA(i);
3277         }
3278         if (his_want_state_is_will(i)) {
3279             ADD(DO);
3280             ADD_DATA(i);
3281         }
3282     }
3283
3284     if (his_want_state_is_will(TELOPT_LFLOW)) {
3285         ADD(SB);
3286         ADD(TELOPT_LFLOW);
3287         if (flowmode) {
3288             ADD(LFLOW_ON);
3289         } else {
3290             ADD(LFLOW_OFF);
3291         }
3292         ADD(SE);
3293
3294         if (restartany >= 0) {
3295             ADD(SB);
3296             ADD(TELOPT_LFLOW);
3297             if (restartany) {
3298                 ADD(LFLOW_RESTART_ANY);
3299             } else {
3300                 ADD(LFLOW_RESTART_XON);
3301             }
3302             ADD(SE);
3303         }
3304     }
3305
3306 #ifdef  LINEMODE
3307         if (his_want_state_is_will(TELOPT_LINEMODE)) {
3308                 unsigned char *cp, *cpe;
3309                 int len;
3310
3311                 ADD(SB);
3312                 ADD(TELOPT_LINEMODE);
3313                 ADD(LM_MODE);
3314                 ADD_DATA(editmode);
3315                 ADD(SE);
3316
3317                 ADD(SB);
3318                 ADD(TELOPT_LINEMODE);
3319                 ADD(LM_SLC);
3320                 start_slc(ncct, 0);
3321                 send_slc(ncct);
3322                 len = end_slc(ncct, &cp);
3323                 for (cpe = cp + len; cp < cpe; cp++)
3324                         ADD_DATA(*cp);
3325                 ADD(SE);
3326         }
3327 #endif  /* LINEMODE */
3328
3329     ADD(IAC);
3330     ADD(SE);
3331
3332     nc_write(ncct, statusbuf, ncp - statusbuf);
3333     netflush(ncct);     /* Send it on its way */
3334 }
Note: See TracBrowser for help on using the browser.