root/src/noit_console_telnet.c

Revision d8d5d86f2560e06f92e7d5d0726750eb0e3abea3, 79.8 kB (checked in by Thom May <thom@digital-science.com>, 3 years ago)

Include signal.h for SIGQUIT and SIGINT

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