root/src/noit_console_telnet.c

Revision 105337673852fc86c170bafc0e21f947f2b6f6d2, 79.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

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