root/src/noit_console_telnet.c

Revision fc879b8ebfb9f5de9e38c553a69f55837e32a394, 79.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

Disable LINEMODE on Linux -- I can't get it to work. This solves the echoing telnet sessions. Make compiles without LINEMODE clean. fixes #19

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