root/src/noit_console_telnet.c

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

compiles on linux -- still no eventer, refs #12

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