root/src/noit_console_telnet.c

Revision 46304a4547d3e7cd3b7e98eaa2b8653034cd1c6b, 79.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

add in linemode

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